Managing Design Tokens with Sass Mixins and Functions
Learn how to replace static CSS variables with a scalable design token system using Sass maps, functions, and mixins to reduce redundancy and simplify theme management.
06 Mar 2026, 08:45 UTC

The Problem: Hard-coded Design Values
When design tokens—such as spacing scales, color palettes, and typography ratios—are applied as static variables throughout a stylesheet, updating a single design decision often requires a global search-and-replace or the creation of dozens of redundant variables. This approach leads to "CSS bloat," where the same property-value pairs are repeated across hundreds of selectors, making the codebase difficult to maintain and audit.
The solution is to shift from static variables to a token-lookup system using Sass maps, functions for calculations, and mixins for style injection. This centralizes the logic and ensures that the compiled CSS remains lean while the source code remains flexible.
Prerequisites
- Dart Sass: Ensure you are using Dart Sass (the current industry standard), as LibSass is deprecated and does not support modern module systems.
- Basic SCSS Knowledge: Familiarity with variables and nesting.
- Compiler Access: A terminal environment where the
sasscommand is installed and available.
Implementing a Token-Based Architecture
To build a scalable system, you must separate the data (the tokens) from the logic (how those tokens are applied).
1. Define the Token Maps
Instead of creating $color-primary-light, $color-primary-main, and $color-primary-dark, use a Sass Map. A map is a collection of key-value pairs that allows for dynamic lookups.
// _tokens.scss
$theme-colors: (
"primary": (
"light": #e3f2fd,
"main": #2196f3,
"dark": #1565c0,
),
"secondary": (
"light": #f5f5f5,
"main": #9e9e9e,
"dark": #616161,
)
);
$spacing-scale: (
"xs": 0.25rem,
"sm": 0.5rem,
"md": 1rem,
"lg": 2rem,
);2. Create a Lookup Function
A Sass function is used to calculate or return a specific value. In this case, we create a function to extract a value from our nested maps. This prevents the need to manually call map-get throughout your components.
// _functions.scss
@function get-color($palette, $shade: "main") {
@return map-get(map-get($theme-colors, $palette), $shade);
}
@function get-space($size) {
@return map-get($spacing-scale, $size);
}3. Build a Style-Injecting Mixin
While functions return values, mixins inject blocks of CSS. Use a mixin to handle repetitive property sets, such as applying a theme color to both background and border properties simultaneously.
// _mixins.scss
@mixin theme-variant($palette, $shade: "main") {
background-color: get-color($palette, $shade);
border: 1px solid get-color($palette, "dark");
color: get-color($palette, "dark");
}4. Applying the System to Components
Run the following implementation in your component file. Use the @include directive to call the mixin.
// components/_button.scss
.btn-primary {
padding: get-space("sm") get-space("md");
@include theme-variant("primary");
border-radius: 4px;
}
.btn-secondary {
padding: get-space("sm") get-space("md");
@include theme-variant("secondary", "light");
border-radius: 4px;
}Comparison: Static Variables vs. Token Maps
| Feature | Static Variables | Token Maps + Mixins |
|---|---|---|
| Maintenance | High (Manual updates per variable) | Low (Update map value once) |
| Naming | Verbose (e.g., $color-blue-dark-500) | Semantic (e.g., "primary", "dark") |
| CSS Output | Identical | Identical (if used correctly) |
| Flexibility | Low | High (Supports programmatic loops) |
Verification and Diagnostics
To verify the implementation, compile your SCSS to CSS using the command line:
# Run from the project root
sass styles/main.scss styles/dist/main.cssExpected Checks:
- Open
main.cssand search for.btn-primary. It should contain the hex code#2196f3for the background and#1565c0for the border. - Verify that
paddingis converted fromget-space("sm")to0.5rem 1rem. - Ensure no Sass-specific syntax (like
@includeormap-get) remains in the compiled CSS.
Limitations and Risks
- Compilation Overhead: Extremely large maps (thousands of entries) can slow down the Dart Sass compiler.
- Deep Nesting: Avoid nesting mixins within other mixins more than two levels deep; this makes the source code difficult to debug and can lead to unexpectedly large CSS files if not managed.
- Runtime Performance: Sass is a pre-processor. These calculations happen at build time, not in the browser, meaning there is zero performance hit for the end-user.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.