Skip to content

Color

Every colour class follows the same shape. Read this for the groups and rungs behind backgrounds, foregrounds, borders and dividers.

View source View as Markdown

Every colour class follows the same shape, <prefix>-<group>-<rung>[-<state>]. The group is either surface, which covers page and container chrome, or one of the intents: primary, secondary, accent, danger, success, warning, neutral and disabled.

Backgrounds

Backgrounds are bg-<group>-<rung>[-<state>] on the intent groups, and bg-surface-<tier> on the grounds.

bg-surface-low

bg-surface-base

bg-surface-high

bg-surface-higher

bg-surface-dimmer

index.html
<div class="bg-surface-low"></div>
<div class="bg-surface-base"></div>
<div class="bg-surface-high"></div>
<div class="bg-surface-higher"></div>
<div class="bg-surface-dimmer"></div>

Every rung is drawn twice from the one authored ramp: light on the left, dark on the right. Nothing here is a second palette.

Classlightdark

primary

  • bg-primary-solid
    Aa
    Aa
  • bg-primary-muted
    Aa
    Aa
  • bg-primary-subtle
    Aa
    Aa

accent

  • bg-accent-solid
    Aa
    Aa
  • bg-accent-muted
    Aa
    Aa
  • bg-accent-subtle
    Aa
    Aa

danger

  • bg-danger-solid
    Aa
    Aa
  • bg-danger-muted
    Aa
    Aa
  • bg-danger-subtle
    Aa
    Aa

success

  • bg-success-solid
    Aa
    Aa
  • bg-success-muted
    Aa
    Aa
  • bg-success-subtle
    Aa
    Aa

warning

  • bg-warning-solid
    Aa
    Aa
  • bg-warning-muted
    Aa
    Aa
  • bg-warning-subtle
    Aa
    Aa
index.html
<button class="bg-primary-solid hover:bg-primary-solid-hover active:bg-primary-solid-active">
  Solid
</button>
<button class="bg-primary-muted hover:bg-primary-muted-hover">Muted</button>
<button class="bg-primary-subtle hover:bg-primary-subtle-hover">Subtle</button>
RungFill
solidOpaque.
mutedThe stronger overlay.
subtleThe lighter overlay.
lightThe group’s light ground.
invertedThe group’s inverted ground.

Of those, solid, muted and subtle each come with a -hover and an -active step.

Foregrounds

Prose ink is text-surface[-<rung>], intent ink is text-<group>-<rung>, and ink that sits on a filled ground is text-<group>-on-<rung>.

The quick brown fox jumps over the lazy dog. text-surface

The quick brown fox jumps over the lazy dog. text-surface-muted

The quick brown fox jumps over the lazy dog. text-surface-subtle

The quick brown fox jumps over the lazy dog. text-accent-solid

The quick brown fox jumps over the lazy dog. text-danger-solid

The quick brown fox jumps over the lazy dog. text-success-solid

The quick brown fox jumps over the lazy dog. text-warning-solid

index.html
<p class="text-surface">Primary prose ink</p>
<p class="text-surface-muted">Muted prose ink</p>
<p class="text-surface-subtle">Subtle prose ink</p>
<span class="bg-danger-solid text-danger-on-solid">Ink on a fill</span>

The pairing between a fill and its ink is resolved in the token layer, so as long as you pair an on-* rung with the matching background, both survive every theme and both colour modes.

<span class="bg-primary-solid text-primary-on-solid">Save</span>
<span class="bg-primary-muted text-primary-on-muted">Save</span>
<span class="bg-primary-light text-primary-on-light">Save</span>

Borders and dividers

Borders are border-<group>-<rung>, with the per-side variants border-t-*, border-b-*, border-r-* and border-l-*, plus divide-* for children.

<div class="border border-surface rounded-surface-sm">
  <ul class="divide-y divide-surface">
    <li>One</li>
    <li>Two</li>
  </ul>
</div>

Border colour is the one family where the surface group carries no rung at all: border-surface is the whole of it, and border-surface-base matches nothing. The intents stop at subtle, as in border-danger-subtle. If you need an edge to be opaque, put a background behind a hairline instead of looking for an opaque border colour.

Limitations

Generated colour utilities have no slash modifier, so bg-accent-solid/40 matches no candidate and Tailwind emits no rule at all.

We dropped the modifiers because they were inconsistent and because they compounded. They only ever worked on bg-* and text-* while border-* and divide-* refused the same syntax, and on a rung that is already translucent they stacked, so bg-primary-subtle/40 came out as 40 % of a fill that is mostly transparent to begin with.

If you want something lighter, reach for a lower rung. Opacity that is part of the design belongs in the token tree, where it has a name and flips with the colour mode.

<div class="bg-accent-solid">Full</div>
<div class="bg-accent-muted">Stronger overlay</div>
<div class="bg-accent-subtle">Lighter overlay</div>

The generated families take over the bg-, text-, border- and divide- prefixes but leave Tailwind’s arbitrary-value syntax alone, so a genuine one-off still compiles in brackets.

<div class="bg-[color-mix(in_oklab,var(--mirror-bg-accent-solid)_40%,transparent)]"></div>

Tailwind’s own colour scale is turned off, since --color-*: initial is set in the generated theme block and bg-red-500 therefore doesn’t exist. Every colour a theme can move is already in the families above.

Token values flip with the colour mode on their own, so bg-surface-base is already correct in both and writing dark: is rarely necessary. Reach for the variant only when a mode needs different tokens, which is covered in Theming.