Themes
How the override model works and how to create your own theme.
The Override Model
A theme is a partial set of token overrides scoped under a [data-theme] CSS selector. Themes never create new raw values — they remap at the alias or system tier by picking different steps on the raw scales the base already provides.
A theme that overrides nothing is identical to the base. This means every theme inherits all 762 tokens and only needs to override the ones it wants to change.
/* Base (no data-theme attribute needed) */
:root {
--color-primary: var(--color-neutral-900);
--button-radius: var(--radius-md);
}
/* Custom theme — scoped override */
[data-theme="ocean"] {
--color-primary: var(--color-blue-600);
--button-radius: var(--radius-sm);
}How data-theme Selectors Work
Set data-theme on any ancestor element. All descendants inherit the overridden token values through CSS cascade. Set it on <html> for global theming, or on any container for scoped theming.
// Global theme
<html data-theme="ocean">
// Scoped theme (only children inherit)
<div data-theme="ocean">
<Button>Ocean-styled button</Button>
</div>Token Override JSON Structure
Theme overrides are JSON files with the same structure as the base tokens. Only include the tokens you want to change — everything else is inherited.
Alias Overrides
Remap semantic roles to different raw values. This is the most powerful lever — a single alias change cascades to every component that references it.
// src/alias/color.json — remap primary to blue
{
"color": {
"primary": { "$value": "{color.blue.600}", "$type": "color" },
"primary-hover": { "$value": "{color.blue.700}", "$type": "color" }
}
}System Overrides
Fine-tune individual component tokens. Use this when you need component-specific tweaks beyond what alias changes provide.
// src/system/button.json — smaller radius for this theme
{
"button": {
"radius": { "$value": "{radius.sm}", "$type": "dimension" }
}
}Building a Theme with Style Dictionary
Theme packages use the same Style Dictionary v5 build pipeline as the base tokens. The key difference is scoping all output under a [data-theme] selector.
import StyleDictionary from 'style-dictionary';
const sd = new StyleDictionary({
source: ['src/**/*.json'],
platforms: {
css: {
transformGroup: 'css',
buildPath: 'dist/css/',
files: [{
destination: 'variables.css',
format: 'css/variables',
options: {
outputReferences: true,
selector: '[data-theme="ocean"]',
},
}],
},
},
});
await sd.buildAllPlatforms();Creating a New Theme
1. Create the override files
Create JSON files under src/alias/ and src/system/ that override the tokens you want to change. Reference raw tokens that already exist in the base.
2. Add fonts (optional)
Create css/fonts.css with your font imports, and override the display/text font family tokens.
3. Build with Style Dictionary
Run Style Dictionary with outputReferences: true and scope output under [data-theme="yourname"].
4. Consume it
// Import after base tokens
import '@toucan-ui/tokens/css';
import './theme-ocean.css';
// Apply globally
<html data-theme="ocean">
// Or scope to a container
<div data-theme="ocean">
<Button>Themed button</Button>
</div>Example Themes
The example pages in this docs site demonstrate theming in action. Each example page applies a different set of token overrides to show how the same components adapt to different visual styles — all without changing any component code.