Tokens.

This implementation reference maps the Foundations standards into named values for Figma and code. Use the names instead of raw values so modes and applications stay aligned.

Pipeline
Figma → CSS → Tailwind
Modes
Light · Dark
Variables
90+ named
Install
@alix/tokens
0.1

One vocabulary

A token is a named decision. --primary is the name; #1b3644 is the value it happens to hold today. You type bg-primary, never bg-[#1b3644] — so the two navies below paint identically, but only one is a token. Name the role and a rebrand becomes one edit, dark mode follows for free, and a Figma variable maps straight to a class.

bg-primary
A token. The role is named; the value resolves from it and both modes follow.
bg-[#1b3644]
A hardcoded hex. Identical today, but frozen — no rebrand, no dark mode, no shared name.
0.2

The pipeline

One token, end to end. A Figma variable becomes a value in semantic.css, a bridge in theme.css turns that value into a utility, you type the utility in JSX, and the pixel is painted. Two files do the work: semantic.css holds the values; theme.css is the @theme inline bridge — the half that actually makes the utility exist. Ship both, or components render unstyled.

One token, end to endFigma → CSS → Tailwind → pixel
01 · Figma variable
primary
Axis · Style guide

A named decision by design.

02 · Value
--primary: #1b3644
semantic.css

The role's value, per mode.

03 · Theme bridge
--color-primary: var(--primary)
theme.css · @theme inline

This is what makes the utility exist.

04 · Utility
bg-primary
your JSX

What you actually type.

05 · Pixel
painted navy
rendered

One value, on screen.

Follow the red line: primary is decided once in Figma and never re-typed as a hex again. semantic.css holds the value; theme.css bridges it to a class.
0.3

Light & dark

A token is one definition with two values. The .dark class remaps the variables underneath; the spelling never changes — bg-background is bg-background in both modes. So you never branch on theme in markup: the token layer owns the switch. For a dark panel inside a light page, reach for the Surface component (bgColorVariants) — it remaps the foreground tokens too, so text stays legible.

One definition · two values
--borderLight · Dark
The same spelling, both modes

--border resolves to #e5e5e5 in light and #262626 in dark. You still type border-border — the value moves, the name holds.

0.4

Install

Three steps, once per project. Map the @alix registry in components.json, install the tokens item, then import it after Tailwind. Only then add components — the theme utilities they use don't exist until the bridge is present. Alix Sans loads separately, via @font-face at weight 400 (the house weight); it is not a token.

1Map the registry in components.json
components.json
{
  "registries": {
    "@alix": "https://your-registry.example.com/r/{name}.json"
  }
}
2Install the tokens (one time, before any component)
Terminal
npx shadcn@latest add @alix/tokens
3Import the bundle after Tailwind, in your global stylesheet
app/globals.css
@import "tailwindcss";
@import "./tokens/index.css";
4Now add components — dependencies resolve automatically
Terminal
npx shadcn@latest add @alix/button @alix/textAndMediaBlock
0.5

Colour

The complete colour surface, by group. Every row: the CSS variable, the Tailwind utility you type, the resolved light and dark values, and its role. Reach for the role — background, muted-foreground, chart-1 — never a raw hex.

Semantic — surfaces, text, lines, states
CSS variableTailwindLightDarkRole
--backgroundbg-background#ffffff#0a0a0aPage surface
--foregroundbg-foreground#0a0a0a#fafafaBody text
--cardbg-card#ffffff#171717Raised surface
--mutedbg-muted#f5f5f5#262626Subtle fill
--muted-foregroundbg-muted-foreground#525252#a3a3a3Secondary text
--borderbg-border#e5e5e5#262626Hairlines
--accentbg-accent#f5f5f5#404040Hover / active fill
--ringbg-ring#a3a3a3#737373Focus ring
--card-foregroundbg-card-foreground#0a0a0a#fafafaText on card
--accent-foregroundbg-accent-foreground#171717#fafafaText on accent fill
--popoverbg-popover#ffffff#262626Overlay surface (menus, tooltips)
--popover-foregroundbg-popover-foreground#0a0a0a#fafafaText on popover
--inputbg-input#e5e5e5#262626Input border / control hairline
Brand — navy, gold, sand
CSS variableTailwindLightDarkRole
--primarybg-primary#1b3644#1b3644Brand navy — anchor & the one dark surface
--primary-foregroundbg-primary-foreground#fafafa#fafafaOn navy
--secondarybg-secondary#eaa23a#eaa23aGold — the single accent, never a surface
--secondary-foregroundbg-secondary-foreground#171717#171717On gold
--sandbg-sand#c6b298#c6b298Warm neutral ground
Status — the functional trio + foregrounds
CSS variableTailwindLightDarkRole
--destructivebg-destructive#dc2626#f87171Errors / destructive
--successbg-success#16a34a#22c55eSuccess
--warningbg-warning#d97706#f59e0bWarning
--destructive-foregroundbg-destructive-foreground#ffffff#0a0a0aText on destructive
--success-foregroundbg-success-foreground#0a0a0a#0a0a0aText on success
--warning-foregroundbg-warning-foreground#0a0a0a#0a0a0aText on warning
Charts — the five-step data ramp
CSS variableTailwindLightDarkRole
--chart-1bg-chart-1#1b3644#7da6bbSeries 1 — navy, the protagonist
--chart-2bg-chart-2#3f6b7d#eaa23aSeries 2 — steel (a lighter rung of the navy ramp)
--chart-3bg-chart-3#c6b298#c6b298Series 3 — sand
--chart-4bg-chart-4#eaa23a#5f8294Series 4 — gold, the single accent
--chart-5bg-chart-5#9aa7b1#c2ccd3Series 5 — cool grey, a quiet 5th
Sidebar — its own surface/foreground/accent set
CSS variableTailwindLightDarkRole
--sidebarbg-sidebar#fafafa#171717Sidebar surface
--sidebar-foregroundbg-sidebar-foreground#0a0a0a#fafafaSidebar text
--sidebar-primarybg-sidebar-primary#171717#1d4ed8Active / selected item
--sidebar-primary-foregroundbg-sidebar-primary-foreground#fafafa#fafafaText on active item
--sidebar-accentbg-sidebar-accent#f5f5f5#262626Hover fill
--sidebar-accent-foregroundbg-sidebar-accent-foreground#171717#fafafaText on hover fill
--sidebar-borderbg-sidebar-border#e5e5e5#262626Sidebar hairline
--sidebar-ringbg-sidebar-ring#a3a3a3#525252Sidebar focus ring
0.6

Interaction states

Hover, active, and subtle fills are derived with color-mix — a percentage of the base colour blended over the surface, so a control's states read as designed on any ground instead of bleeding through an alpha. Because color-mix resolves at use time, each recomputes correctly under .dark.

color-mix derived states
CSS variableTailwindRecipeRole
--primary-hoverbg-primary-hover80% primary over backgroundPrimary button — hover
--primary-activebg-primary-active60% primary over backgroundPrimary button — pressed
--secondary-hoverbg-secondary-hover80% secondary over backgroundSecondary (gold) — hover
--secondary-activebg-secondary-active60% secondary over backgroundSecondary (gold) — pressed
--input-hoverbg-input-hover50% input over backgroundInput hairline — hover
--input-activebg-input-active30% input over backgroundInput hairline — active
--destructive-subtlebg-destructive-subtle10% destructive over backgroundDestructive tinted surface — rest
--destructive-subtle-hoverbg-destructive-subtle-hover20% destructive over backgroundDestructive tinted surface — hover
--destructive-subtle-activebg-destructive-subtle-active30% destructive over backgroundDestructive tinted surface — pressed
0.7

Gradients & focus

Two gradient grounds and the focus treatment. The gradients are custom @utility classes, not bg-* colours. The focus ring ships as one @utility so every control opts in with a single class — outline reset plus the visible ring, tokenised.

Gradients — @utility grounds
CSS variableTailwindRole
--grey-gradientbg-grey-gradientCool light surface — an alternative ground to white
--gold-gradientbg-gold-gradientDeep gold surface (dark-surface text remap applies)
Focus ring · @utility

.default-focus-ring

focus:outline-none focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-ring/50

0.8

Type

Alix Sans at weight 400 throughout — hierarchy comes from size and colour, never weight. Display headings are fixed utilities; the -r variants step down on mobile and back up at md (768px). The text scale is the reading scale. Two font stacks back it all.

Display headings (app/globals.css)
UtilitySizeLeadingTrackingSpecimen
heading-xl60px72px0Axis
heading-lg48px58px-0.4pxAxis
heading-md36px43px-0.2pxAxis
heading-sm24px32px0Axis
Responsive headings — mobile → md (768px)
UtilityMobile≥ mdSpecimen
heading-xl-r36/42px60/72pxAxis
heading-lg-r30/38px48/58pxAxis
heading-md-r24/32px36/43pxAxis
Text scale — the reading scale
UtilitySizeLeadingSpecimen
text-xl20px28pxThe quick brown fox
text-lg18px24pxThe quick brown fox
text-base16px24pxThe quick brown fox
text-sm14px20pxThe quick brown fox
text-xs12px16pxThe quick brown fox
Font stacks (@theme inline)
CSS variableTailwindStack
--font-sansfont-sans"Alix Sans", ui-sans-serif, system-ui, sans-serifThe product sans, loaded via @font-face (Alix Sans, weight 400/700).
--font-monofont-mono"Geist Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospaceData/spec/code — falls back to the platform mono so builds fetch no web font.
0.9

Space

One gap vocabulary drives Stack, Inline, and Grid — each named step maps to a Tailwind gap-* class you type. Page layout is deliberately responsive utility classes, not variables (the gutter and rhythm grow with the viewport at the breakpoints below). The container scale backs both content width and block min-height.

Gap scale — Stack / Inline / Grid
StepClass you typeValueSpecimen
nonegap-00px
xxsgap-14px
xsgap-28px
smgap-312px
mdgap-416px
lggap-624px
xlgap-1040px
Page layout — responsive reality (utilities, not variables)
NameClassesResolvedNote
Page gutterpx-6 md:px-10 lg:px-1624 · 40 · 64pxHorizontal padding supplied by SiteContainer. Apply it to editorial content, not full-bleed sections.
Section rhythmpy-20 md:py-2880 · 112pxVertical padding set on each editorial section.
Tile insetp-1040pxInternal padding supplied by Surface-based tiles. Other blocks own their internal spacing.
Canvas max-widthmx-auto w-full max-w-page1728pxApply once to the page root. The page fills the viewport up to 1728px, then centres.
Container scale — content width & block min-height
Tokenrempx
--container-sm24rem384px
--container-md28rem448px
--container-lg32rem512px
--container-xl36rem576px
--container-2xl42rem672px
Breakpoints — Tailwind's mobile-first min-widths
CSS variablerempxPrefixRole
--breakpoint-sm40rem640pxsm:Use only for small-control adjustments. Page layout starts changing at md.
--breakpoint-md48rem768pxmd:Primary page breakpoint. Grids move to two columns; display type, page gutter and section padding step up.
--breakpoint-lg64rem1024pxlg:Secondary page breakpoint. Card rows reach their full column count and the page gutter reaches 64px.
--breakpoint-xl80rem1280pxxl:Available from Tailwind. Used only by a small number of components.
--breakpoint-2xl96rem1536px2xl:Available from Tailwind. No current page layout depends on it.
0.10

Figma layout names

These names live in the Figma Style Guide as designer vocabulary — but they do NOT ship as CSS variables or utilities. The layout behaviour is expressed as the responsive classes in 0.9 instead. Documented here so the shared language is honest, without pretending a flat value ships.

Designer vocabulary — not shipped CSS. The behaviour lives in code as responsive utilities.
Figma-only layout variables (inCss: false)
Figma nameFigma pxWhere the behaviour actually lives
container-padding-x24pxFigma stores a single 24px gutter. Code uses responsive px-6 md:px-10 lg:px-16 through SiteContainer.
section-padding-y96pxFigma stores 96px. Code uses py-20 md:py-28 for editorial sections; full-bleed blocks own their internal spacing.
section-title-gap-sm16pxSectionTitle uses gap-2 at base and gap-3 from lg for size sm.
section-title-gap-md20pxSectionTitle uses gap-3 at base and gap-4 from lg for size md.
section-title-gap-lg20pxSectionTitle uses gap-4 at base and gap-5 from lg for size lg.
section-title-gap-xl24pxSectionTitle uses gap-5 at base and gap-6 from lg for size xl.
0.11

Shape & elevation

The radius scale derives from a single --radius: 0px knob; every step clamps to a sharp 0 corner (only xl lifts to 4px, and rounded-full is reserved for where round carries meaning). Elevation is the exception too — six shadow steps, most surfaces sit on a hairline instead. See the Shape and Elevation chapters for the full argument.

Radius — derived from one knob, clamps to 0
TokenTailwindDerivationpxNote
--radiusbase knob = 0px0pxThe single source; every step derives from it.
--radius-smrounded-smcalc(--radius − 4px) = −4px → clamps to 00pxSharp corner.
--radius-mdrounded-mdcalc(--radius − 2px) = −2px → clamps to 00pxSharp corner.
--radius-lgrounded-lgvar(--radius) = 00pxSharp corner.
--radius-xlrounded-xlcalc(--radius + 4px) = 4px4pxReserved headroom, not a brand surface.
--rounded-fullrounded-fullTailwind built-in (9999px)9999pxThe only active curve, used where round = meaning.
Shadow ramp
TailwindValue
shadow-xs0 1px 2px rgba(0,0,0,.05)
shadow-sm0 1px 3px rgba(0,0,0,.1)
shadow-md0 4px 6px -1px rgba(0,0,0,.1)
shadow-lg0 10px 15px -3px rgba(0,0,0,.1)
shadow-xl0 20px 25px -5px rgba(0,0,0,.1)
shadow-2xl0 25px 50px -12px rgba(0,0,0,.25)
0.12

Misuse

The token layer only pays off if you go through it. Type the role, stay on the scale, and never let markup decide the theme.

bg-[#1b3644]
Don't hardcode a hex — type the semantic token.
unreadable
Don't paint a full dark surface with bg-primary alone — use Surface, which remaps the foreground too.
gap-[13px]
Don't invent an off-scale value — reach for a step.
className={dark ? "bg-black" : "bg-white"}
Don't branch on .dark in markup — the token layer owns the switch.