What WCAG 2.2 actually asks of a focus ring
Five success criteria touch focus, and they are often misquoted. Only one, 2.4.13, sets a size and a contrast figure for the indicator itself, and it is Level AAA. At Level AA a ring has to be visible (2.4.7) and reach 3:1 against what is next to it (1.4.11).
| Criterion | Level | What it requires |
|---|---|---|
| 2.4.7 Focus Visible | AA | Some indicator is visible while a control has keyboard focus. No size or figure. |
| 1.4.11 Non-text Contrast | AA | The indicator reaches 3:1 against its adjacent colours: the background for a ring outside the control, the control for one inside. |
| 2.4.11 Focus Not Obscured (Minimum) | AA | The focused control is not entirely hidden by author content such as a sticky header. It tests the control, not the ring. |
| 2.4.12 Focus Not Obscured (Enhanced) | AAA | No part of the focused control is hidden. |
| 2.4.13 Focus Appearance | AAA | An area of the indicator as large as a 2 CSS px perimeter of the control changes by at least 3:1 between unfocused and focused. |
The figures many guides still give — “2.4.11 Focus Appearance (Minimum)”, a 1 pixel perimeter or a 4 pixel shortest side, 4.5:1 for AAA — come from the May 2021 working draft. Focus Appearance was moved to 2.4.13 and Level AAA in the candidate draft of 17 May 2023, and WCAG 2.2 was published that October without the older text.
Change contrast is not ring contrast
2.4.13 compares each pixel with itself: its colour before the control took focus against its colour after. WCAG defines the focus indicator as the pixels that change, whatever CSS changed them. A checker that compares the ring’s colour with the page background is measuring 1.4.11 instead, and the two disagree whenever the ring sits inside a control, replaces a border or is translucent. A black border that turns #555 on focus is 7.46:1 against white, yet the pixels change by only 2.82:1, so it fails 2.4.13.
The area is a perimeter 2 CSS pixels thick: 4w + 4h for a rectangle, 4w + 4h − (16 − 4π)r for rounded corners, 480 px² for a 90 × 30 button. Only pixels that change by 3:1 count towards it. W3C’s own Understanding document also accepts a 2px line just inside the edge, which falls 16 px² short of the formula on square corners; where a ring lands in that gap, the tool says so rather than choosing a reading for you.
Design systems’ rings, measured
Each system’s published ring on a 120 × 40 button with an 8px radius: a dark button on a white page, and a light button on #0f172a. The figure is the strongest change any ring pixel makes. Values and versions are listed under “Design systems’ rings” in the tool, which loads each one; computed by the tool when this page was built.
| System | On white | On #0f172a | Forced colours, as shipped |
|---|---|---|---|
| W3C C40 | AA pass · AAA pass · 13.38:1 | AA pass · AAA pass · 16.96:1 | Outline kept |
| Primer | AA pass · AAA on Fig. 4 · 3.44:1 | AA pass · AAA on Fig. 4 · 4.96:1 | Outline kept |
| Carbon | AA pass · AAA on Fig. 4 · 3.57:1 | AA pass · AAA on Fig. 4 · 4.78:1 | Outline kept |
| Material | AA pass · AAA pass · 6.45:1 | AA fail · AAA fail · 2.77:1 | Outline kept |
| Radix | AA fail · AAA fail · 2.42:1 | AA pass · AAA pass · 7.39:1 | Outline kept |
| Tailwind | AA pass · AAA pass · 3.76:1 | AA pass · AAA pass · 17.85:1 | Nothing drawn |
“AAA on Fig. 4” marks Primer’s and Carbon’s inset rings: they change slightly less than the formula’s area but more than the flush 2px line the Understanding document draws as passing.
outline, box-shadow and forced colours
Draw the ring with outline. It follows border-radius in every current browser (Chrome and Edge 94, Firefox 88, Safari 16.4), it is never clipped by its own box, and in forced colours mode — Windows contrast themes — the browser keeps it and recolours it. box-shadow computes to none there, so a shadow-only ring, including Tailwind’s ring utilities, leaves keyboard users with nothing. If you need a shadow for a second colour, keep an outline as well, or add outline: 2px solid transparent, which forced colours turns visible.
Use :focus-visible rather than :focus: it shows the ring for keyboard focus and for fields that take typing, and not on every mouse click. It has worked in Chrome since 86, Firefox since 85 and Safari since 15.4. Any parent with overflow hidden, auto or scroll cuts off a ring drawn outside the control, so give it padding at least as wide as the ring reaches, or draw the ring inside. For a colour pair that suits every solid background, pick two colours at 9:1 or more with each other; the contrast checker measures a pair, and the box shadow generator uses the same engine for edges and glass.