Principles
Three rules that decide whether to reach for an accent, and which token to start with.
Principle 01
Categorical, not semantic
Use accents where the colour could be swapped for another without changing meaning — calendars, project tags, a colour picker. If the colour is the meaning (success, danger, warning), reach for a status role instead.
Principle 02
Subtlest first
The default reach is background.accent.<hue>.subtlest. Only layer subtler or subtle on top when a section inside the canvas needs to differentiate itself. Three layers max.
Principle 03
Stay in the family
Accent backgrounds, text, icons, and borders must come from the same hue. A blurple card with green text reads as broken. The token names make this easy — `accent.blurple.*` everywhere or nothing.
Anatomy of an accent
Every accent ships five tokens, each pinned to a step on the hue ramp. Together they cover the canvas, the layered surfaces inside it, the headline that names the section, and the line that contains it.
Blurple
Blurple · token map
background.accent.blurple.subtlest
Outer canvas · blurple/100 → blurple/1000 dark
background.accent.blurple.subtler
Layer in · blurple/200 → blurple/900 dark
background.accent.blurple.subtle
Deepest layer · blurple/300 → blurple/800 dark
border.accent.blurple
Containment · blurple/300 → blurple/800 dark
text.accent.blurple
Headline / accent text · blurple/900 → blurple/200 dark
Icons inherit from text.accent.<hue> via currentColor— there's no separate icon accent token. Set the colour on the wrapper; the icon follows. (See Iconography › Color for the rule.)
The layering rule
Always start with subtlest.It's the canvas — a tinted page section, an empty-state surface, an accent-flavoured callout. Only reach for subtler when a child element needs to read as a step in from the canvas. Only reach for subtle when a third tier of nesting earns the depth.
Three layers, in order
subtlest is the canvas you stand on. subtler is a card placed on the canvas. subtleis something inside that card the eye must land on. If you're reaching for a fourth layer, the page doesn't need a fourth layer — it needs a different design.
Green
Start with subtlest as the canvas. Step in to subtler for a card. Reach subtle only for the inner highlight.
Green
Don't open on subtle. The deepest layer is for what comes last — using it as the canvas leaves nowhere to climb when a child needs emphasis.
The accent palettes
Eight brand hues plus natural. Each ships the full ladder — three backgrounds, a border, a headline text. Each ramp is a hue family in the underlying palette; the accent tokens just name the steps the system commits to.
Blurple
Blurple
Discovery and exploration. Default categorical pick when no other hue earns the slot.
Green
Green
Success / affirmative. As an accent — categorical only — never paint state with it.
Orange
Orange
Warm, energetic. The stand-in for yellow on accessibility-sensitive surfaces.
Pink
Pink
Editorial warmth. Used in marketing and humanising content.
Blue
Blue
Cool, technical. Reaches into product surfaces — never confused with discovery (use blurple) or info (use the info role).
Purple
Purple
Distinct from blurple — softer, less saturated. Used when the page already carries blurple.
Yellow
Yellow
Brand signature. Read the accessibility note before pairing yellow with text.
Red
Red
Alarmed, alert. As an accent — categorical only — never paint danger with it.
Natural
Natural
Warm cream. The quietest accent — used as a paper-feeling tint when no hue is right.
Pairing text, icons, and borders
Every accent surface inherits its foreground from the same hue family. A blurple background carries blurple text and a blurple border — never a green or charcoal mix. The token names enforce this; the rule is to follow them.
- Headline / accent text →
text.accent.<hue>. The dark hue (900 step). Pairs with subtlest, subtler, or subtle. Always meets WCAG AA against the matching background. - Body copy →
text.primary. The default neutral charcoal. Body running on top of a subtlest or subtler background reads as ordinary prose, just on a tint. - Icons →
currentColor. No separate icon accent tokens. Set the parent's text colour (text.accent.<hue> or text.primary) and the icon follows. - Borders →
border.accent.<hue>. Same step assubtle. Pair with a subtler background to clear the 3:1 contrast bar for non-text UI.
Accents vs. status colors
The hue is the same; the role is different. background.accent.green.subtlest and background.success.subtlest can resolve to the same hex — but they communicate two different things, and they shouldn't be substitutable in code.
Use an accent when
- The user picked the colour (calendar, label, project).
- The colour groups items without ranking them.
- The page-section background is editorial (e.g. a blog post hero).
- The colour could be swapped without changing what anything means.
Use a status role when
- The colour communicates state — success, warning, danger, info, discovery.
- A screenreader user without colour would lose meaning if it disappeared.
- The role token is named for the meaning, not the hue (success not green).
- Discovery uses blurple as a role — distinct from
accent.blurpleas a categorical pick.
Accessibility
Every text.accent.<hue> is calibrated to meet WCAG AA against the three accent backgrounds in its family — 4.5:1 for body, 3:1 for large text and non-text UI. The system pre-pairs these; you don't need to hand-check every combination so long as you stay inside one hue.
The yellow caveat
Yellow presents a unique accessibility challenge. To meet WCAG AA on a subtler or subtle yellow background, text.accent.yellow sits at the 900 step (`#554b00`) — which reads almost brown. Use accent.orange when the section needs a warm hue and the text has to be unmistakably the hue you started with.
Never carry meaning in colour alone. Pair every accent surface with a label, an icon, or a structural cue (a heading, a list group). A blurple card without a label is a decoration; with the label “Discovery”, it's a section.
In code
Reach for the accent CSS variables exposed by @flatpay-dk/ui/styles. The variables resolve to the right primitive in light or dark mode without the consumer thinking about it.
tsx
// Default — start with subtlest, layer subtler/subtle as needed
<section
className="rounded-xl p-6"
style={{ backgroundColor: "var(--color-bg-accent-blurple-subtlest)" }}
>
<h3
className="font-display text-2xl font-bold uppercase"
style={{ color: "var(--color-text-accent-blurple)" }}
>
Discovery
</h3>
<p className="mt-3 text-sm">
A note on what this section communicates.
</p>
{/* Step in for a child surface */}
<article
className="mt-4 rounded-md p-4"
style={{ backgroundColor: "var(--color-bg-accent-blurple-subtler)" }}
>
<p className="text-sm">A card that needs to read as nested.</p>
{/* The deepest layer — used sparingly */}
<span
className="ml-2 inline-block rounded px-2 py-0.5 text-xs"
style={{ backgroundColor: "var(--color-bg-accent-blurple-subtle)" }}
>
Highlight
</span>
</article>
</section>Keep families together in the source
When you reach for accent.blurple.subtlest, the next token in the same component should also be accent.blurple.*. Mixing accent families is the single fastest way to make a surface read as broken.
Don'ts
- Don't skip subtlest. Opening a section on
subtlerorsubtleburns the ladder before the child elements can climb it. - Don't mix accent families inside one component. Blurple background plus green border plus pink badge reads as a bug. Same family, every surface.
- Don't use accents for state.
accent.greencommunicates “this thing is green-coded”, not “this thing succeeded”. Status uses the success role. - Don't reach for primitives.
color/blurple/200skips the system. Components consumeaccent.blurple.subtlerso light/dark themes resolve the right step. - Don't pair accent text with charcoal accent backgrounds (and vice versa). The contrast pair is calibrated within one hue family. Crossing families breaks the pairing.
- Don't carry meaning in colour alone. Pair every accent surface with a heading, label, or icon. The colour is the dressing; the copy is the message.
Related
Page history
1 revision- DocumentedDerek Fidler@derekfidler
First documented version. Anatomy, layering rule (subtlest first), the eight brand accents, accents-vs-status disambiguation, and the yellow contrast caveat.