A Design Tokens Workflow (part 11)
Creating Sass-backed CSS Custom Properties With Style Dictionary
- Getting Started With Style Dictionary
- Outputting to Different Formats with Style Dictionary
- Beyond JSON: Exploring File Formats for Design Tokens
- Converting Tokens with Style Dictionary
- Organising Outputs with Style Dictionary
- Layers, referencing tokens in Style Dictionary
- Implementing Light and Dark Mode with Style Dictionary
- Implementing Light and Dark Mode with Style Dictionary (part 2)
- Implementing Multi-Brand Theming with Style Dictionary
- Creating Multiple Themes with Style Dictionary
- Creating Sass-backed CSS Custom Properties With Style Dictionary – You are here
- Creating a Penpot Design Tokens Format with Style Dictionary
On This Page:
Whilst writing my recent article on where and when I choose to use Sass variables or CSS Custom Properties in a project I thought to myself “you could automate this bit using style dictionary”. As that article is published I’ve quickly opened up a new markdown file to write this. In this article we will look at how we can use our design tokens to not only generate Sass variables, but generate CSS custom properties that will make use of the Sass variables using interpolation.
Implementing Spacing Tokens
Let's create a sample token structure that combines scale definitions with component-specific applications. tokens/base/spacing.tokens
{"spacing": {"100": {"$value": "0.125rem","$type": "spacing"},"200": {"$value": "0.25rem","$type": "spacing"},"300": {"$value": "0.5rem","$type": "spacing"},"400": {"$value": "1rem","$type": "spacing"},"800": {"$value": "2rem","$type": "spacing"}}}
tokens/semantic/spacing.tokens
{"component": {"margin": {"inline": {"$value": "{spacing.400.$value}","$type": "spacing"},"block": {"$value": "{spacing.400.$value}","$type": "spacing"}},"padding": {"inline": {"$value": "{spacing.100.$value}","$type": "spacing"},"block": {"$value": "{spacing.100.$value}","$type": "spacing"}}}}
Style Dictionary Configuration
Next we need to create a new build script that will use Style Dictionary. What we want to do is:
- generate a
.scss
file of the base spacing tokens as Sass variables - generate a
.scss
file of the semantic spacing tokens that will create a set of CSS custom properties that use Sass variables via interpolations (—component-margin-inline: #{$spacing-400];
.
import { globSync } from 'glob'; // For file pattern matchingimport StyleDictionary from 'style-dictionary';// Find all token files matching the patternconst tokenFiles = globSync('src/tokens/**/*.tokens');// Header comment for generated filesconst HEADER_COMMENT = `// Do not edit directly, this file was auto-generated.\n\n`;// Configure Style Dictionary instanceconst myStyleDictionary = new StyleDictionary({source: tokenFiles,platforms: {sass_base: {transformGroup: 'scss', // Use standard SCSS transformsbuildPath: 'build/sass/base/',files: [{destination: '_base-tokens.scss',format: 'scss/variables', // SCSS variables formatfilter: (token) => token.filePath.includes('base'), // Only process base tokens}],},css_semantic: {transformGroup: 'scss', // Use SCSS transformsbuildPath: 'build/sass/semantic/',files: [{destination: 'variables.scss',format: 'css/sass-ref', // Custom format defined in hooksfilter: (token) => token.filePath.includes('semantic'), // Only process semantic tokens}],},},// Custom format definitionshooks: {formats: {// Custom CSS format that references Sass variables'css/sass-ref': function ({ dictionary }) {// Process all tokens in the dictionaryconst tokens = dictionary.allTokens.map((token) => {const isReference = typeof token.original.$value === 'string' &&token.original.$value.startsWith('{') &&token.original.$value.endsWith('}');const sassVariable = isReference? `$${token.original.$value.slice(1, -1) // Remove curly braces.replace(/\.\$value/g, '') // Remove .$value suffix first.replace(/\./g, '-')}` // Then convert remaining dots to hyphens: `$${token.name}`;return ` --${token.name}: #{${sassVariable}};`;}).join('\n');// Combine header, Sass import, and CSS variablesreturn `${HEADER_COMMENT}@use "../base/_base-tokens.scss";\n\n:root {\n${tokens}\n}`;}}}});// Execute the build process for all platformsmyStyleDictionary.buildAllPlatforms();console.log('Build completed!');
Key Script Components
- Token Aggregation: Using
globSync
to recursively collect all .token
sfiles from
src/tokens/**/*.tokens`. - Smart Reference Handling: The custom
css/sass-ref
format automatically:- Detects any token reference format (
{type.path.to.token}
) - Converts dot-notation paths to Sass-friendly hyphens
- Removes
. $value
suffixes while maintaining reference integrity
- Detects any token reference format (
- Separation of Concerns: Platform configuration keeps base tokens (design primitives) separate from semantic tokens (contextual applications)
The code for this example can be found on this branch of the repo this series has been using
Generated Output Structure
The script produces two critical files. These files are:
- a Sass variables file that maintains design system constants.
- a CSS custom properties file that references Sass values via interpolation.
Base Tokens (Sass)
_base-tokens.scss
$spacing-100: 0.125rem;$spacing-200: 0.25rem;$spacing-300: 0.5rem;$spacing-400: 1rem;$spacing-800: 2rem;
Semantic Tokens (CSS)
_variables.scss
@use "../base/_base-tokens.scss";:root {--component-margin-inline: #{$spacing-400};--component-margin-block: #{$spacing-400};--component-padding-inline: #{$spacing-100};--component-padding-block: #{$spacing-100};}
This Style Dictionary configuration helps solve several challenges in modern design systems:
- Single Source of Truth: Token updates propagate through both Sass and CSS layers.
- Contextual Adaptation: Component-specific overrides maintain design consistency
- Toolchain Integration: Automates the Sass to CSS bridge while preserving both paradigms.
- Scale Maintenance: Spacing ratios remain consistent across primitive and semantic layers.
By combining Style Dictionary's transformation power with Sass's preprocessing capabilities, we can create a future-proof system that works seamlessly with CSS-in-JS solutions, traditional Sass projects, and modern CSS-only architectures and more.