JM Family Design System

Tokens Overview

Design tokens are the source of truth for every visual decision in the system — color, typography, spacing, shadow, motion. They live as JSON, build into CSS custom properties and TypeScript constants, and validate themselves on every build.

299Total tokens

Across global and semantic tiers, both themes.

133Global primitives

Raw values: palette, font weights, spacing scale.

166Semantic aliases

Purpose-named tokens that components consume.

65Dark theme overrides

Same semantic names, different values per theme.

Two-tier architecture

Tokens come in two tiers. Global primitives define raw values with no meaning — the full palette, every spacing step, every font weight. Semantic aliases point at globals and carry intent — what to use for primary text, default surface, error background. Components only ever consume semantic aliases.

Tier 1

Global primitives

Raw, neutral values. Define what colors and sizes exist, not where they should be used. Never referenced directly by components.

color.teal.800
= #115662
Tier 2

Semantic aliases

Purpose-named. Define where a value belongs. The same alias resolves to different globals across themes — that’s how light and dark work.

color.text.primary
→ color.teal.800 (light)→ color.gray.50 (dark)
The rule
Components reference semantic tokens only. If a value you need doesn’t have a semantic alias, propose a new one — don’t reach into the global pool from a component.
Do

color: var(--color-text-primary);

Don’t

color: var(--color-teal-800); — this is a global; it won’t flip in dark mode.

Naming convention

Tokens are written as dot-paths in JSON and exposed as kebab-case CSS custom properties. The transform is mechanical: dots become hyphens, the whole string is lowercased.

JSON pathCSS custom propertyResolved value
color.text.primary--color-text-primary#115662
spacing.inset.md--spacing-inset-md16px
type.body.md--type-body-md-size, --type-body-md-lineheight, …composite
border.radius.component--border-radius-component8px
Composite tokens
Typography tokens hold multiple sub-properties (size, weight, line-height, letter-spacing). The build expands each into its own CSS variable suffixed by the property name — e.g. --type-h1-size, --type-h1-lineheight.

Build pipeline

A single Node script reads the JSON, resolves references, validates contrast, and emits CSS plus TypeScript. It runs as part of npm run build; you can run it directly with npm run build:tokens.

Source

JSON files

tokens/global/*.json + tokens/semantic/*.json

Build

build-tokens.mjs

Flatten paths, resolve {references}, validate contrast, fail on error.

Output

CSS + TypeScript

tokens-base.css, tokens-light.css, tokens-dark.css, src/lib/tokens.ts

Validation that fails the build
Any unresolved {reference} stops the build with a specific error. WCAG 2.2 AA contrast is checked for ten required pairings — if a token change drops a pairing below 4.5:1, the build exits 1 with the failing combination called out.

Theme system

Themes are switched via a single data-theme attribute on <html>. The base CSS defines theme-neutral tokens (typography, spacing, motion, layout). Theme files override only what changes — semantic colors and shadows.

/* tokens-base.css */
:root {
  --spacing-4: 16px;
  --type-body-md-size: 14px;
  /* ...everything theme-neutral */
}

/* tokens-light.css */
[data-theme="light"] {
  --color-text-primary: #115662;
  --color-surface-default: #FFFFFF;
  /* ...semantic colors + shadows */
}

/* tokens-dark.css */
[data-theme="dark"] {
  --color-text-primary: #F9FAFB;
  --color-surface-default: #0F172A;
  /* ...same names, different values */
}
Why data-theme instead of a class?
Attribute selectors play well with the inline theme-bootstrap script in <head> that prevents flash-of-wrong-theme. The attribute is also easier to query in JavaScript than a class.

Categories

Tokens are organized by what they describe. The first segment of the path identifies the category — color.*, spacing.*, type.*, and so on.

  • color.*124 tokens

    Brand colors, semantic text and surface colors, action and feedback states.

  • type.*13 tokens

    Display, heading, body, label, caption, code — composite tokens with size, weight, and line-height.

  • font.*26 tokens

    Font families, weights, sizes, and line heights — referenced by composite type tokens.

  • spacing.*24 tokens

    4px-based scale plus semantic aliases for inset, inline, stack, content, and section gaps.

  • border.*14 tokens

    Radius and width values for components, containers, and focus rings.

  • motion.*9 tokens

    Duration and easing tokens for transitions and animations.

  • breakpoint.*4 tokens

    Four breakpoint widths for documentation and JavaScript matchMedia.

  • layout.*5 tokens

    Container widths, grid gutters, and surface dimensions.

  • z-index.*8 tokens

    Layering scale for stacking context: dropdown, sticky, modal, toast, etc.

Where to go next