JSPM

  • Created
  • Published
  • Downloads 130
  • Score
    100M100P100Q89518F
  • License MIT

Generate scss/sass from your design tokens schema (requires @cobalt-ui/cli)

Package Exports

    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 Sass output for Cobalt from design tokens.

    Setup

    npm i -D @cobalt-ui/plugin-sass
    // 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

    Tokens

    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');
    }

    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

    By default, the token() function returns the raw token value to Sass. But to take advantage of all the features of plugin-css such as built-in color theming and out-of-the-box P3 support, you’ll want to use both together.

    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: {
              'color#light': ['[data-color-theme="light"]'],
              'color#dark': ['[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';

    Now token('color.blue') will output var(--ds-color-blue) instead of a raw hex value, and your app can now dynamically change themes and take advantage of all the power CSS variables have to offer.

    If you‘re using Sass, this method is recommended because token() will safely guard against accidental typos for your design tokens.

    Typography

    As $type: "typography" tokens contain multiple values, you’ll need to use the typography() mixin instead:

    @use '../tokens' as *;
    
    .heading {
      @include typography('typography');
    
      font-size: token('typography.size.xxl');
    }

    Note that you can override any individual property so long as it comes after the mixin.

    Modes

    If you take advantage of modes in your tokens, you can pass a 2nd param into tokens() with a mode name:

    @use '../tokens' as *;
    
    .heading {
      color: token('color.blue');
    
      body[color-mode='dark'] & {
        color: token('color.blue', 'dark');
      }
    }

    ⚠️ Note that modes are designed to gracefully fall back. So if a certain value isn’t defined on a mode, it will fall back to the default, rather than throwing an error.

    List modes

    To see which modes a token has defined, use the getModes() function which returns a 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);
      }
    }

    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>');
    }

    Read more

    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;
              }
            }
          },
        }),
      ],
    };