Files
Parsons/docs/conventions/token-conventions.md
Richie 732c872576 Initial commit: FA 2.0 Design System foundation
Token pipeline (Style Dictionary v4, DTCG format):
- Primitive tokens: colour palettes (brand, sage, neutral, feedback),
  typography (3 font families, 21-variant type scale), spacing (4px grid),
  border radius, shadows, opacity
- Semantic tokens: text, surface, border, interactive, feedback colours;
  typography roles; layout spacing
- Component tokens: Button (4 sizes), Input (2 sizes)
- Generated outputs: CSS custom properties, JS ES6 module, flat JSON

Atoms (3 components):
- Button: contained/soft/outlined/text × primary/secondary, 4 sizes,
  loading state, underline for text variant
- Typography: 21 variants across display/heading/body/label/caption/overline,
  maxLines truncation
- Input: external label, helper text, error/success validation,
  start/end icons, required indicator, 2 sizes, multiline support

Infrastructure:
- MUI v5 theme with full token mapping
- Storybook 8 with autodocs
- Claude Code agents and skills for token/component workflows
- Design system documentation and cross-session memory

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 15:08:15 +11:00

5.6 KiB

Token conventions

These conventions MUST be followed by any agent creating or modifying tokens.

W3C DTCG format

All tokens use the DTCG JSON format. Every token has $value, $type, and $description.

{
  "color": {
    "brand": {
      "primary": {
        "$value": "#1B4965",
        "$type": "color",
        "$description": "Primary brand colour — deep navy. Used for primary actions, key headings, and trust-building UI elements."
      }
    }
  }
}

Naming rules

General

  • Use dot notation for hierarchy in documentation: color.brand.primary
  • In JSON, dots become nested objects
  • Names are lowercase, no spaces, no special characters except hyphens for compound words
  • Names must be descriptive of purpose (semantic) or scale position (primitive)

Primitive naming

Primitives describe WHAT the value is, not WHERE it's used.

color.{hue}.{scale}        → color.blue.500, color.neutral.100
spacing.{scale}             → spacing.1, spacing.2, spacing.4, spacing.8
fontSize.{scale}            → fontSize.xs, fontSize.sm, fontSize.base, fontSize.lg
fontWeight.{name}           → fontWeight.regular, fontWeight.medium, fontWeight.bold
fontFamily.{purpose}        → fontFamily.heading, fontFamily.body, fontFamily.mono
borderRadius.{scale}        → borderRadius.sm, borderRadius.md, borderRadius.lg, borderRadius.full
shadow.{scale}              → shadow.sm, shadow.md, shadow.lg
lineHeight.{scale}          → lineHeight.tight, lineHeight.normal, lineHeight.relaxed
letterSpacing.{scale}       → letterSpacing.tight, letterSpacing.normal, letterSpacing.wide
opacity.{scale}             → opacity.disabled, opacity.hover, opacity.overlay

Colour scale convention

Use a 50-950 scale (matching Tailwind/MUI convention):

  • 50: Lightest tint (backgrounds, subtle fills)
  • 100-200: Light tints (hover states, borders)
  • 300-400: Mid tones (secondary text, icons)
  • 500: Base/reference value
  • 600-700: Strong tones (primary text on light bg, active states)
  • 800-900: Darkest shades (headings, high-contrast text)
  • 950: Near-black (used sparingly)

Semantic naming

Semantic tokens describe WHERE and WHY a value is used.

color.text.{variant}        → color.text.primary, color.text.secondary, color.text.disabled, color.text.inverse
color.surface.{variant}     → color.surface.default, color.surface.raised, color.surface.overlay
color.border.{variant}      → color.border.default, color.border.strong, color.border.subtle
color.interactive.{state}   → color.interactive.default, color.interactive.hover, color.interactive.active, color.interactive.disabled
color.feedback.{type}       → color.feedback.success, color.feedback.warning, color.feedback.error, color.feedback.info
spacing.component.{size}    → spacing.component.xs, spacing.component.sm, spacing.component.md, spacing.component.lg
spacing.layout.{size}       → spacing.layout.section, spacing.layout.page, spacing.layout.gutter
typography.{role}            → typography.display, typography.heading, typography.body, typography.caption, typography.label

Component token naming

Component tokens are scoped to a specific component.

{component}.{property}.{state}
button.background.default
button.background.hover
button.background.active
button.background.disabled
button.text.default
button.text.disabled
button.border.default
button.border.focus
button.padding.horizontal
button.padding.vertical
button.borderRadius
card.background.default
card.border.default
card.padding
card.borderRadius
card.shadow
input.background.default
input.background.focus
input.border.default
input.border.error
input.border.focus
input.text.default
input.text.placeholder

Alias rules

  • Semantic tokens MUST reference primitives (never hardcode values)
  • Component tokens MUST reference semantic tokens (never reference primitives directly)
  • This creates the chain: component → semantic → primitive
  • Exception: spacing and borderRadius component tokens may reference primitives directly when the semantic layer adds no value
// CORRECT: component → semantic → primitive
"button.background.default": { "$value": "{color.interactive.default}" }
"color.interactive.default": { "$value": "{color.brand.primary}" }
"color.brand.primary": { "$value": "{color.blue.700}" }
"color.blue.700": { "$value": "#1B4965" }

// WRONG: component referencing a primitive directly
"button.background.default": { "$value": "{color.blue.700}" }

Accessibility requirements

  • All colour combinations used for text must meet WCAG 2.1 AA contrast ratio (4.5:1 for normal text, 3:1 for large text)
  • Interactive elements must have a visible focus indicator
  • Disabled states must still be distinguishable from enabled states
  • When creating colour tokens, note the contrast ratio with common background colours in the $description

File organisation

tokens/
├── primitives/
│   ├── colours.json       # All colour primitives (brand, neutral, feedback hues)
│   ├── typography.json    # Font families, sizes, weights, line heights
│   ├── spacing.json       # Spacing scale, border radius, sizing
│   └── effects.json       # Shadows, opacity values
├── semantic/
│   ├── colours.json       # Semantic colour mappings
│   ├── typography.json    # Typography role mappings
│   └── spacing.json       # Layout and component spacing
└── component/
    ├── button.json        # Button-specific tokens
    ├── input.json         # Input-specific tokens
    ├── card.json          # Card-specific tokens
    └── ...                # One file per component that needs specific tokens