# 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`. ```json { "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 ```json // 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 ```