# MediaRadar Design System

> LLM context file for `@mediaradar/design-tokens`. Provides brand rules, semantic guides, and component patterns for AI-assisted UI development. Import via `@node_modules/@mediaradar/design-tokens/DESIGN.md` in Claude Code. The token reference section below is auto-generated — rebuild the package to update resolved values.
>
> This is the authoritative resolved-values reference — if any other doc
> disagrees with it, this one wins. Two other places document the same
> system from a different angle: `design-site`'s live `/stylesheet` route
> (real components, real variants — the visual reference) and
> `mr-clientapps-insights-studio`'s local `frontend/DESIGN.md` (app-specific
> component usage).

## Brand

MediaRadar is a B2B media intelligence platform — enterprise software for advertising analytics, market research, and competitive intelligence. The UI is professional, information-dense, and analytical. It is never playful or decorative. Palette is warm-cream, not the cool-gray/blue look common to SaaS dashboards.

**Two brand colors, strictly separated by role — do not conflate them:**
- **Coral** — `sys.color.brand.primary` (`#f05323` light / `#ff7548` dark) — **identity only**: logo, AI-surface accents (sparkle icons, AI gradient endpoint), brand moments. Never a button fill, never a link, never used for "interactive" anything.
- **Warm royal blue** — `sys.color.brand.primaryAction` (`#2747c4` light / `#5b73dd` dark) — **the only interactive color**: primary CTA fills, links, focus rings, active/selected nav states.
- **Neutral "black":** `sys.color.text.primary` (`#1f1b16` light / `#f4f2ee` dark) — a warm near-black, not a cool blue-black. The whole neutral ramp (canvas/surface/subtle/borders) is warm-toned; never substitute cool grays.
- **Typography:** Instrument Sans (UI/body) + Instrument Serif (display — used italic for editorial/greeting moments). Not Roboto Flex.

## Token semantics

**Rule:** Always consume `sys.*` tokens in component styles. `ref.*` tokens bypass dark mode and semantic intent — they are raw primitives, not design decisions.

### Color roles

| Tailwind key | Use for |
|---|---|
| `canvas` | Outermost page background |
| `surface` | Cards, panels, dialogs, drawers |
| `subtle` | Secondary areas, sidebars, striped table rows |
| `text-primary` | Body copy, headings, form values |
| `text-secondary` | Supporting text, metadata, timestamps |
| `text-inverse` | Text/icons on dark or brand-colored backgrounds |
| `text-warning` | Caution states |
| `brand-primary` | Logo, AI-surface accents, brand identity moments ONLY — never buttons/links |
| `brand-primaryAction` | CTAs, links, focus rings, active/selected nav — the actual interactive color |
| `brand-onprimary` | Text/icons placed directly on brand-primary backgrounds |
| `border-default` | Dividers, input outlines, card borders |
| `border-focus` | Keyboard focus rings |
| `status-success` | Positive values, confirmations |
| `status-danger` | Errors, destructive actions |
| `status-onstatus` | Text/icons on status-colored backgrounds |

### Typography hierarchy

Prefer `text-style-*` composite classes — they set size, weight, line-height, letter-spacing, and color in one class and support all Tailwind variants (`hover:`, `md:`, etc.).

| Role | Class | Use for |
|---|---|---|
| Page title | `text-style-heading-1` | h1, page-level titles |
| Section heading | `text-style-heading-2` | h2, major section headers |
| Sub-heading | `text-style-heading-3` | h3, card titles, panel headers |
| Large body | `text-style-body-lg` | Intro or featured paragraphs |
| Body | `text-style-body` | Standard paragraphs, descriptions |
| Small body | `text-style-body-sm` | Secondary content, sidebar text |
| XS body | `text-style-body-xs` | Fine print, helper text |
| Label | `text-style-label` | Form labels, key–value metadata |
| Label strong | `text-style-label-strong` | Bold labels, selected state labels |
| Label small | `text-style-label-sm` | Compact labels, tag text |
| Label XS | `text-style-label-xs` | Dense table headers, badge text |
| Caption | `text-style-caption` | Tooltips, image captions, timestamps |
| Link | `text-style-link` | Anchor text |
| Link strong | `text-style-link-strong` | Prominent / bold links |
| Link XS | `text-style-link-xs` | Inline small links |

## Component patterns

### Card
```tsx
<div className="bg-surface rounded-card shadow-raised border border-border-default p-6">
  <h2 className="text-style-heading-3">Title</h2>
  <p className="text-style-body text-text-secondary mt-2">Description</p>
</div>
```

### Form input
```tsx
<label className="text-style-label text-text-primary">
  Field label
  <input
    className="mt-1 block w-full bg-surface rounded-control border border-border-default
               px-3 py-2 text-style-body text-text-primary
               focus:border-border-focus focus:outline-none"
  />
</label>
```

### Primary button
```tsx
<button className="inline-flex items-center gap-2 bg-brand-primaryAction text-brand-onprimary
                   rounded-control px-4 py-2 text-style-label-strong
                   hover:opacity-90 disabled:opacity-disabled">
  Action
</button>
```
Note: `bg-brand-primary` (coral) is reserved for identity/AI-accent moments — never a button fill.

### Dialog / modal
```tsx
<div className="fixed inset-0 z-modal flex items-center justify-center">
  <div className="bg-surface rounded-card shadow-overlay p-8 w-full max-w-lg">
    <h2 className="text-style-heading-2">Dialog title</h2>
    <p className="text-style-body text-text-secondary mt-3">Content goes here.</p>
  </div>
</div>
```

### Data table row
```tsx
<tr className="border-b border-border-default hover:bg-subtle
               transition-colors duration-interactive ease-interactive">
  <td className="px-4 py-3 text-style-body text-text-primary">{value}</td>
  <td className="px-4 py-3 text-style-body-sm text-text-secondary">{meta}</td>
</tr>
```

### Status badge
```tsx
<span className="inline-flex items-center gap-1 rounded-control px-2 py-1
                 text-style-label-xs bg-status-success text-status-onstatus">
  Active
</span>
```

### Dropdown / tooltip overlay
```tsx
<div className="bg-surface rounded-card shadow-overlay border border-border-default
               z-dropdown p-1">
  {items}
</div>
```

## Dark mode

Set `data-theme="dark"` on any ancestor element. All `sys.*` tokens adapt automatically — no `dark:` Tailwind variants needed.

```ts
document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light')
```

**Diagnostic:** If a color is not adapting to dark mode, verify you're using a `sys.color.*` token (e.g., `bg-canvas`) rather than a `ref.color.*` primitive (e.g., `--mr-ref-color-shadow-universal-2`). Only sys tokens have dark overrides.

## Decision guide

When building UI, follow this order:

1. **Text:** Is there a `text-style-*` that matches the typographic role? Use it — do not manually compose font-size + weight + line-height.
2. **Color:** Is there a `sys.color.*` semantic name describing the element's intent? Use it — `bg-{key}`, `text-{key}`, `border-{key}`, etc.
3. **Spacing:** Is there a spacing scale value that fits? Use the discrete scale (`p-4`, `gap-3`, `mt-8`). Avoid arbitrary values. Use pixel values (`p-1px`) only for sub-pixel tuning.
4. **Shape/elevation:** Use `rounded-control` for interactive elements, `rounded-card` for containers. Use `shadow-raised` for resting cards, `shadow-overlay` for floating surfaces.
5. **Primitives:** Reach into `ref.*` only when no semantic token fits. If the pattern recurs, propose a new `sys.*` token.

---

## Token reference

> Auto-generated from Style Dictionary. All values are resolved — no aliases. Rebuild to update.

### Colors

Key is the suffix for any Tailwind color utility: `bg-{key}`, `text-{key}`, `border-{key}`, `ring-{key}`, `fill-{key}`, etc. Dark values activate when `data-theme="dark"` is set on any ancestor.

| Key | Light | Dark |
| --- | --- | --- |
| canvas | #f4f2ee | #1c1814 |
| surface | #fbfaf7 | #26211b |
| subtle | #ebe7e0 | #322c24 |
| text-primary | #1f1b16 | #f4f2ee |
| text-secondary | #6b6358 | #b5aea1 |
| text-tertiary | #5f574b | #a39b8c |
| text-inverse | #fbfaf7 | #1c1814 |
| text-warning | #5c4118 | #e8c285 |
| text-danger | #8b1018 | #ff7068 |
| text-success | #1d4d2a | #7fc28f |
| text-link | #2747c4 | #8aa0ff |
| brand-primary | #f05323 | #ff7548 |
| brand-primaryAction | #2747c4 | #5b73dd |
| brand-onprimary | #ffffff | — |
| brand-primarySoft | #ebe3d5 | #3a322a |
| status-success | #1d7a3b | #3a9555 |
| status-warning | #b8843a | #d9a655 |
| status-danger | #c53030 | #e84a4a |
| status-onstatus | #ffffff | — |
| status-on-status | #ffffff | — |
| border-default | #dcd6cb | #3a3329 |
| border-interactive | #6b6358 | — |
| border-focus | #2747c4 | #8aa0ff |
| border-focusSoft | rgba(39, 71, 196, 0.28) | rgba(138, 160, 255, 0.35) |
| ai-tint | #e4e2f5 | #2b2e48 |
| ai-ontint | #2747c4 | #8aa0ff |
| ai-gradient | linear-gradient(135deg, #2747c4 0%, #f05323 60%, #f99d20 100%) | linear-gradient(135deg, #5b73dd 0%, #ff7548 60%, #ffb878 100%) |

### Typography

**Font sizes** — Tailwind: `text-{key}`

| Key | Value |
| --- | --- |
| xs | 0.75rem |
| sm | 0.875rem |
| base | 1rem |
| md | 1.125rem |
| lg | 1.25rem |
| xl | 1.5rem |
| h1 | 1.875rem |

**Font weights** — Tailwind: `font-{key}`

| Key | Value |
| --- | --- |
| regular | 400 |
| medium | 500 |
| semibold | 600 |
| bold | 700 |

**Line heights** — Tailwind: `leading-{key}`

| Key | Value |
| --- | --- |
| tight | 1.2 |
| base | 1.5 |
| head | 1.5 |

**Letter spacing** — Tailwind: `tracking-{key}`

| Key | Value |
| --- | --- |
| normal | 0em |
| tight | -0.01em |

**Text styles** — Tailwind: `text-style-{name}`. One class sets font-size, font-weight, line-height, letter-spacing, and color. Supports all Tailwind variants.

| Style | Size | Weight | Leading | Tracking | Color |
| --- | --- | --- | --- | --- | --- |
| text-style-heading-1 | 1.875rem | 600 | 1.5 | 0em | #363233 |
| text-style-heading-2 | 1.25rem | 600 | 1.2 | 0em | #1f1b16 |
| text-style-heading-3 | 0.875rem | 600 | 1.2 | 0em | #1f1b16 |
| text-style-body-lg | 1.25rem | 700 | 1.2 | 0em | #1f1b16 |
| text-style-body | 1.125rem | 400 | 1.5 | 0em | #1f1b16 |
| text-style-body-sm | 0.875rem | 400 | 1.5 | 0em | #1f1b16 |
| text-style-body-xs | 0.75rem | 400 | 1.5 | 0em | #1f1b16 |
| text-style-label | 1rem | 400 | 1.2 | 0em | #1f1b16 |
| text-style-label-strong | 1rem | 600 | 1.2 | 0em | #1f1b16 |
| text-style-label-sm | 0.875rem | 700 | 1.2 | 0em | #6b6358 |
| text-style-label-xs | 0.75rem | 600 | 1.2 | 0em | #6b6358 |
| text-style-caption | 0.75rem | 400 | 1.5 | 0em | #6b6358 |
| text-style-link | 0.875rem | 400 | 1.5 | 0em | #2747c4 |
| text-style-link-strong | 0.875rem | 600 | 1.5 | 0em | #2747c4 |
| text-style-link-xs | 0.75rem | 400 | 1.5 | 0em | #2747c4 |

### Spacing

Tailwind: `p-{key}`, `m-{key}`, `gap-{key}`, `w-{key}`, `h-{key}`, etc. Integer scale: 1 step = 0.25rem (4px) — so `p-4` = 1rem = 16px. Half-steps (e.g. `p-1_5`) and pixel values (e.g. `p-1px`) are available.

**rem scale:**

| Key | Value |
| --- | --- |
| 0 | 0rem |
| 1 | 0.25rem |
| 2 | 0.5rem |
| 3 | 0.75rem |
| 4 | 1rem |
| 5 | 1.25rem |
| 6 | 1.5rem |
| 7 | 1.75rem |
| 8 | 2rem |
| 9 | 2.25rem |
| 10 | 2.5rem |
| 11 | 2.75rem |
| 12 | 3rem |
| 13 | 3.25rem |
| 14 | 3.5rem |
| 15 | 3.75rem |
| 16 | 4rem |
| 17 | 4.25rem |
| 18 | 4.5rem |
| 19 | 4.75rem |
| 20 | 5rem |
| 21 | 5.25rem |
| 22 | 5.5rem |
| 23 | 5.75rem |
| 24 | 6rem |
| 25 | 7rem |
| 26 | 8rem |
| 27 | 9rem |
| 28 | 10rem |
| 29 | 11rem |
| 30 | 12rem |
| 1_5 | 0.375rem |
| 2_5 | 0.625rem |
| 3_5 | 0.875rem |
| 4_5 | 1.125rem |
| 5_5 | 1.375rem |
| 6_5 | 1.625rem |
| 7_5 | 1.875rem |

**pixel values:**

| Key | Value |
| --- | --- |
| 0px | 0px |
| 1px | 1px |
| 2px | 2px |
| 3px | 3px |
| 5px | 5px |
| 6px | 6px |
| 7px | 7px |
| 8px | 8px |
| 9px | 9px |
| 10px | 10px |

**Semantic spacing** — no Tailwind utility; use `var(--mr-spacing-{key})` in CSS directly.

| Token | Value |
| --- | --- |
| control-padding-x | 0.75rem |
| control-padding-y | 0.5rem |
| layout-xs | 0.5rem |
| layout-sm | 0.75rem |
| layout-md | 1rem |
| layout-lg | 1.5rem |
| layout-xl | 2rem |
| card-padding | 1.25rem |
| dialog-padding | 1.5rem |

### Shape

**Border radius** — Tailwind: `rounded-{key}`

| Key | Value |
| --- | --- |
| control | 8px |
| card | 12px |

### Elevation

**Box shadows** — Tailwind: `shadow-{key}`

| Key | Value |
| --- | --- |
| raised | #000bec |
| overlay | #000bec |

### Motion

| Type | Key | Tailwind | Value |
| --- | --- | --- | --- |
| duration | interactive | duration-interactive | 200ms |
| easing | interactive | ease-interactive | cubic-bezier(0.2, 0, 0, 1) |

### Opacity

Tailwind: `opacity-{key}`

| Key | Value |
| --- | --- |
| disabled | 0.4 |

### Z-index

Tailwind: `z-{key}`

| Key | Value |
| --- | --- |
| dropdown | 1000 |
| modal | 1100 |
| tooltip | 1200 |
