Package Exports
- @cobalt-ui/plugin-sass
- @cobalt-ui/plugin-sass/dist/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@cobalt-ui/plugin-sass) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
@cobalt-ui/plugin-sass
Generate .scss
and .sass
output from your design tokens using Cobalt.
Features
- ✅ Supports all features of the CSS plugin
- ✅ Strong typechecking with Sass to never have broken styles
Setup
npm i -D @cobalt-ui/plugin-sass @cobalt-ui/plugin-css
// tokens.config.mjs
import pluginSass from '@cobalt-ui/plugin-sass';
/** @type import('@cobalt-ui/core').Config */
export default {
tokens: './tokens.json',
outDir: './tokens/',
plugins: [
pluginSass({
/** set the filename inside outDir */
filename: './index.scss',
/** output CSS vars generated by @cobalt-ui/plugin-css? */
pluginCSS: undefined,
/** use indented syntax? (.sass format) */
indentedSyntax: false,
/** embed file tokens? */
embedFiles: false,
/** handle specific token types */
transform(token, mode) {
// Replace "sans-serif" with "Brand Sans" for font tokens
if (token.$type === 'fontFamily') {
return token.$value.replace('sans-serif', 'Brand Sans');
}
},
}),
],
};
Usage
Use the provided token()
function to get a token by its ID (separated by dots):
@use '../tokens' as *; // update '../tokens' to match your location of tokens/index.scss
.heading {
color: token('color.blue');
font-size: token('typography.size.xxl');
}
body[color-mode='dark'] .heading {
color: token('color.blue', 'dark'); // pull "dark" mode variant
}
Note that a function has a few advantages over plain Sass variables:
- ✅ The name perfectly matches your schema (no guessing!)
- ✅ You can programmatically pull values (which is more difficult to do with Sass vars)
- ✅ Use the same function to access modes
CSS Variable Mode (recommended)
By default, the Sass plugin only loads your raw token values to Sass. This is a good basic usage, but leaves all the automatic mode inheritance and advanced features of the CSS plugin on the table. To get all the features of the CSS plugin, you can load it through the Sass plugin. In other words:
color: token('color.blue');
Becomes:
- color: #506fff;
+ color: var(--color-blue);
“Why would I want to do this?” you may ask. “Why not just type CSS variables directly?”
The answer is that CSS variables have no typechecking. If, say, your tokens were renamed, or you made a typo, you would never know! You would just have broken styles. However, using the Sass plugin in CSS variable mode gives you all the advantages of CSS variables but with the typechecking of Sass so that you’ll never have a single broken style.
Pros
- Get automatic mode inheritance from CSS variables (such as light/dark mode)
- Get dynamic style inheritance from your app
- Get P3 Color enhancement (provided by
@cobalt-ui/plugin-css
)
Cons
- None, really! You may have to just change how you write CSS.
Setup
In your tokens.config.mjs
file, opt in by adding a pluginCSS
option. That will automatically load @cobalt-ui/plugin-css and will pass all options to it (all options are supported):
// tokens.config.mjs
import pluginSass from '@cobalt-ui/plugin-sass';
/** @type import('@cobalt-ui/core').Config */
export default {
tokens: './tokens.json',
outDir: './tokens/',
plugins: [
pluginSass({
cssVars: true,
pluginCSS: {
prefix: 'ds',
modeSelectors: [
{mode: 'light', tokens: ['color.*'], selectors: ['[data-color-theme="light"]']},
{mode: 'dark', tokens: ['color.*'], selectors: ['[data-color-theme="dark"]']},
],
},
}),
],
};
⚠️ Don’t load another instance of @cobalt-ui/plugin-css, otherwise they may conflict!
Lastly, you’ll need to make sure the new tokens.css
file is loaded in your app somehow (otherwise the variables won’t be defined):
// src/app.ts
+ import '../tokens/tokens.css';
Usage
Here’s one example of how you may need to adjust your code with CSS vars. For example, opacity can be achieved with the color-mix() function:
- color: rgba(token('color.ui.foreground'), 0.75); // ❌ rgba(var(--color-ui-foreground), 0.75)
+ color: color-mix(in oklab, #{token('color.ui.foreground')}, 25% transparent); // ✅ var(--color-ui-foreground) at 75% opacity
Or perhaps you want to do some calculations off your tokens. CSS’ calc()
can do that the same:
- margin-left: -0.5 * token('space.sm'); // ❌ Error: Undefined operation "-0.5 * var(--space-sm)"
+ margin-left: calc(-0.5 * #{token('space.ms')}); // ✅ calc(-0.5 * var(--color-ui-foreground));
In either case, letting the browser do the work is better, especially considering CSS variables are dynamic and can be modified on-the-fly.
✨ Tip: Always use
in oklab
as the default colorspace forcolor-mix()
. It usually outperforms other blending methods (comparison).
Config
Embed Files
Say you have link
tokens in your tokens.json
:
{
"icon": {
"alert": {
"$type": "link",
"$value": "./icon/alert.svg"
}
}
}
By default, consuming those will print values as-is:
.icon-alert {
background-image: token('icon.alert');
}
// Becomes …
.icon-alert {
background-image: url('./icon/alert.svg');
}
In some scenarios this is preferable, but in others, this may result in too many requests and may result in degraded performance. You can set embedFiles: true
to generate the following instead:
.icon-alert {
background-image: token('icon.alert');
}
// Becomes …
.icon-alert {
background-image: url('image/svg+xml;utf8,<svg …></svg>');
}
Color Format
pluginSass({
colorFormat: 'oklch',
}),
By specifying a colorFormat
, you can transform all your colors to any browser-supported colorspace. Any of the following colorspaces are accepted:
If you are unfamiliar with these colorspaces, the default hex
value is best for most users (though you should use OKLCH to define your colors).
Transform
Inside plugin options, you can specify an optional transform()
function:
/** @type import('@cobalt-ui/core').Config */
export default {
tokens: './tokens.json',
outDir: './tokens/',
plugins: [
pluginSass({
transform(token, mode) {
const oldFont = 'sans-serif';
const newFont = 'Custom Sans';
if (token.$type === 'fontFamily') {
return token.$value.map((value) => (value === oldFont ? newFont : value));
}
},
}),
],
};
Your transform will only take place if you return a truthy value, otherwise the default transformer will take place.
Custom tokens
If you have your own custom token type, e.g. my-custom-type
, you’ll have to handle it within transform()
:
/** @type import('@cobalt-ui/core').Config */
export default {
tokens: './tokens.json',
outDir: './tokens/',
plugins: [
pluginSass({
transform(token, mode) {
switch (token.$type) {
case 'my-custom-type': {
return String(token.$value);
break;
}
}
},
}),
],
};
API
All available methods in the Sass plugin.
listModes()
List all modes a token has defined. This returns a Sass list. This can be used to generate styles for specific modes:
@use '../tokens' as *;
@for $mode in listModes('color.blue') {
[data-color-mode='#{$mode}'] {
color: token('color.blue', $mode);
}
}
token()
Retrieve a token by its ID. Optionally provide the mode as the 2nd param.
token($tokenID, [$mode]);
typography()
Sass mixin to inject all styles from a typography token. Optionally provide the mode as the 2nd param.
@include typography($tokenID, [$mode]);
@use '../tokens' as *;
h2 {
@include typography('typography.heading-2');
font-size: token('typography.size.xxl'); // overrides can still be applied after the mixin!
}
Note that you can override any individual property so long as it comes after the mixin.