Foundations · Color

Accent colors

Categorical hues for surfaces where colour helps group, label, or identify content — but doesn't communicate state. Eight brand hues plus natural, each with a three-step background ladder.

Documentedby Derek Fidler

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

background.subtler
background.subtle
text.primarytext.accent
background.subtlest

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

background.subtler
background.subtle
text.primarytext.accent
background.subtlest
Do

Start with subtlest as the canvas. Step in to subtler for a card. Reach subtle only for the inner highlight.

Green

background.subtler
background.subtle (canvas)
Don't

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 as subtle. 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.blurple as 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 subtler or subtle burns 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.green communicates “this thing is green-coded”, not “this thing succeeded”. Status uses the success role.
  • Don't reach for primitives. color/blurple/200 skips the system. Components consume accent.blurple.subtler so 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.

Page history

1 revision
  1. Documented
    Derek Fidler@derekfidler

    First documented version. Anatomy, layering rule (subtlest first), the eight brand accents, accents-vs-status disambiguation, and the yellow contrast caveat.