# Color

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

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.

::component-preview{name="ColorSurfacePreview"}
```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>
```
::

::component-preview{name="ColorIntentPreview"}
```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>
```
::

::docs-table
---
columns:
  - label: Rung
  - label: Fill
rows:
  - items:
      - label: solid
      - label: Opaque.
        plaintext: true
  - items:
      - label: muted
      - label: The stronger overlay.
        plaintext: true
  - items:
      - label: subtle
      - label: The lighter overlay.
        plaintext: true
  - items:
      - label: light
      - label: The group’s light ground.
        plaintext: true
  - items:
      - label: inverted
      - label: The group’s inverted ground.
        plaintext: true
---
::

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>`.

::component-preview{name="ColorForegroundPreview"}
```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.

```html
<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.

```html
<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.

::warning
The failure is silent: no build error, no console warning, and the element
keeps whatever background it already had. If a colour looks like it did not
apply, check for a slash first.
::

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.

```html
<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.

```html
<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](/tokens/theming).
