# SHARP-ER design tokens
Two tiers, so changing the brand is a one-line edit in one file.
```
app/app.css entry - imports tailwindcss then the three files below
app/styles/primitives.css TIER 1 raw values, no meaning (@theme)
app/styles/semantic.css TIER 2 roles that alias tier 1 (per surface)
app/styles/typography.css TIER 2 type roles, @font-face, EN/AR font switch
```
## The one rule
**Components may reference only tier-2 tokens.**
Never a raw hex, never a tier-1 primitive.
This binds `app/app.css` too. Its `@layer base` block styles the document, so
the body type is set from `--type-body-base-size` / `--type-body-base-leading`
(the semantic alias of the Body/Medium role, declared in `semantic.css`) rather
than from `--text-md` / `--leading-6` directly. Only `primitives.css`,
`semantic.css` and `typography.css` may name a tier-1 token.
```tsx
// yes
// no
// raw hex
// tier-1 primitive
// raw type
```
Grep for violations: `grep -rn "#[0-9a-fA-F]\{6\}\|--color-green\|--color-neutral\|--color-red" app/components app/routes`.
### Two ways to spend a tier-2 token
1. `bg-[var(--surface-page)]` - works for every token, always correct.
2. A Tailwind utility from the bridge at the bottom of `semantic.css`. The name
is always `-`:
`--surface-page` -> `bg-surface-page`, `--text-primary` -> `text-text-primary`,
`--border-subtle` -> `border-border-subtle`, `--radius-card` -> `rounded-card`.
The bridge uses `@theme inline`, so these still re-resolve inside a surface.
Type roles are utilities: `display-sm`, `heading-h1`, `heading-h2`, `heading-h3`,
`body-lg`, `body-lg-bold`, `body-md`, `body-md-bold`, `body-sm`, `body-sm-bold`,
`label-md`. They set family, size, line-height, tracking and weight together, and
they compose with variants (`md:heading-h1`).
## Tier 1 - primitives (`primitives.css`)
A Tailwind 4 `@theme static` block. Raw ramps only, named by value:
`--color-green-600`, never `--color-accent`.
* colour - `white`, `black`, `transparent`, `neutral-50..900`, `green-50..900`,
`red-50..900`, plus alpha steps (`white-a12`, `black-a56`, `green-a24`, ...)
* type - `--font-en` / `--font-ar` / `--font-sans` / `--font-mono`,
`--text-xs..5xl`, `--leading-4..12`, `--tracking-tighter..wider`,
`--font-weight-regular..black`
* space `--spacing-2xs..5xl` - radius `--radius-none..full` -
border width `--border-width-sm..xl` - opacity `--opacity-0..100`
* shadow `--shadow-xs..xl` plus the dark-ground steps `--shadow-dark-lg|xl` -
duration `--duration-instant..slower` -
easing `--ease-standard | decelerate | accelerate | emphasized`
* z ladder `--z-0..70` - containers `--container-prose | content | wide`
The hexes taken verbatim from the published Figma variables are marked `[figma]`;
the rest of each ramp is derived from them (tints mix with white, shades multiply
toward black).
### Changing a brand colour
Edit the ramp in `primitives.css` and nothing else. To move the brand green:
```css
--color-green-600: #008764; /* brand -> every accent, primary button, link */
--color-green-700: #006b4f; /* hover */
--color-green-800: #00563f; /* active */
--color-green-400: #4daa93; /* disabled fill, accent text on dark */
```
Same for `--color-red-*` (danger) and `--color-neutral-800/900` (the dark
surface). No component file changes.
## Tier 2 - semantics (`semantic.css`)
Plain CSS custom properties whose value is always `var(--)`. Families:
`--surface-*` `--text-*` `--border-*` `--interactive--*` `--field-*`
`--focus-ring-*` `--overlay-*` `--radius-*` `--control-*` `--layout-*`
`--elevation-*` `--motion-*` `--layer-*` `--type-body-base-*`
Two families are worth knowing by heart, because both were retuned to the
Claude Design drafts rather than to the older Figma Button frame:
* **Shape.** The drafts draw three corners, not five. `--radius-chip` (2px) for
a tag, thumbnail, flag or checkbox; `--radius-control` / `--radius-card` /
`--radius-surface` (all 4px) for a button, field, menu, card or panel;
`--radius-pill` for a filter chip or status pill. Nothing in the drafts is
8px or 12px, which is why the old `--radius-control-lg` is gone. The tier-1
ramp still carries `--radius-md` / `--radius-lg` - only the meanings moved.
* **Elevation.** A shadow is a fraction of the ground it falls on, so
`--elevation-card`, `--elevation-popover` and `--elevation-modal` are
declared **per surface**. On the dark surface a resting card has no shadow at
all (the drafts separate with a hairline) and the deep black - tier 1's
`--shadow-dark-lg` / `--shadow-dark-xl`, the drafts' own
`0 20px 44px rgba(0,0,0,.5)` and `0 24px 48px rgba(0,0,0,.6)` - is spent on
what actually floats: a card under the pointer, the nav dropdown. The light
surface keeps the soft ramp. Only `--elevation-flat` and `--elevation-raised`
are surface-independent.
Interactive tokens are fully regular - for each of the five Figma button
variants (`primary`, `secondary`, `dark`, `danger`, `ghost`) there is
```
--interactive--bg -bg-hover -bg-active -bg-disabled
--interactive--fg -fg-hover -fg-disabled
--interactive--border -border-hover -border-disabled
```
Figma renders the disabled state as the default fill at 40% opacity; that
opacity is available as `--control-opacity-disabled`, and the `-disabled`
tokens above give the equivalent opaque colour for cases where a translucent
button would show the page through it.
## Surfaces, not a global theme
The home page is dark by default (`#1d1d1b`), while the nav and footer also have
light variants, so the whole tier-2 palette is redefined per surface rather than
per theme. `:root` **is** the dark surface; any element can flip its own subtree:
```html
...