@primer/primitives
Version:
Typography, spacing, and color primitives for Primer design system
210 lines (159 loc) • 8.12 kB
Markdown
# Design Tokens Master Guide
> Metadata: This file defines the Logic and Rules for the design system. For individual token definitions and raw values, refer to DESIGN_TOKENS_SPEC.md.
## Core Rule
**You are a CSS expert. Never use raw values (hex, px, etc.). Only use semantic tokens.**
## Logic Matrix: Color Pairings
| Background Token | Foreground Token | Notes |
| ---------------------- | ---------------------- | ------------------------- |
| `--bgColor-*-emphasis` | `--fgColor-onEmphasis` | MUST pair |
| `--bgColor-*-muted` | `--fgColor-{semantic}` | MUST use semantic fg |
| `--bgColor-default` | `--fgColor-default` | Standard pairing |
| `--bgColor-muted` | `--fgColor-default` | NEVER use `fgColor-muted` |
**Contrast Requirements:**
| Context | Ratio | Standard |
| --------------- | ----- | -------- |
| Normal text | 4.5:1 | WCAG AA |
| Large text / UI | 3:1 | WCAG AA |
## Pattern Compression
### Control Tokens
```
--control-[size]-[property]
├── size: xsmall | small | medium | large | xlarge
└── property: size | paddingInline-[density] | paddingBlock
└── density: condensed | normal | spacious
```
### Stack Tokens
```
--stack-[property]-[size]
├── property: gap | padding
└── size: condensed | normal | spacious
--controlStack-[size]-gap-[density]
├── size: small | medium | large
└── density: condensed | auto | spacious
```
### Typography Tokens
```
--text-[role]-shorthand-[size]
├── role: display | title | body | subtitle | caption | codeBlock | codeInline
└── size: small | medium | large
```
## Keyword Enforcement (RFC 2119)
### Motion
| Keyword | Rule |
| ------- | -------------------------------------------------------------- |
| MUST | Use motion for interactive state changes (hover, focus, press) |
| MUST | Keep animations ≤300ms for UI interactions |
| MUST | Respect `prefers-reduced-motion` media query |
| MUST | Provide instant alternatives when motion is reduced |
| SHOULD | Use 100-200ms for micro-interactions |
| SHOULD | Use 200-300ms for state changes |
| NEVER | Exceed 500ms for UI interactions |
| NEVER | Use motion purely for decoration |
| NEVER | Create indefinitely looping motion without user control |
| NEVER | Rely solely on motion to convey information |
### Typography
| Keyword | Rule |
| ---------- | --------------------------------------------------------------------------------------------------------------- |
| **MUST** | Use **shorthand** tokens (e.g., `font: var(...)`) to ensure `line-height` and `font-weight` are synchronized. |
| **MUST** | Use `text-codeBlock` for multi-line blocks and `text-codeInline` for inline spans. |
| **SHOULD** | Match the token to the **semantic role** (e.g., use `title` tokens for headers, not just a large `body` token). |
| **SHOULD** | Downgrade one size level for mobile viewports (e.g., `title-large` → `title-medium`). |
| **NEVER** | Use individual `font-size` or `line-height` tokens if a shorthand variant is available. |
| **NEVER** | Use `text-caption-shorthand` for multi-line body text (accessibility/readability failure). |
### Spacing
| Keyword | Rule |
| ------- | ------------------------------------------- |
| MUST | Use control tokens for interactive elements |
| MUST | Use stack tokens for layout spacing |
| MUST | Match padding density to control's purpose |
| SHOULD | Use `medium` size as default |
### Z-Index
```
--zIndex-[layer]
└── layer: behind | default | sticky | dropdown | overlay | modal | popover | skipLink
```
| Keyword | Rule |
| ------- | ------------------------------------------------------------------------------------------------------------ |
| MUST | Use z-index tokens instead of raw numeric values |
| MUST | Use `skipLink` only for accessibility skip-navigation links |
| MUST | Pair z-index with appropriate shadow level (see table below) |
| SHOULD | Prefer creating a new stacking context (`isolation: isolate`) over escalating z-index |
| NEVER | Use `behind` (-1) without verifying no ancestor creates a stacking context (transform, opacity, filter, etc) |
| NEVER | Use arbitrary z-index values outside the token scale |
**Shadow ↔ Z-Index Alignment:**
| Shadow Level | Z-Index Token | Example |
| ------------------------------ | ------------------------------------ | --------------------- |
| `shadow.resting.*` | `zIndex.default` / `zIndex.sticky` | Cards, sticky headers |
| `shadow.floating.small/medium` | `zIndex.dropdown` / `zIndex.overlay` | Menus, drawers |
| `shadow.floating.large/xlarge` | `zIndex.modal` / `zIndex.popover` | Dialogs, tooltips |
## Decision Tree: Easing Selection
1. Is element entering/exiting viewport? → ease-out (default)
2. Is element moving/morphing on screen? → ease-in-out
3. Is this a hover state change? → ease
4. Is this constant motion (loaders)? → linear
## Golden Example: Reference Component
```css
/* Button: All 5 interactive states with correct token usage */
.btn {
/* Base styles */
background-color: var(--control-bgColor-rest);
color: var(--fgColor-default);
border: none;
border-radius: var(--borderRadius-medium);
padding-block: var(--control-medium-paddingBlock);
padding-inline: var(--control-medium-paddingInline-normal);
font: var(--text-body-shorthand-medium);
cursor: pointer;
/* Motion: MUST be <300ms */
transition:
background-color 150ms ease,
box-shadow 150ms ease,
transform 100ms ease;
}
/* State: Hover */
.btn:hover {
background-color: var(--control-bgColor-hover);
}
/* State: Focus-visible (MUST use :focus-visible, not :focus) */
.btn:focus-visible {
outline: var(--focus-outline);
outline-offset: var(--outline-focus-offset);
}
/* State: Active/Pressed */
.btn:active {
background-color: var(--control-bgColor-active);
transform: scale(0.98);
}
/* State: Disabled */
.btn:disabled {
background-color: var(--bgColor-disabled);
color: var(--fgColor-disabled);
cursor: not-allowed;
opacity: 1; /* NEVER use opacity for disabled */
}
/* Accessibility: MUST respect reduced motion */
@media (prefers-reduced-motion: reduce) {
.btn {
transition: none;
}
}
```
## Interactive States Checklist
All interactive elements MUST define:
| State | Selector | Required |
| -------- | -------------------------------------- | ------------------------ |
| Rest | `.element` | ✓ |
| Hover | `:hover` | ✓ |
| Focus | `:focus-visible` | ✓ (NEVER `:focus` alone) |
| Active | `:active` | ✓ |
| Disabled | `:disabled` / `[aria-disabled="true"]` | ✓ |
## Hallucination Guard
> **If you suggest a token name not found in this spec or the system, suffix it with `/* check-token */`.**