Pair surface and foreground

Text on bg-primary takes text-primary-foreground.

From shadcn (Vercel plugin), Theming. Light values are shadcn’s default neutral.

Demo theme

Re-theme the primary

Drag the lightness, then switch the theme to Light.

text-foregroundBad

Invoice INV-2041

Due Oct 12 · $4,200.00

–--color-foreground
primary-foregroundGood

Invoice INV-2041

Due Oct 12 · $4,200.00

–--color-primary-foreground
paired foreground text-foreground
/* globals.css */
@theme inline {
  --color-primary: oklch(0.488 0.243 264.376);
  --color-primary-foreground: oklch(0.985 0 0);
}

/* lighter brand */
@theme inline {
  --color-primary: oklch(0.9 0.05 264.376);
  --color-primary-foreground: oklch(0.205 0 0);
}

<button className="bg-primary text-primary-foreground">Send invoice</button>

Any surface, any text

Pick a cell. Outlined cells are the pairs.

Contrast ratio of every surface token against every text token

Q3 retainer

Approved by finance. Payment runs Friday.

bg-card text-card-foreground

–

AA bodyAA large

bg-background  text-foreground
bg-background  text-muted-foreground
bg-card        text-card-foreground
bg-primary     text-primary-foreground
bg-secondary   text-secondary-foreground
bg-muted       text-muted-foreground
bg-accent      text-accent-foreground

Why it matters, pros and cons

Why it matters

Change a surface and its pair changes too; borrowed text passes by luck.

Pros

  • Contrast is decided once, in the theme.
  • Rebrands and dark mode stay legible.
  • Mismatches are visible in the class names.

Cons

  • A pair states intent; the theme can still fail.
  • Text over images needs a scrim or new surface.
  • Status colors need their own pairs too.