Theming
Build light and dark themes with Sass and CSS custom properties.
Syntax
:root { --color: #{$value}; } color: var(--color);The strongest theming approach combines both worlds: use Sass to organize and generate tokens at build time, and emit CSS custom properties so themes can switch live in the browser.
The pattern
- Define theme maps in Sass (light, dark).
- Loop with
@eachto emit custom properties under a theme selector. - Reference the custom properties with
var()in your components.
This gives you Sass's authoring power and CSS's runtime flexibility together.
Example
$themes: (
'light': (bg: #ffffff, text: #333333),
'dark': (bg: #1a1a1a, text: #eeeeee),
);
@use 'sass:map';
@each $name, $tokens in $themes {
.theme-#{$name} {
--bg: #{map.get($tokens, bg)};
--text: #{map.get($tokens, text)};
}
}
body {
background: var(--bg);
color: var(--text);
}When to use it
- A SaaS product uses Sass maps to define light and dark themes, then emits CSS custom properties from each map under a data-theme attribute selector.
- A developer builds a white-label system where each client's theme is a small Sass map that overrides brand colors, all compiled into separate CSS files.
- A team co-locates their Sass theme maps with a @forward so any module can read theme values, while the :root generation happens only once in main.scss.
More examples
Light and dark theme maps
Two maps define light and dark tokens; a mixin emits them as CSS custom properties scoped to :root and [data-theme=dark] respectively.
$light: (bg: #ffffff, text: #111827, border: #e5e7eb);
$dark: (bg: #0f172a, text: #f1f5f9, border: #1e293b);
@mixin apply-theme($map) {
@each $k, $v in $map {
--#{$k}: #{$v};
}
}
:root { @include apply-theme($light); }
[data-theme=dark] { @include apply-theme($dark); }Component using theme tokens
A component references CSS custom properties set by the theme system, automatically switching between light and dark with zero extra CSS.
.card {
background: var(--bg);
color: var(--text);
border: 1px solid var(--border);
padding: 1rem;
transition: background 0.2s, color 0.2s;
}Multi-brand theme compilation
Each brand file configures the shared theme-system module with its own tokens, producing an independent CSS file per brand.
// brand-a.scss
@use 'theme-system' with (
$primary: #dc2626,
$font: 'Georgia, serif'
);
// brand-b.scss
@use 'theme-system' with (
$primary: #7c3aed,
$font: 'Inter, sans-serif'
);
// Each compiles to a separate CSS file for white-labelling.
Discussion