color-mix & opacity modifiers

[check: …]
📖 Pair this live Tailwind demo with the companion guide (.md) — this page is the rendered ground truth. ↗ Builds on tailwind: oklch colors (OKLCH is the color space that makes color-mix perceptually correct).

rgba() → color-mix(): the v4 opacity revolution

In v3, an opacity modifier like bg-cyan-500/40 compiled to rgba(34, 211, 238, 0.4) — a hardcoded 4-tuple. v4 replaces this with native CSS color-mix(): color-mix(in oklch, var(--color-cyan-500) 40%, transparent). The color stays a CSS variable (so themes can swap it live), and OKLCH blending means the fade-to-transparent path is perceptually linear — no muddy mid-tones.

/* v3 — hardcoded rgba, no var(), sRGB fade (muddy mid-tones) */ .bg-cyan-500\/40 { background-color: rgba(34, 211, 238, 0.4); } /* v4 — color-mix over the theme variable, OKLCH perceptual fade */ .bg-cyan-500\/40 { background-color: color-mix(in oklch, var(--color-cyan-500) 40%, transparent); }
aspectv3 rgba()v4 color-mix()
compiled output rgba(34,211,238,0.4) color-mix(in oklch, var(--color-cyan-500) 40%, transparent)
color space sRGB (gamma-encoded) OKLCH (perceptually uniform — fade looks even to the eye)
theme-swap friendly ❌ opacity baked into a literal rgba — must regenerate per theme ✅ reads var(--color-*) — swap the var, every /40 follows
also works for bg only (text/border had separate / impls) any color utility: text-*, border-*, ring-*, divide-*, from-*
browser floor every browser ever color-mix(): Chrome 111+, Safari 16.2+, Firefox 113+ (all 2023+)

1 · opacity playground — pick a color, drag opacity, see color-mix()

Pick a Tailwind v4 color token and drag the opacity slider. The preview swatch applies the actual color-mix(in oklch, …) via inline style (so it's instant), and the readout shows the equivalent Tailwind utility, the compiled CSS, and what your browser resolves the computed value to.

40%
class: bg-cyan-500/40
background-color: color-mix(in oklch, var(--color-cyan-500) 40%, transparent);
computed: —

2 · layered transparency — where color-mix() actually shines

Each block below is a Tailwind opacity utility. Where they overlap, the browser composites them — the same math color-mix() uses internally. OKLCH keeps the overlaps perceptually even (no brown mud where red + green meet, which sRGB rgba blending produces).

Try it in v3 mental model: rgba(239,68,68,0.5) over rgba(34,197,94,0.5) over rgba(59,130,246,0.5) — the overlap desaturates toward gray. v4's OKLCH path stays vivid.

opacity on text & border too

The /opacity suffix works on every color utility — not just bg-*. One syntax, every property.

bg-cyan-500/20 · border-cyan-500/60 · text-cyan-900/80

three opacities, three properties, one syntax — all color-mix().

3 · arbitrary opacity — when /40 isn't granular enough

Tailwind ships /5 /10 /20 … /95 as named steps. Need exactly 33%? Use the arbitrary-value form: /[0.33] (number) or /[33%] (percentage). Both compile to the same color-mix.

intentutilitycompiled (abridged)
named opacity step bg-cyan-500/40 color-mix(in oklch, var(--color-cyan-500) 40%, transparent)
arbitrary decimal (0–1) bg-cyan-500/[0.33] color-mix(in oklch, var(--color-cyan-500) 33%, transparent)
arbitrary percentage bg-cyan-500/[33%] color-mix(in oklch, var(--color-cyan-500) 33%, transparent)
fully opaque (explicit) bg-cyan-500/100 color-mix(in oklch, var(--color-cyan-500) 100%, transparent)
fully transparent bg-cyan-500/0 color-mix(in oklch, var(--color-cyan-500) 0%, transparent)

4 · beyond opacity — color-mix() blends two colors

color-mix() isn't just for transparency. Drop the transparent and pass a second color — you get a true blend. Useful for gradient stops, hover tints, and generated palettes inside @utility or a <style type="text/tailwindcss"> block.

@utility tint-cyan { /* blend cyan-500 with blue-500 at 50% — a perceptually correct mid-stop */ background-color: color-mix(in oklch, var(--color-cyan-500), var(--color-blue-500) 50%); } /* or inline in custom CSS */ .gradient-stop { background: linear-gradient( in oklch, var(--color-cyan-500), color-mix(in oklch, var(--color-cyan-500), var(--color-blue-500) 50%), var(--color-blue-500) ); }

The in oklch keyword controls the interpolation color space. For gradients this matters a lot — see gradients_v4.

intent → pattern

intentpatternwhy
semi-transparent background bg-cyan-500/40 compiles to color-mix(in oklch, var(--color-cyan-500) 40%, transparent)
non-standard opacity bg-cyan-500/[0.33] arbitrary decimal — escapes the named /5 /10 /20 … scale
opacity on text or border text-red-500/70 · border-green-500/30 same /opacity suffix — works on any color utility
ring/divide/from opacity ring-cyan-500/50 · from-blue-500/30 every color-bearing utility accepts the modifier
blend two theme colors color-mix(in oklch, var(--color-a), var(--color-b) 50%) generates a perceptually correct mid-stop — great for gradients
dynamic opacity from JS style.backgroundColor = 'color-mix(in oklch, ' + oklch + ' ' + pct + '%, transparent)' use the OKLCH token value directly — no rgba recomputation