Bhinish Dhiman
How to make a focus indicator that passes WCAG on any background
Draw the ring with outline on :focus-visible, at least 2px thick and offset from the control, in a colour that reaches 3:1 against every background it sits on. For any solid background, pair it with a second colour at 9:1 or more. Level AA needs it visible and at 3:1 against its neighbours; the 2px perimeter and 3:1 before-and-after change of 2.4.13 are Level AAA. Keep an outline, because box-shadow disappears in forced colours mode.
A focus ring is the only thing that tells a keyboard user where they are on a page. It is also the part of a design most often checked by eye, on one background, in one theme. A ring that looks crisp on white can measure 2.42:1 on it, and one that passes on white can vanish on a dark card.
Measure a ring with the tool
- Open the focus indicator checker. It starts on Two-colour, a dark ring inside a white halo, so the answer at the top already reads “Yes — it passes AA and AAA”.
- To check a ring you already ship, set Style, Colour, Thickness and Gap under Your ring to match your CSS, or open Design systems’ rings and load one.
- Read the answer at the top. It covers six controls on four backgrounds, with one line for AA and one for AAA giving the reason from the worst case; Show me draws that control on that background in the large preview, before and after focus.
- If it fails, press Fix it for me. It pairs your colour with a second one at 9:1 and thickens the ring if it still fails anywhere; Undo puts your ring back.
- Under Test it further, open Every control on every background to press Tab through the 24 controls and see each result and its arithmetic, Your backgrounds to set your own light, dark and brand colours, and Your page to add an
overflow: hiddenbox or a sticky header height. - Press Copy CSS or Tailwind in the bar at the bottom of the screen, or open All code formats for One class and Custom properties.
What each level actually asks
Focus Appearance is success criterion 2.4.13 of WCAG 2.2, and it is Level AAA. Many guides still describe a “2.4.11 Focus Appearance (Minimum)” at Level AA with a 1 pixel perimeter or a 4 pixel side. That text belongs to the May 2021 working draft; the criterion was renumbered and moved to AAA in May 2023, before WCAG 2.2 was published.
| Criterion | Level | The test |
|---|---|---|
| 2.4.7 Focus Visible | AA | An indicator is visible while a control has keyboard focus |
| 1.4.11 Non-text Contrast | AA | The indicator reaches 3:1 against its adjacent colours |
| 2.4.11 Focus Not Obscured (Minimum) | AA | The focused control is not entirely hidden, for example under a sticky header |
| 2.4.13 Focus Appearance | AAA | Pixels covering a 2px perimeter of the control change by at least 3:1 |
So a conforming AA ring needs no particular thickness. It does need contrast: a pale ring on a white page fails 1.4.11 even though it is plainly there.
The change is measured on the same pixels
Change contrast is the ratio between one pixel’s colour before the control is focused and its colour after. 2.4.13 is built on it, and it is not the same as the ring against the page. Take a button with a black border that turns #555 when focused: #555 is 7.46:1 against white, which looks safe, but the border pixels only move from black to #555, a change of 2.82:1, so they do not count at all.
The area those pixels must cover is a 2px perimeter: 4w + 4h for a rectangle, 4w + 4h − (16 − 4π)r with rounded corners. For a 120 × 40 button with an 8px radius that is 613 px². A 2px ring outside the button covers 650 px² and passes; the same ring drawn inside, flush with the edge, covers about 600 px². W3C’s own Understanding document draws that inside line as passing, although it is 16 px² short of the formula on square corners, so the tool reports it as passing on the figure and under the formula, rather than picking a side.
One colour cannot suit every background
A single blue ring has to contrast with every surface it lands on. A 3px #1d4ed8 ring changes white pixels by 6.70:1, but on #0f172a it changes them by 2.66:1, so on a dark card no pixel counts and AA fails as well.
The fix is two colours. WCAG technique C40 shows that if the two have 9:1 or more between them, one of them clears 3:1 against any solid colour. A dark ring inside a white halo does it, drawn with an outline for the ring and a box-shadow spread underneath for the halo. Over a photograph or a gradient there is no such guarantee, which is why the tool measures an image background separately, pixel by pixel.
Keep an outline for forced colours
Windows contrast themes switch the browser to forced colours mode, which recolours outlines and sets box-shadow to none. A ring drawn only with box-shadow, as Tailwind’s ring utilities draw it, disappears for exactly the people who chose high contrast. The exported CSS draws the ring with an outline, adds outline: 2px solid transparent where a shadow does the drawing, and carries a forced-colours rule that paints one solid ring across the full thickness.
Other ways to check
There is no built-in WCAG focus checker in browsers. Chrome’s DevTools can force an element into the :focus-visible state from the Styles pane, which lets you sample the ring’s colour with the picker, and the Rendering panel can emulate forced colours. Neither measures the area. Testing by hand means taking a screenshot before and after focus and comparing pixels, which is what the tool does for you on six controls at once.
Nothing is uploaded: the measurement runs in your browser on the preview it draws, and you can confirm it by watching the Network tab in your browser’s developer tools while you change the ring.
When the ring is right, the focus indicator checker hands you the CSS. For the colours themselves, the contrast checker finds the nearest passing shade without losing your hue.