Semantic Tokens
Semantic tokens add meaning to your primitives. Instead of saying “use shade 500 of the primary palette,” a semantic token says “use the primary text color.” This layer of indirection is what enables theming — the same semantic token name resolves to different primitive values in light and dark mode.
Select Semantic in the sidebar to see all 5 categories.
How semantics work
Section titled “How semantics work”Every semantic token is an alias that references a primitive. You don’t enter raw hex codes or pixel values — you select which primitive token it should point to.
semantic-color-text-primary-default Light mode → primitive-color-brand-primary-700 (dark shade for contrast on light bg) Dark mode → primitive-color-brand-primary-300 (light shade for contrast on dark bg)This reference structure means:
- Changing a primitive automatically updates all semantics that reference it
- Light and dark themes use the same semantic names but different primitive references
- Your UI code always uses semantic names, never raw values
Semantic Colors
Section titled “Semantic Colors”The largest semantic category. Semantic colors map your primitive palettes to purpose-driven roles with full light/dark theme support.
Color intents
Section titled “Color intents”There are 8 color intents, each representing a different purpose:
| Intent | Purpose | Typical source palette |
|---|---|---|
| Primary | Main brand actions and emphasis | Brand Primary |
| Secondary | Supporting actions and content | Brand Secondary |
| Accent | Highlights and decorative elements | Brand Accent |
| Neutral | Default text, borders, surfaces | Neutral Gray |
| Success | Positive feedback, confirmations | Feedback Success |
| Warning | Caution states, alerts | Feedback Warning |
| Error | Error states, destructive actions | Feedback Error |
| Info | Informational content, hints | Feedback Info |
Color roles
Section titled “Color roles”Each intent is available across four roles:
| Role | What it colors | Examples |
|---|---|---|
| Text | Foreground text | Headings, labels, body copy, links |
| Surface | Backgrounds | Cards, panels, page backgrounds, hover states |
| Border | Outlines and dividers | Card borders, input outlines, separators |
| Interactive | Actionable elements | Button backgrounds, link colors, focus rings |
Variants per role
Section titled “Variants per role”Each role has multiple variants for different emphasis levels:
- default — Standard usage
- subtle — Lower emphasis (lighter backgrounds, muted text)
- bold — Higher emphasis (darker backgrounds, prominent text)
- contrast — (Semantic text only) Used for text on dark surfaces (e.g., white text on a bold background)
This creates a matrix of approximately 80+ semantic color tokens (8 intents x 4 roles x 2-4 variants each).
Editing light and dark values
Section titled “Editing light and dark values”The semantic color editor shows side-by-side editing for light and dark modes. Each token has:
- A sun icon column for the light mode value
- A moon icon column for the dark mode value
For each mode, you select which primitive shade the semantic token should reference using a dropdown. The dropdown lists all available shades from the relevant primitive palette.
For example, semantic-color-text-primary-default:
- Light mode dropdown: select from brand-primary shades (50-950) — typically a dark shade like 700 for good contrast on light backgrounds
- Dark mode dropdown: select from brand-primary shades (50-950) — typically a light shade like 300 for good contrast on dark backgrounds
Export format
Section titled “Export format”/* Light mode */[data-theme="light"] { --semantic-color-text-primary-default: var(--primitive-color-brand-primary-700); --semantic-color-surface-neutral-subtle: var(--primitive-color-neutral-gray-50); --semantic-color-border-neutral-default: var(--primitive-color-neutral-gray-300);}
/* Dark mode */[data-theme="dark"] { --semantic-color-text-primary-default: var(--primitive-color-brand-primary-300); --semantic-color-surface-neutral-subtle: var(--primitive-color-neutral-gray-900); --semantic-color-border-neutral-default: var(--primitive-color-neutral-gray-600);}Semantic Typography (Type Styles)
Section titled “Semantic Typography (Type Styles)”Semantic typography defines composite type styles — combinations of font family, size, weight, line height, letter spacing, and text transform that form a complete text treatment.
Available type styles
Section titled “Available type styles”Typography semantic tokens are organized into style categories, each accessible via tabs:
| Category | Typical use |
|---|---|
| Display | Hero sections, landing pages, feature highlights |
| Headings | Heading levels (H1–H6) |
| Body | Standard body and paragraph text |
| Labels | Form labels, UI labels |
| Captions | Small supporting text |
| Code | Monospace, code snippets |
| Overline | Uppercase small text above headings or sections |
Properties per style
Section titled “Properties per style”Each style token includes a live preview and configurable properties:
| Property | Selects from |
|---|---|
| Font family | Primitive typography families (display, heading, body, mono) |
| Font size | Primitive typography size scale |
| Font weight | Primitive typography weights |
| Line height | Primitive typography line heights |
| Letter spacing | Primitive typography letter spacing |
| Text transform | none, uppercase, lowercase, capitalize |
Properties marked (Inherited) pull their values from your primitive typography settings. You can override any inherited value at the semantic level. By default, most properties are set to inherit — you only need to override the specific properties you want to change.
Export format
Section titled “Export format”Type styles export as composite token objects in JSON:
{ "semantic": { "typography": { "heading": { "lg": { "fontFamily": "{primitive.typography.family.heading}", "fontSize": "{primitive.typography.size.2xl}", "fontWeight": "{primitive.typography.weight.bold}", "lineHeight": "{primitive.typography.lineheight.tight}", "letterSpacing": "{primitive.typography.letterspacing.tight}" } } } }}Semantic Spacing
Section titled “Semantic Spacing”Semantic spacing creates purpose-driven aliases for your primitive spacing scale. While primitives define what sizes exist (xs, sm, md, lg…), semantic spacing defines how those sizes are used.
Three spacing categories
Section titled “Three spacing categories”| Category | Purpose | CSS analogy |
|---|---|---|
| Stack | Vertical spacing between elements | margin-bottom, gap (column) |
| Inset | Internal padding of containers | padding |
| Inline | Horizontal spacing between elements | margin-right, gap (row) |
Each category has sizes from xs through xl (or similar), and each size maps to a primitive spacing token via a dropdown selector.
Why not use primitives directly?
Section titled “Why not use primitives directly?”You could use --primitive-spacing-md everywhere. But semantic spacing lets you:
- Differentiate usage — Stack-md and Inset-md can map to different primitive values (e.g., vertical rhythm might use larger spacing than padding)
- Change proportions globally — Want tighter padding everywhere? Change the Inset mappings without touching Stack or Inline
- Communicate intent —
semantic-spacing-stack-mdclearly says “medium vertical spacing between blocks”
Editing
Section titled “Editing”Each spacing token shows:
- The current mapping (e.g., ”→ primitive-spacing-lg”)
- The resolved pixel value
- A dropdown to change which primitive it references
Export format
Section titled “Export format”--semantic-spacing-stack-sm: var(--primitive-spacing-sm);--semantic-spacing-stack-md: var(--primitive-spacing-md);--semantic-spacing-inset-md: var(--primitive-spacing-lg);--semantic-spacing-inline-sm: var(--primitive-spacing-sm);Focus tokens control the appearance of keyboard focus indicators — essential for accessibility. When users navigate with a keyboard (Tab key), focused elements need a clearly visible outline.
Define focus ring styles for keyboard navigation and accessibility. These tokens control the outline and ring appearance when users tab through interactive elements. A live preview shows your current configuration.
The focus ring is composed of two layers — an outline (inner) and a ring (outer) — giving you fine-grained control over the visual treatment.
Focus properties
Section titled “Focus properties”| Property | Controls | Default |
|---|---|---|
| Outline width | Thickness of the inner outline | border-width.medium (2px) |
| Outline offset | Gap between the element and the outline | spacing.xs (2px) |
| Outline color | Color of the inner outline | Primary Default |
| Ring width | Thickness of the outer ring | border-width.heavy (3px) |
| Ring color | Color of the outer ring | Primary Default |
| Ring opacity | Transparency of the outer ring | opacity.alpha-25 (25%) |
Each property references an existing primitive or semantic token, keeping your focus styles connected to the rest of your system.
Editing
Section titled “Editing”Color properties use a grouped dropdown with options organized by:
- Intent variants — Colors from each semantic intent (primary, secondary, neutral, etc.)
- Other — Additional color options
This lets you pick a focus color that’s consistent with your brand but distinct enough to be visible.
Accessibility note
Section titled “Accessibility note”Focus rings are critical for accessibility compliance (WCAG 2.4.7). The default two-layer approach — a solid outline with a semi-transparent outer ring — provides strong visibility across different background colors. If you customize these values, test with keyboard navigation to ensure focus remains clearly visible throughout your design system.
Export format
Section titled “Export format”--semantic-focus-ringwidth: 2px;--semantic-focus-ringcolor: var(--semantic-color-border-primary-default);--semantic-focus-outlineoffset: 2px;Transitions
Section titled “Transitions”Semantic transitions define named transition presets that combine a duration and easing curve into a reusable animation style.
Transition presets
Section titled “Transition presets”| Preset | Typical use |
|---|---|
| Slow | Page transitions, layout shifts |
| Base | Standard UI transitions (dropdowns, panels) |
| Fast | Micro-interactions (hover, focus, button press) |
| Color | Color-only transitions (background, text color changes) |
Properties per transition
Section titled “Properties per transition”Each transition has:
| Property | Selects from |
|---|---|
| Duration | Primitive duration tokens (instant, fast, base, moderate, slow, slower) |
| Easing | Primitive easing tokens (standard, decelerate, accelerate, bounce, etc.) |
| Description | Free-text description of when to use this transition |
Animated preview
Section titled “Animated preview”Each transition card includes a play button that triggers an animation preview. A colored block animates across the card using the configured duration and easing, so you can visually evaluate how the transition feels before using it.
Export format
Section titled “Export format”Transitions export as composite tokens in JSON, referencing their primitive components:
{ "semantic": { "transition": { "fast": { "duration": "{primitive.duration.fast}", "easing": "{primitive.easing.standard}" } } }}Frequently asked questions
Section titled “Frequently asked questions”Why can’t I enter a hex value directly in semantic color?
Section titled “Why can’t I enter a hex value directly in semantic color?”By design. Semantic tokens are aliases that reference primitives — they don’t hold raw values. This ensures the layer architecture works correctly: change a primitive once, and it cascades through all semantics that reference it.
How does dark mode work at the semantic level?
Section titled “How does dark mode work at the semantic level?”Each semantic color token has two mappings — one for light mode and one for dark mode. Both map to primitive shades, but typically use opposite ends of the shade scale. Light mode text uses dark shades (700-900) for contrast on light backgrounds; dark mode text uses light shades (100-400) for contrast on dark backgrounds. The [data-theme] attribute on your HTML element controls which mapping is active.
What if a type style property is left empty?
Section titled “What if a type style property is left empty?”Empty (inherited) properties are set by the primitive typography defaults. This is useful when you want a type style to follow the base configuration for most properties but override just one or two (e.g., set a different font weight for headings but inherit everything else).
Can I create my own semantic categories?
Section titled “Can I create my own semantic categories?”Not in the current version. The five semantic categories (Color, Typography, Spacing, Focus, Transitions) are fixed. You can customize all values within these categories, but you can’t add new categories like “Semantic Shadow” or “Semantic Radius.”
Should I map every semantic size to a different primitive?
Section titled “Should I map every semantic size to a different primitive?”Not necessarily. It’s fine for stack-md and inset-md to reference the same primitive (spacing-md). The semantic layer exists for when you want them to differ, not as a requirement that they must.