Skip to content

Automatic contrast ​

--jib-auto-contrast() takes a background color and a WCAG level and returns a text color whose contrast ratio against that background equals the level exactly. The shade is not searched for, it is solved: WCAG's ratio constrains one quantity, relative luminance, and luminance is linear in linear-light RGB, so the target inverts algebraically.

text-contrast-* paints exactly one thing, color, on the element carrying its own background. The function has no such limit, and that is the reason to reach for it.

Chromium only

CSS @function ships in Chromium and nowhere else yet, so every example here is blank in Firefox and Safari.

The class form is stable and works in every engine, verified exact against all 242 Tailwind colors at all three levels. If you need accessible text color rather than accessible border or stroke color, use the stable utility.

Quick reference ​

CallReturns
--jib-auto-contrast(<color>)a shade at exactly WCAG AA, 4.5:1, against that color
--jib-auto-contrast(<color>, aaa)a shade at exactly WCAG AAA, 7:1
--jib-auto-contrast(<color>, aa-lg)a shade at exactly WCAG AA Large, 3:1
--jib-auto-contrast(<color>, <level>, <space>)the same, with chroma shaped by the named space

The level is a bare identifier, not a string. aaa maps to 7, aa-lg to 3, and anything else to 4.5. The space is optional and defaults to oklch.

What the utility cannot reach ​

A border and its text, from one token ​

The element's background, its border color and its text color all derive from a single value. Change the token and all three follow, each still at its ratio.

solved for 7:1
Border and text
html
<div
  class="bg-teal-500 border-4
         [border-color:--jib-auto-contrast(var(--color-teal-500),aaa)]
         [color:--jib-auto-contrast(var(--color-teal-500),aaa)]"
>
  Border and text
</div>

An SVG stroke ​

A stroke is not text, so no amount of text-contrast-* reaches it.

html
<svg viewBox="0 0 48 48">
  <rect class="fill-teal-500" />
  <path class="stroke-[--jib-auto-contrast(var(--color-teal-500),aaa)]" />
</svg>

A gradient ending on the solved shade ​

The far stop of this bar is the accessible pair of the near stop, which makes the derivation visible: the two ends of the ramp are exactly 7:1 apart.

teal-500shade(aaa)
html
<div
  class="bg-[linear-gradient(90deg,var(--color-teal-500),
             --jib-auto-contrast(var(--color-teal-500),aaa))]"
></div>

Nothing is written inside the bar. The shade is the far stop, so text painted in it would sit at 1:1 against its own background at the right-hand end.

A focus ring that stays legible ​

html
<button
  class="bg-teal-500 outline-offset-2
         [color:--jib-auto-contrast(var(--color-teal-500),aaa)]
         focus:[outline:3px_solid_--jib-auto-contrast(var(--color-teal-500),aa)]"
>
  focus me
</button>

The three levels ​

aa-lg · 3:1
aa · 4.5:1
aaa · 7:1

A level is only reachable if the base can carry it

The solve is exact, but it is bounded by white and black. Ask for a ratio a color cannot reach and the result clamps to whichever end is further away and returns the maximum available instead, which is a lower ratio than you asked for. It does not error.

teal-500 carries AAA: measured in Chromium it solves to 7.151:1. teal-600 does not. Its solve clamps to black and delivers 5.724:1, which is the ceiling for that color, not a 7:1 pair. Mid-to-dark saturated colors are where this bites.

If a ratio matters, measure the pair rather than trusting the request. --jib-wcag-rating() reports what a pair actually achieved, and puts the answer on the page.

Color spaces ​

The space argument shapes the chroma of the returned shade. It does not affect correctness: the achieved ratio is owned by a shared final stage that every path runs through.

SpaceRecognised as
oklch, oklabthe Oklch path, and the default
lch, labthe Lch path
hslthe HSL path
hwbthe HWB path
anything elsethe shared core path
oklch
lch
hsl
hwb

The override ​

This module re-implements text-contrast-* on top of this function wherever @function is supported, keeping the class name and its output. See the overrides.

Import ​

css
@import 'tw-jib-css';
@import 'tw-jib-css-experimental/functions';

Baseline status

What these utilities are built on, live from the Web Platform Dashboard.