Design tokens are the visual DNA of your design system. Spektral uses a three-layer architecture based on theDTCG(Design Tokens Community Group) format.
Three Layers
Primitive raw values: "#1e40af", "16px", "'Inter', sans-serif" ↑ referenced by Semantic purpose aliases: "$value": "{color.brand-blue.70}" ↑ referenced by Component component defaults: "$value": "{color.primary}"
Primitive
Raw design values with no implied meaning. The foundation of the entire token system.
Semantic
Purpose-based aliases that reference primitives. Changing a semantic token cascades to all components.
Component
Component-specific defaults that reference semantic tokens. Overridable per-component.
DTCG JSON Format
Every token is a JSON object with $value and $type properties:
// primitive.tokens.json { "color": { "brand-blue": { "70": { "$value": "#1e40af", "$type": "color" } } }, "spacing": { "4": { "$value": "16px", "$type": "dimension" } } } // semantic.tokens.json { "color": { "primary": { "$value": "{color.brand-blue.70}", "$type": "color" } } }
Reference Chain
Semantic and component tokens use {curly.brace} syntax to reference tokens in the layer below. References must resolve: a broken reference will fail the Style Dictionary build.
button.bg → {color.primary} → {color.brand-blue.70} → #1e40afChanging color.brand-blue.70 cascades through semantic and component layers automatically.
Style Dictionary Build
Spektral uses Style Dictionary to transform DTCG JSON tokens into CSS custom properties.
tokens/default/*.tokens.json
↓ Style Dictionary
platforms/react/ds-lib/src/tokens/variables.css
platforms/vue/ds-lib/src/tokens/variables.css
platforms/lit/ds-lib/src/tokens/variables.css
... every initialized platformThe same tokens are built into every platform. Components consume them as var(--color-primary) in CSS. Output references are preserved (outputReferences: true) so DevTools shows the token name, not resolved values.
Multi-Brand Support
By default, tokens live in brand directories under tokens/. Each brand has the same three-layer structure with different values. Switching brands rebuilds all platforms with the new token values.
tokens/ default/ primitive.tokens.json, semantic.tokens.json, component.tokens.json spektral/ same structure, different values kevla/ same structure, different values brands.json { "active": "default" }
Light and dark are not brands. They are modes, a second axis that varies inside every brand and is switched at runtime rather than at build time. See Add dark mode.
Already have tokens? Keep them where they are
Everything above is Spektral's default layout, not a requirement. If your design system already exists, you do not restructure it to adopt Spektral: you point Spektral at the tree you have.
An optional spektral.yml at your project root covers the differences: tokens in a package subdirectory, a flat tree with no brand folders, layers named something other than primitive/semantic/component, a DTCG resolver driving the whole thing, a CSS custom property prefix, and your own build command in place of Spektral's Style Dictionary.
# spektral.yml tokens: dir: packages/ui/src/tokens cssPrefix: ds builtCss: - packages/ui/src/styles/tokens.css
You usually will not write it by hand: opening a project whose tokens are not in the default place makes Spektral scan for the real tree, propose a config, and show you the layers, brands and sample tokens it reads back before writing anything. See the spektral.yml reference for every field.
Token Types
| $type | Example $value | CSS Output |
|---|---|---|
| color | "#1e40af" | --color-brand-blue-70: #1e40af; |
| dimension | "16px" | --spacing-4: 16px; |
| fontFamily | "'Inter', sans-serif" | --font-family-body: 'Inter', sans-serif; |
| fontWeight | 600 | --font-weight-semibold: 600; |
| shadow | "0 4px 6px rgba(0,0,0,0.1)" | --shadow-md: 0 4px 6px rgba(0,0,0,0.1); |
| duration | "200ms" | --duration-fast: 200ms; |