Colour
Every value below is bound to a variable — these are not pictures of the palette, they are the palette. Names are ramp + number: a number is a shared language, “Lightning Yellow” is not.
Neutral
Section titled “Neutral”One grey ramp.
| Token | Value | Its job | |
|---|---|---|---|
color/neutral/0 |
#ffffff |
page surface | |
color/neutral/100 |
#f2f2f2 |
wells | |
color/neutral/200 |
#dad9d9 |
borders | |
color/neutral/300 |
#b5b3b3 |
disabled only | |
color/neutral/400 |
#858080 |
subheading ink | |
color/neutral/500 |
#6c6666 |
derived | |
color/neutral/600 |
#544d4d |
muted text | |
color/neutral/700 |
#3b3333 |
derived | |
color/neutral/800 |
#231b1b |
inverse surface | |
color/neutral/900 |
#0b0202 |
default text | |
color/neutral/1000 |
#000000 |
masks, print |
Blue — accent 1
Section titled “Blue — accent 1”Persian Blue. 400 is the brand main.
| Token | Value | Its job | |
|---|---|---|---|
color/blue/100 |
#e9e9fb |
accent bg | |
color/blue/200 |
#d4d3f7 |
||
color/blue/300 |
#6a66e4 |
subtitle ink | |
color/blue/400 |
#2b25d9 |
BRAND MAIN | |
color/blue/500 |
#221dad |
||
color/blue/600 |
#110e56 |
||
color/blue/700 |
#0c0b41 |
table header |
Red — accent 2
Section titled “Red — accent 2”Alizarin Crimson. 400 is the logo fill.
| Token | Value | Its job | |
|---|---|---|---|
color/red/100 |
#fce9e8 |
accent bg | |
color/red/200 |
#f9d3d2 |
||
color/red/300 |
#ec6663 |
subtitle ink | |
color/red/400 |
#e42521 |
BRAND MAIN | |
color/red/500 |
#b61d1a |
||
color/red/600 |
#5b0e0d |
||
color/red/700 |
#440b09 |
table header |
Yellow — accent 3
Section titled “Yellow — accent 3”Lightning Yellow. Never a text colour.
| Token | Value | Its job | |
|---|---|---|---|
color/yellow/100 |
#fef9e9 |
accent bg | |
color/yellow/200 |
#fdf3d3 |
||
color/yellow/300 |
#fad567 |
too pale at 24px | |
color/yellow/400 |
#f9c426 |
BRAND MAIN | |
color/yellow/500 |
#c79c1e |
||
color/yellow/600 |
#634e0f |
warning text | |
color/yellow/700 |
#4a3a0b |
table header |
Green — status only
Section titled “Green — status only”Not an accent.
| Token | Value | Its job | |
|---|---|---|---|
color/green/100 |
#d4f2de |
success bg | |
color/green/400 |
#74af36 |
solid | |
color/green/600 |
#1a6b36 |
success text |
Red — status only
Section titled “Red — status only”Not an accent and not the brand red: color/red/400 is the brand, this is a failure. Three values, the ones the status/danger roles resolve to.
| Token | Value | Its job | |
|---|---|---|---|
color/red/100 |
#fce9e8 |
danger bg | |
color/status/danger |
#da4144 |
solid | |
color/red/600 |
#5b0e0d |
danger text |
One-offs
Section titled “One-offs”Values with no ramp home, kept because each has exactly one job.
| Token | Value | Its job | |
|---|---|---|---|
color/light-blue/100 |
#f2f2f7 |
THE card surface | |
color/status/danger |
#da4144 |
error state | |
color/gradient-stop/pink |
#f24288 |
gradient only | |
color/gradient-stop/orchid |
#ebb8fc |
gradient only | |
color/gradient-stop/magenta |
#d947d6 |
gradient only |
Semantic roles
Section titled “Semantic roles”Tier 2 — the roles a component binds to, never a ramp step. Every swatch is bound to its semantic variable and reads its alias live from the Primitives; the text roles are shown as ‘Aa’ on the ground they are read on.
| Role | Aliases | Its job | |
|---|---|---|---|
text/default |
color/neutral/900 |
body and headings on light surfaces | |
text/muted |
color/neutral/600 |
secondary text | |
text/subtle |
color/neutral/400 |
subheadings at 24 px and over; captions read text/caption | |
text/on-accent |
color/neutral/0 |
on a filled accent | |
text/on-inverse |
color/neutral/0 |
on surface/inverse | |
text/link |
color/blue/400 |
links — brand blue in every accent mode | |
text/disabled |
color/neutral/300 |
the one job neutral/300 has | |
text/error |
color/red/400 |
field errors | |
text/caption |
color/neutral/600 |
captions, axes and placeholders |
Surface
Section titled “Surface”| Role | Aliases | Its job | |
|---|---|---|---|
surface/page |
color/neutral/0 |
the ground | |
surface/card |
color/light-blue/100 |
#f2f2f7, not neutral/100 — two colours, two jobs | |
surface/sunken |
ground/sunken |
wells — neutral/100 on white, neutral/200 on surface/card | |
surface/inverse |
color/neutral/800 |
dark blocks — 800, not the text black | |
surface/slot |
color/neutral/0 |
the inversion rule: white on a card, card on the page | |
overlay/scrim |
color/alpha/neutral-800-50 |
behind a dialog, sheet or drawer |
Border
Section titled “Border”| Role | Aliases | Its job | |
|---|---|---|---|
border/default |
color/neutral/200 |
cards and inputs | |
border/strong |
color/neutral/400 |
dividers that must be seen — decorative ones are banned | |
border/focus |
color/blue/300 |
the ring, every mode | |
border/error |
color/red/400 |
the error border and ring |
Action
Section titled “Action”| Role | Aliases | Its job | |
|---|---|---|---|
action/primary/bg |
color/neutral/900 |
the black button | |
action/primary/bg-hover |
color/neutral/700 |
the black button’s hover | |
action/primary/fg |
color/neutral/0 |
the black button’s label | |
action/secondary/bg |
color/neutral/0 |
the outlined button | |
action/secondary/bg-hover |
color/neutral/100 |
its hover | |
action/secondary/fg |
color/neutral/900 |
its label | |
action/secondary/border |
color/neutral/200 |
its outline | |
action/disabled/bg |
color/neutral/100 |
disabled ground | |
action/disabled/fg |
color/neutral/300 |
disabled label |
Status
Section titled “Status”| Role | Aliases | Its job | |
|---|---|---|---|
status/success/bg |
color/green/100 |
badge ground | |
status/success/fg |
color/green/600 |
badge text | |
status/success/solid |
color/green/400 |
dots and fills | |
status/danger/bg |
color/red/100 |
badge ground | |
status/danger/fg |
color/red/600 |
badge text — red/600, not the solid | |
status/danger/solid |
color/status/danger |
the one real #da4144 | |
status/warning/bg |
color/yellow/100 |
badge ground | |
status/warning/fg |
color/yellow/600 |
badge text | |
status/warning/solid |
color/yellow/400 |
fills and icons, never text | |
status/info/bg |
color/blue/100 |
badge ground | |
status/info/fg |
color/blue/600 |
badge text | |
status/info/solid |
color/blue/400 |
the brand blue |
| Role | Aliases | Its job | |
|---|---|---|---|
tint/neutral/bg |
color/neutral/200 |
label ground | |
tint/neutral/fg |
color/neutral/600 |
label text | |
tint/blue/bg |
color/blue/200 |
label ground — the hue at 200, every mode | |
tint/blue/fg |
color/blue/300 |
label text — the hue at 300 | |
tint/red/bg |
color/red/200 |
label ground | |
tint/red/fg |
color/red/300 |
label text — 2.3:1 on its tint, below the floor (open) | |
tint/yellow/bg |
color/yellow/300 |
label ground — 300, the one tone tinted one step up | |
tint/yellow/fg |
color/yellow/600 |
label text — yellow/600 | |
tint/green/bg |
color/green/100 |
label ground — the success pair, its own role | |
tint/green/fg |
color/green/600 |
label text |
Contrast — 47 evidenced pairs
Section titled “Contrast — 47 evidenced pairs”Role-aware, not a cross-product: every row is a pair the system actually puts together, checked at the floor its role gets — 4.5 for body text under 24px, 3.0 for large text, 3.0 for icons and rings. 41 pass · 4 exempt with a recorded reason · 2 below the floor when copied — both neutral/400 at 15px caption size, since moved to their own role, text/caption.
Exempt, with the reason recorded in the matrix: disabled text (WCAG-exempt); the red subtitle on a card at 2.83 (an accepted exception, 6% under on one colour in one role); .icon-chip--soft in yellow (dead CSS nothing renders); the yellow icon-list glyph (the glyph always sits beside its own label). Below the floor: neutral/400 as a 15px caption ink, 3.89 on the page and 3.49 on a card — the same token passes as a 26px subheading; one token, two roles, two floors — so captions moved to their own role, text/caption, and neutral/400 keeps the subheading job at 24 px and over.
One accent per surface, all three across a document
Section titled “One accent per surface, all three across a document”A surface takes its accent from the mode, and a component never chooses a hue — it binds a role and the mode resolves it. The four columns are one set of ten bindings, each column’s frame set to its mode; what differs between columns is the rule, not the component.
| Role | Its job |
|---|---|
accent/main |
the hue itself — solid fills, the primary of a coloured card |
accent/light |
the 300 step |
accent/lighter |
the 200 step — soft chips |
accent/bg |
the 100 step — tinted grounds |
accent/dark |
the table header bar — grey in multicolor |
accent/on-accent |
text and icons on main — ink on yellow (white on #f9c426 is 1.8:1) |
accent/heading |
the subtitle — the light step, or neutral/400 on yellow and multicolor |
accent/icon-ink |
the Icon list’s glyph, its only reader — the main hue on yellow, where the light step is too pale |
accent/text |
the accent as ink on a light surface — the Insight block; ink on yellow |
accent/pill-tint |
the Table’s row-header pill — grey in multicolor |
| Role | blue | red | yellow | multicolor |
|---|---|---|---|---|
accent/main |
blue/400 |
red/400 |
yellow/400 |
red/400 |
accent/light |
blue/300 |
red/300 |
yellow/300 |
red/300 |
accent/lighter |
blue/200 |
red/200 |
yellow/200 |
red/200 |
accent/bg |
blue/100 |
red/100 |
yellow/100 |
red/100 |
accent/dark |
blue/700 |
red/700 |
yellow/700 |
neutral/800 |
accent/on-accent |
neutral/0 |
neutral/0 |
neutral/900 |
neutral/0 |
accent/heading |
blue/300 |
red/300 |
neutral/400 |
neutral/400 |
accent/icon-ink |
blue/300 |
red/300 |
yellow/400 |
red/300 |
accent/text |
blue/400 |
red/400 |
neutral/900 |
red/400 |
accent/pill-tint |
blue/200 |
red/200 |
yellow/300 |
neutral/200 |
Two things never follow the accent: a link (text/link — the brand blue in every mode) and the focus ring (border/focus, blue/300), because a link and a ring have to stay recognisable on a red or yellow slide. Multicolor is solid red for any single-accent element; its charts cycle the three hues, its table goes grey. Yellow is never text at its main step — every text-shaped role flips to ink or grey there, which is why on-accent, heading and text exist per mode at all. A component that needs a colour the ten roles do not give has found a missing role, not a reason for a hex: add it to tokens/semantic.json accent.*, rebuild, then bind.
On the Figma page
Section titled “On the Figma page”The same page in the Figma library: Colour.
Pepper UI v1.5.0
The Mobupps design system. 386 tokens, 88 components, 65 pages in the workshop — every page of this site generated from the repository, so nothing here is typed twice.