Foundations
Tokens
Every visual decision has a name. Screens use the name, never the value, so a change made once reaches every screen.
Foundations
Tokens
Every visual decision has a name. Screens use the name, never the value, so a change made once reaches every screen.
How names are built
Read a name left to right, from the broad to the specific. Each part answers one question. Skip a part when it has nothing to add.
color
Foundation
What kind of style
.
background
Property
What it paints: text, background, border, icon
.
selected
Role
What it means: neutral, brand, selected, danger
.
hovered
State
Interaction: hovered, pressed, focused, disabled
With emphasis
color.background.brand.bold.hovered
Emphasis (subtle, bold) sits after the role
Shortest
color.text
No modifier means the default
In code
--color-background-selected
Dots become hyphens, with a leading --
Every token
Grouped by family. The code column gives today's CSS variable in globals.css and its Tailwind utility. Rows marked New are added on this page.
Token
Value
Use
CSS variable · Tailwind
Color · text and surfaces
color.text
#101112
Names, values, titles
--foreground · text-foreground
color.text.subtle
#5E6066
Headers, labels, meta
--muted-foreground · text-muted-foreground
color.text.subtlest
#6E7178
Placeholders, footers
--muted-soft-foreground · text-muted-soft-foreground
elevation.surface
#FFFFFF
Canvas, tables, menus
--background · bg-background
elevation.surface.sunken
#FBFBFB
Sidebar, rails, form footers
--sidebar · bg-sidebar
color.background.neutral.hovered
#F5F6F7
Row, nav and ghost button hover
--muted · bg-muted
color.background.neutral.pressed
#F0F0F1
Current nav item, pressed
--sidebar-accent · bg-sidebar-accent
color.background.selected
#E6F2EF
Checked row, chosen option, text #004F46
--accent · bg-accent
color.tile
#ECEEF1
Icon tiles
--tile · bg-tile
color.blanket
rgb(0 0 0 / .10)
Behind dialogs, 4px blur
bg-black/10
Color · borders, action and status
color.border
#EEEFF1
Hairlines, table grid, dividers
--border · border-border
color.border.input
#8C8F97
Inputs, selects, textareas
--input · border-input
color.border.focused
#004F46
Focus ring, 2px with a 2px gap
--ring · ring-ring
color.background.brand.bold
#004F46
The one filled button per header
--primary · bg-primary
color.background.brand.bold.hovered
#003E37
Hover and press on the primary button
--primary-active · bg-primary-active
color.link
#004F46
Links, always underlined
--primary · text-primary underline
color.text.danger · background.danger
#B91C1C · #FCE9E9
Errors, delete, overdue
--destructive-soft-foreground
color.text.success · background.success
#2E6813 · #E8F5E0
Placed, done, paid
--success · --success-soft
color.text.warning · background.warning
#92400E · #FEF3C7
Needs review, draft
--warning · --warning-soft
color.text.information · background.information
#1D5FB4 · #E0F0FF
CV sent, client rounds
--info · --info-soft
color.text.discovery · background.discovery
#6B3FB0 · #F3ECFF
Offer, exclusive
--plum · --plum-soft
color.text.meeting · background.meeting
#056374 · #E0F1F5
Client meetings on the calendar
--teal · --teal-soft
Color · interaction, loading and charts
color.interaction.hovered
New
rgb(16 17 18 / .04)
Laid over a coloured fill on hover: selected rows, pills
--interaction-hovered · bg-interaction-hovered
color.interaction.pressed
New
rgb(16 17 18 / .08)
The same overlay while pressed
--interaction-pressed · bg-interaction-pressed
color.skeleton
New
#F0F0F1
Loading placeholders
--skeleton · bg-skeleton
color.skeleton.subtle
New
#F5F6F7
The pulse's lighter end, placeholders on sunken surfaces
--skeleton-subtle · bg-skeleton-subtle
color.chart.*
New
8 categorical, 7 sequential, 4 status. Values on Data visualisation
--chart-categorical-1 · fill-chart-categorical-1
Elevation · shadow
elevation.shadow.control
ring #1C2840 18%, 0 1px 3px 4%
Secondary and icon buttons
--shadow-control · shadow-control
elevation.shadow.raised
ring #EEEFF1, 0 1px 3px 6%
Kanban cards, cards on a fill
--shadow-raised · shadow-raised
elevation.shadow.overlay
three #1C2840 layers
Menus, popovers, dialogs, toasts, Ask bar
--shadow-overlay · shadow-overlay
elevation.shadow.primary
inset 6% plus green drops
The primary button only
--shadow-primary · shadow-primary
Space and radius
space.025 · 050 · 075 · 100
2 · 4 · 6 · 8px
Inside controls; layout gaps start at 8
--spacing · gap-0.5 · 1 · 1.5 · 2
space.150 · 200 · 300 · 400
12 · 16 · 24 · 32px
Between groups, sections, page padding
--spacing · gap-3 · 4 · 6 · 8
radius.small · medium
4 · 6px
Checkbox and kbd · pills and menu items
--radius-sm · md · rounded-sm · rounded-md
radius.large · xlarge · full
8 · 12px · round
Controls and menus · dialogs and cards · avatars
--radius-lg · xl · rounded-lg · xl · full
Type
font.family.body
Inter · 400 · 500 · 600
Every product surface
--font-sans · font-sans · font-medium
font.heading.large
24 / 28 / 600, −0.02em
Record name
text-heading-large
font.heading.small
16 / 20 / 600, −0.01em
Page, block and dialog titles
text-heading-small
font.body
14 / 20 / 500, −0.005em
Cells, buttons, menus. Prose at 400
text-body
font.body.small
12 / 16 / 500
Labels, pills, counts, meta
text-body-small
font.code
New
JetBrains Mono 12/16
IDs, API keys, the code input
--font-mono · font-mono text-body-small
font.metric
New
£148,500
24 / 28 / 600, tabular figures. Metric tiles, report totals
--text-metric · text-metric tabular-nums
Motion
motion.duration.*
120 · 160 · 200 · 250 · 260ms
fast, base, move, sheet, morph. Nothing over 260ms
--motion-fast … --motion-morph
motion.easing.*
out · in-out · drawer
Enter and exit · move on screen · panels
--ease-out · --ease-in-out · --ease-drawer
Border width and opacity
border.width
New
1px
Hairlines, dividers, input edges
--border-width · border
border.width.selected
New
2px
Active tab underline, chosen card
--border-width-selected · border-2
border.width.focused
New
2px
The focus ring, always with color.border.focused
--border-width-focused · ring-2
opacity.disabled
New
0.4
Disabled controls and avatars. One value everywhere
--opacity-disabled · opacity-40
opacity.loading
New
0.6
Content under a spinner while it reloads
--opacity-loading · opacity-60
Rules
1. Screens use semantic tokens only. Pick the token whose name fits the job, not the one whose value happens to match.
2. Never a raw hex, px or ms value in a screen. The lint rejects them; a look the tokens can't give goes back to the design system.
3. A new token needs a reason: a real screen that needs it, the role it fills and why no existing token fits. Record it on this page and in the Changelog.
4. Width and colour travel together: border.width.focused with color.border.focused, border.width.selected with the selected colour.
5. One disabled opacity, 0.4, and one loading opacity, 0.6. Nothing else fades a control.
Layout and scaffold
The layer between tokens and components. Tailwind with a spacing lint for primitives, and one app shell every page sits in.
Grid and border
Blocks in the main area sit on a 12-column grid. Space separates first; a 1px hairline groups when space isn't enough; 2px marks focus and selection.