# SGT Design Tokens — Reference

<!-- GENERATED by build/docs.js — do not hand-edit. Run `npm run build`. -->

Source of truth: `tokens/` (DTCG JSON) · Generated CSS: `tokens.css` · Machine-readable mirror: [`docs/tokens.json`](./tokens.json)

**137 CSS custom properties**, of which 87 are the frozen legacy API and 50 the additive modern API.

## How to read this

The design system exposes **two CSS APIs from one source**:

| API | Prefix | Status | Use for |
|---|---|---|---|
| **Legacy** | `--sgt-*`, `--text-*`, `--lh-*`, `--sp-*`, `--radius-*`, `--shadow-*`, `--glass-*`, `--ease-*`, `--dur-*` | Frozen — values never change | Everything shipped today: `sgt-ds-core.css`, all decks |
| **Modern** | `--sgt-` + tier path | Additive, safe to adopt | New web work: fluid scales, semantic colours, dark mode |

Adding a value means adding a token in `tokens/` and running `npm run build` — never hand-editing `tokens.css`, and never writing a raw hex or px into a component stylesheet.

---

## Tier 1 — Primitives (legacy CSS API)

### Colours

| CSS variable | Hex | OKLCH (design SoT) | Source token |
|---|---|---|---|
| `--sgt-purple-light` | `#d2c1ff` | `oklch(84.592% 0.087 297.08)` | `color.purple.300` |
| `--sgt-purple-gradient` | `#8972fc` | `oklch(64.209% 0.1974 287.12)` | `color.purple.400` |
| `--sgt-purple` | `#754cff` | `oklch(57.118% 0.2484 286.03)` | `color.purple.500` |
| `--sgt-white` | `#ffffff` | `oklch(100% 0 0)` | `color.neutral.0` |
| `--sgt-grey-light` | `#f5f5f5` | `oklch(97.015% 0 0)` | `color.neutral.100` |
| `--sgt-border` | `#e0e0e0` | `oklch(90.67% 0 0)` | `color.neutral.200` |
| `--sgt-grey-mid` | `#cccccc` | `oklch(84.522% 0 0)` | `color.neutral.300` |
| `--sgt-grey-dark` | `#aaaaaa` | `oklch(73.802% 0 0)` | `color.neutral.400` |
| `--sgt-grey` | `#767676` | `oklch(56.584% 0 0)` | `color.neutral.500` |
| `--sgt-black` | `#1e1e1e` | `oklch(23.503% 0 0)` | `color.neutral.900` |
| `--sgt-warm-white` | `#faf8f5` | `oklch(97.983% 0.0045 78.3)` | `color.warm.50` |
| `--sgt-strategy` | `#d9e530` | `oklch(88.392% 0.1865 113.62)` | `color.strategy.default` |
| `--sgt-strategy-light` | `#d3f030` | `oklch(90.334% 0.1998 118.51)` | `color.strategy.light` |
| `--sgt-creativity` | `#ffd129` | `oklch(87.585% 0.1709 91.7)` | `color.creativity.default` |
| `--sgt-creativity-light` | `#ffc800` | `oklch(85.747% 0.1752 88.49)` | `color.creativity.light` |
| `--sgt-technology` | `#629cfa` | `oklch(69.556% 0.1517 259.62)` | `color.technology.default` |
| `--sgt-technology-light` | `#7dafff` | `oklch(75.163% 0.1279 259.49)` | `color.technology.light` |
| `--sgt-green` | `#1d862d` | `oklch(54.362% 0.1566 145.08)` | `color.system.green` |
| `--sgt-red` | `#ec1111` | `oklch(59.631% 0.2389 28.71)` | `color.system.red` |

Colours are authored in OKLCH. The hex column is what `tokens.css` emits for the legacy names — the OKLCH round-trips to exactly that hex.

### Font family

| CSS variable | Value | Source token |
|---|---|---|
| `--sgt-font` | `'DM Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif` | `font.family.sans` |

### Type scale — desktop

| CSS variable | Value | Source token |
|---|---|---|
| `--text-sm` | `14px` | `text.sm` |
| `--text-md` | `18px` | `text.md` |
| `--text-lg` | `24px` | `text.lg` |
| `--text-xl` | `30px` | `text.xl` |
| `--text-2xl` | `40px` | `text.2xl` |
| `--text-3xl` | `50px` | `text.3xl` |
| `--text-4xl` | `60px` | `text.4xl` |
| `--text-5xl` | `70px` | `text.5xl` |
| `--text-6xl` | `80px` | `text.6xl` |
| `--text-7xl` | `120px` | `text.7xl` |
| `--text-8xl` | `160px` | `text.8xl` |

### Line heights

| CSS variable | Value | Source token |
|---|---|---|
| `--lh-sm` | `22px` | `lh.sm` |
| `--lh-md` | `26px` | `lh.md` |
| `--lh-lg` | `34px` | `lh.lg` |
| `--lh-xl` | `40px` | `lh.xl` |
| `--lh-2xl` | `52px` | `lh.2xl` |
| `--lh-3xl` | `63px` | `lh.3xl` |
| `--lh-4xl` | `75px` | `lh.4xl` |
| `--lh-5xl` | `88px` | `lh.5xl` |
| `--lh-6xl` | `100px` | `lh.6xl` |
| `--lh-7xl` | `120px` | `lh.7xl` |
| `--lh-8xl` | `160px` | `lh.8xl` |

### Type scale — mobile

| CSS variable | Value | Source token |
|---|---|---|
| `--text-sm-m` | `14px` | `textMobile.sm` |
| `--text-md-m` | `16px` | `textMobile.md` |
| `--text-lg-m` | `18px` | `textMobile.lg` |
| `--text-xl-m` | `22px` | `textMobile.xl` |
| `--text-2xl-m` | `28px` | `textMobile.2xl` |
| `--text-3xl-m` | `35px` | `textMobile.3xl` |
| `--text-4xl-m` | `38px` | `textMobile.4xl` |
| `--text-5xl-m` | `40px` | `textMobile.5xl` |
| `--text-6xl-m` | `45px` | `textMobile.6xl` |
| `--text-7xl-m` | `55px` | `textMobile.7xl` |
| `--text-8xl-m` | `65px` | `textMobile.8xl` |

Under `@media (max-width: 768px)` each `--text-*` is reassigned to its `-m` sibling, so components consume `--text-*` unchanged and need no media query of their own.

### Letter-spacing

| CSS variable | Value | Source token |
|---|---|---|
| `--tracking-default` | `0.01em` | `tracking.default` |
| `--tracking-loose` | `0.02em` | `tracking.loose` |

Never negative — brand rule.

### Spacing (4px base)

| CSS variable | Value | Source token |
|---|---|---|
| `--sp-1` | `4px` | `sp.1` |
| `--sp-2` | `8px` | `sp.2` |
| `--sp-3` | `12px` | `sp.3` |
| `--sp-4` | `16px` | `sp.4` |
| `--sp-5` | `20px` | `sp.5` |
| `--sp-6` | `24px` | `sp.6` |
| `--sp-8` | `32px` | `sp.8` |
| `--sp-10` | `40px` | `sp.10` |
| `--sp-12` | `48px` | `sp.12` |
| `--sp-15` | `60px` | `sp.15` |
| `--sp-20` | `80px` | `sp.20` |

### Radii

| CSS variable | Value | Source token |
|---|---|---|
| `--radius-sm` | `6px` | `radius.sm` |
| `--radius-md` | `10px` | `radius.md` |
| `--radius-lg` | `16px` | `radius.lg` |
| `--radius-xl` | `24px` | `radius.xl` |
| `--radius-full` | `9999px` | `radius.full` |

### Shadows

| CSS variable | Value | Source token |
|---|---|---|
| `--shadow-sm` | `0 1px 2px rgba(30, 30, 30, 0.06)` | `shadow.sm` |
| `--shadow-md` | `0 4px 12px rgba(30, 30, 30, 0.08)` | `shadow.md` |
| `--shadow-lg` | `0 12px 32px rgba(30, 30, 30, 0.12)` | `shadow.lg` |

### Gradients

| CSS variable | Value | Source token |
|---|---|---|
| `--sgt-gradient-purple` | `linear-gradient(135deg, #1e1e1e 0%, #2a1a4e 40%, #3d2480 70%, #754cff 100%)` | `gradient.purple` |
| `--sgt-gradient-strategy` | `linear-gradient(135deg, #1e1e1e 0%, #2a2e1a 35%, #4a5a18 65%, #d9e530 100%)` | `gradient.strategy` |
| `--sgt-gradient-creativity` | `linear-gradient(135deg, #1e1e1e 0%, #2e2a1a 35%, #5a4a18 65%, #ffd129 100%)` | `gradient.creativity` |
| `--sgt-gradient-technology` | `linear-gradient(135deg, #1e1e1e 0%, #1a2230 35%, #1e3a5a 65%, #629cfa 100%)` | `gradient.technology` |
| `--sgt-gradient-dark` | `linear-gradient(180deg, #1e1e1e 0%, #2a2a2a 100%)` | `gradient.dark` |
| `--sgt-gradient-purple-radial` | `radial-gradient(circle at center, #754cff 0%, #8972fc 100%)` | `gradient.purple-radial` |

### Glassmorphism

| CSS variable | Value | Source token |
|---|---|---|
| `--glass-bg` | `rgba(255, 255, 255, 0.06)` | `glass.bg` |
| `--glass-border` | `rgba(255, 255, 255, 0.10)` | `glass.border` |

### Motion

| CSS variable | Value | Source token |
|---|---|---|
| `--dur-fast` | `160ms` | `duration.fast` |
| `--dur-base` | `240ms` | `duration.base` |
| `--dur-slow` | `400ms` | `duration.slow` |

| CSS variable | Value | Source token |
|---|---|---|
| `--ease-out` | `cubic-bezier(0.2, 0.8, 0.2, 1)` | `easing.out` |
| `--ease-in-out` | `cubic-bezier(0.4, 0, 0.2, 1)` | `easing.inOut` |

---

## Tier 2 — Semantic (additive, `--sgt-` prefixed)

### Semantic colours — light / dark

Emitted as `light-dark(light, dark)`. Dark mode is **opt-in**: set `class="sgt-theme"` (follow OS) or `data-sgt-theme="dark|light"` on `<html>`. Pages that do not opt in render light, exactly as before.

| CSS variable | Light | Dark |
|---|---|---|
| `--sgt-color-text-default` | `color.neutral.900` | `color.neutral.0` |
| `--sgt-color-text-muted` | `color.neutral.500` | `color.neutral.300` |
| `--sgt-color-text-inverse` | `color.neutral.0` | `color.neutral.900` |
| `--sgt-color-surface-default` | `color.neutral.0` | `color.neutral.900` |
| `--sgt-color-surface-raised` | `color.neutral.100` | `color.neutral.850` |
| `--sgt-color-border-default` | `color.neutral.200` | `color.neutral.850` |
| `--sgt-color-border-strong` | `color.neutral.300` | `color.neutral.500` |
| `--sgt-color-accent-default` | `color.purple.500` | `color.purple.400` |
| `--sgt-color-accent-muted` | `color.purple.300` | `color.purple.300` |
| `--sgt-color-action-default` | `color.purple.500` | `color.purple.400` |
| `--sgt-color-action-text` | `color.neutral.0` | `color.neutral.0` |
| `--sgt-color-feedback-success` | `color.system.green` | `color.system.green` |
| `--sgt-color-feedback-danger` | `color.system.red` | `color.system.red` |
| `--sgt-color-department-strategy` | `color.strategy.default` | `color.strategy.light` |
| `--sgt-color-department-creativity` | `color.creativity.default` | `color.creativity.light` |
| `--sgt-color-department-technology` | `color.technology.default` | `color.technology.light` |

### Fluid type scale

Utopia-style `clamp()`, poles 320px → 1280px. The fixed `--text-*` scale is untouched — decks depend on exact px.

| CSS variable | Value | Source token |
|---|---|---|
| `--sgt-font-size-sm` | `0.875rem` | `font.size.sm` |
| `--sgt-font-size-md` | `clamp(1rem, 0.9583rem + 0.2083vw, 1.125rem)` | `font.size.md` |
| `--sgt-font-size-lg` | `clamp(1.125rem, 1rem + 0.625vw, 1.5rem)` | `font.size.lg` |
| `--sgt-font-size-xl` | `clamp(1.375rem, 1.2083rem + 0.8333vw, 1.875rem)` | `font.size.xl` |
| `--sgt-font-size-2xl` | `clamp(1.75rem, 1.5rem + 1.25vw, 2.5rem)` | `font.size.2xl` |
| `--sgt-font-size-3xl` | `clamp(2.1875rem, 1.875rem + 1.5625vw, 3.125rem)` | `font.size.3xl` |
| `--sgt-font-size-4xl` | `clamp(2.375rem, 1.9167rem + 2.2917vw, 3.75rem)` | `font.size.4xl` |
| `--sgt-font-size-5xl` | `clamp(2.5rem, 1.875rem + 3.125vw, 4.375rem)` | `font.size.5xl` |
| `--sgt-font-size-6xl` | `clamp(2.8125rem, 2.0833rem + 3.6458vw, 5rem)` | `font.size.6xl` |
| `--sgt-font-size-7xl` | `clamp(3.4375rem, 2.0833rem + 6.7708vw, 7.5rem)` | `font.size.7xl` |
| `--sgt-font-size-8xl` | `clamp(4.0625rem, 2.0833rem + 9.8958vw, 10rem)` | `font.size.8xl` |

### Fluid space scale

| CSS variable | Value | Source token |
|---|---|---|
| `--sgt-space-3xs` | `0.25rem` | `space.3xs` |
| `--sgt-space-2xs` | `0.5rem` | `space.2xs` |
| `--sgt-space-xs` | `clamp(0.5rem, 0.4167rem + 0.4167vw, 0.75rem)` | `space.xs` |
| `--sgt-space-s` | `clamp(0.75rem, 0.6667rem + 0.4167vw, 1rem)` | `space.s` |
| `--sgt-space-m` | `clamp(1rem, 0.8333rem + 0.8333vw, 1.5rem)` | `space.m` |
| `--sgt-space-l` | `clamp(1.5rem, 1.1667rem + 1.6667vw, 2.5rem)` | `space.l` |
| `--sgt-space-xl` | `clamp(2.5rem, 2rem + 2.5vw, 4rem)` | `space.xl` |
| `--sgt-space-2xl` | `clamp(4rem, 3.3333rem + 3.3333vw, 6rem)` | `space.2xl` |
| `--sgt-space-3xl` | `clamp(6rem, 5.3333rem + 3.3333vw, 8rem)` | `space.3xl` |

### Font weights

| CSS variable | Value | Source token |
|---|---|---|
| `--sgt-font-weight-regular` | `400` | `font.weight.regular` |
| `--sgt-font-weight-medium` | `500` | `font.weight.medium` |

Hierarchy comes from size, never weight. 400 everywhere, 500 only for small labels, never 700.

### Type roles

| CSS variable | Resolves to | Source token |
|---|---|---|
| `--sgt-role-display` | `var(--sgt-font-size-7xl)` | `role.display` |
| `--sgt-role-heading` | `var(--sgt-font-size-5xl)` | `role.heading` |
| `--sgt-role-title` | `var(--sgt-font-size-3xl)` | `role.title` |
| `--sgt-role-lead` | `var(--sgt-font-size-2xl)` | `role.lead` |
| `--sgt-role-body` | `var(--sgt-font-size-lg)` | `role.body` |
| `--sgt-role-caption` | `var(--sgt-font-size-md)` | `role.caption` |
| `--sgt-role-label` | `var(--sgt-font-size-sm)` | `role.label` |
| `--sgt-role-quote` | `var(--sgt-font-size-4xl)` | `role.quote` |

---

## Tier 3 — Component tokens

Additive and minimal by design. A component token MUST reference a semantic (or, rarely, a primitive) token — never a raw value.

| CSS variable | Resolves to | Source token |
|---|---|---|
| `--sgt-button-primary-background` | `var(--sgt-color-action-default)` | `button.primary.background` |
| `--sgt-button-primary-text` | `var(--sgt-color-action-text)` | `button.primary.text` |
| `--sgt-button-primary-radius` | `var(--radius-full)` | `button.primary.radius` |
| `--sgt-button-primary-padding` | `var(--sp-3)` | `button.primary.padding` |

---

## Not emitted as CSS variables

These exist in `tokens/` as internal ramp steps and are referenced inline by semantic tokens, so they get no custom property of their own:

- `color.neutral.850` — Dark-mode raised surface. Not a legacy var; the value #2a2a2a already appears in the brand dark gradient.

---

## Rebuilding

```bash
npm install     # once, Node >= 22
npm run build   # tokens/ -> tokens.css + docs/tokens.md + docs/tokens.json
npm run verify  # legacy vars byte-identical + docs in sync with tokens.css
```
