import { Meta } from '@storybook/addon-docs/blocks';

<Meta title="Design Tokens" />

# Design tokens

Everything below is read live from the stylesheet. If a swatch here does not
move when you edit `src/styles/tokens.css`, something is hardcoded and needs
fixing.

export const Swatch = ({ token, label }) => (
  <div style={{ display: 'flex', flexDirection: 'column', gap: 4, minWidth: 84 }}>
    <div
      style={{
        background: `var(${token})`,
        height: 52,
        borderRadius: 8,
        border: '1px solid var(--border)',
      }}
    />
    <code style={{ fontSize: 11, opacity: 0.75 }}>{label}</code>
  </div>
);

export const Ramp = ({ name, steps }) => (
  <>
    <h3 style={{ textTransform: 'capitalize' }}>{name}</h3>
    <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginBottom: 24 }}>
      {steps.map((s) => (
        <Swatch key={s} token={`--ui-color-${name}-${s}`} label={s} />
      ))}
    </div>
  </>
);

## Colour primitives

Raw ramps in oklch. **Components never reference these directly** — they are the
input to the semantic layer below.

<Ramp name="neutral" steps={[50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950]} />
<Ramp name="brand" steps={[50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950]} />
<Ramp name="success" steps={[100, 300, 500, 700, 900]} />
<Ramp name="warning" steps={[100, 300, 500, 700, 900]} />
<Ramp name="danger" steps={[100, 300, 500, 700, 900]} />

## Semantic roles

What components actually use. Each role resolves to a primitive, and swaps
wholesale in dark mode — flip the theme toggle to watch these change while the
ramps above stay put.

export const roles = [
  'background',
  'foreground',
  'card',
  'popover',
  'primary',
  'secondary',
  'accent',
  'muted',
  'destructive',
  'success',
  'warning',
  'border',
  'input',
  'ring',
];

<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
  {roles.map((r) => (
    <Swatch key={r} token={`--${r}`} label={r} />
  ))}
</div>

## Radius

Every radius derives from `--ui-radius`, so one edit re-rounds the whole kit.

export const radii = ['sm', 'md', 'lg', 'xl'];

<div style={{ display: 'flex', gap: 16, marginTop: 12 }}>
  {radii.map((r) => (
    <div key={r} style={{ textAlign: 'center' }}>
      <div
        style={{
          width: 72,
          height: 72,
          background: 'var(--secondary)',
          border: '1px solid var(--border)',
          borderRadius: `var(--ui-radius-${r})`,
        }}
      />
      <code style={{ fontSize: 11 }}>{r}</code>
    </div>
  ))}
</div>

## Elevation

Two-layer shadows — a tight contact shadow plus a soft ambient one. A single
blurred shadow reads as a grey smudge rather than depth.

export const shadows = ['xs', 'sm', 'md', 'lg', 'xl'];

<div style={{ display: 'flex', gap: 20, marginTop: 12, flexWrap: 'wrap' }}>
  {shadows.map((s) => (
    <div key={s} style={{ textAlign: 'center' }}>
      <div
        style={{
          width: 88,
          height: 64,
          background: 'var(--card)',
          borderRadius: 'var(--ui-radius-lg)',
          boxShadow: `var(--ui-shadow-${s})`,
        }}
      />
      <code style={{ fontSize: 11 }}>{s}</code>
    </div>
  ))}
</div>

## Motion

| Token | Value | Use |
| --- | --- | --- |
| `--ui-duration-fast` | 120ms | hover, focus, colour changes |
| `--ui-duration-base` | 180ms | popovers, dropdowns, tooltips |
| `--ui-duration-slow` | 280ms | dialogs, drawers, sheets |
| `--ui-ease-standard` | `cubic-bezier(0.2, 0, 0, 1)` | most transitions |
| `--ui-ease-emphasized` | `cubic-bezier(0.05, 0.7, 0.1, 1)` | entrances that need weight |

All motion is suppressed under `prefers-reduced-motion: reduce` by a global rule
in `globals.css`, so individual components do not need to handle it.

## Syncing from Figma

`tokens.css` is machine-owned. With the Figma Dev Mode MCP server running:

```bash
/figma-sync <figma-file-url>
```

The skill reads your Figma Variables, shows the diff, and rewrites the
primitives. `theme.css` — the mapping from primitives to roles — is hand-owned
and survives the sync.
