Take any content page on our marketing site, for example pub-trivia.app/guides/how-to-host-a-pub-quiz, click once in the address bar, and then press Tab repeatedly. There are 35 focusable elements. The 36th press hands focus back to the browser, so there is no trap and no surprise ordering. The surprise is what the ring looks like, which turns out to be three different answers and one non-answer.
We walked the whole order through CDP, reading document.activeElement and its computed outline and box-shadow after each press.
Three treatments and a gap
| treatment | how the ring is drawn | count |
|---|---|---|
outline-style: auto | Chrome draws its own ring, we contribute nothing | 31 |
Tailwind ring on box-shadow | ours, 2px at rgb(204, 204, 204) | 3 |
| nothing at all | no outline, no box shadow | 1 |
The 31 are every plain anchor: the wordmark, the four header nav links, the two breadcrumb links, the four related-page cards, and all twenty footer links. We style none of them, so the browser's default applies, which on a dark background is Chrome's two-tone ring. That is a perfectly good indicator and it is not ours.
The three are the button-shaped links: "Sign in", "Get started" and the page's closing call to action. Their computed box shadow when focused is exactly this:
rgb(23, 23, 23) 0px 0px 0px 2px, rgb(204, 204, 204) 0px 0px 0px 4px That is focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 resolved: two pixels of the page background as an offset, then two pixels of ring. --ring is 0 0% 80%, so the ring lands on rgb(204, 204, 204) against a rgb(23, 23, 23) page, which is 11.16:1. WCAG 2.4.11 wants at least 2px and at least 3:1 against what is next to it. This clears both comfortably.
Incidentally, those three report border-radius: 1.67772e+07px, which is what rounded-full looks like once the browser has clamped it.
The one that draws nothing
The fifth thing you tab to is the only <button> in the public header: the "Resources" dropdown trigger. Focused, by keyboard, with :focus-visible matching, it computes to:
outline-style: none box-shadow: none --tw-ring-shadow: 0 0 #0000 Its class list in components/SiteHeader.tsx explains it in one word:
<DropdownMenuTrigger className="flex items-center gap-1 text-sm font-medium text-muted-foreground hover:text-foreground transition-colors outline-none"> outline-none removes the browser's ring, and nothing adds one back. The shadcn pattern is outline-none paired with focus-visible:ring-*, and here only the first half was written.
We proved it with pixels rather than with computed styles, because a computed style is a claim and a screenshot is evidence. Two captures of the same 420 by 60 region of the header at 2x, one with nothing focused and one with the trigger focused by keyboard:
e34271a40cbe3ea6ef7d5484be110ebd ring-none.png e34271a40cbe3ea6ef7d5484be110ebd ring-resources.png Byte identical. Focusing that control changes not one pixel. The same capture with the neighbouring "Pricing" link focused differs, as it should.
WCAG 2.4.7 Focus Visible is a Level A criterion, so one element is enough to fail it, and this is the worst one to lose: the trigger is what reveals five navigation destinations that are not in the HTML until it opens. A keyboard user tabbing across the header has no way to know they are standing on it.
Eighteen outline-nones, and only one is a problem
Grepping the components for outline-none gives 18 hits. Twelve pair it with a ring or a focus-visible:outline, which is the intended shape. Five more are dropdown and select menu items that pair it with focus:bg-accent instead:
"... text-sm outline-none focus:bg-accent data-[state=open]:bg-accent" That is the right call for a menu item. Inside an open menu the convention is a highlighted row, not a ring, and Radix moves focus between items as you arrow through them. A ring there would be noise.
So the audit is not "18 places removed the focus ring". It is 12 correct, 5 correct by a different convention, 1 missing. The useful lesson is that counting outline-none tells you nothing; you have to ask each one what it replaced the outline with.
The sixth no-ring case is TabsContent, a panel rather than a control, and it is only reachable in the signed-in dashboard.
A transition that animates a number nothing paints
One more thing fell out of the walk. The first few samples after each Tab reported odd outline colours: rgb(153, 155, 156), then rgb(153, 176, 202), then rgb(153, 194, 243). Sampling every 40ms explains it:
| time after Tab | computed outline-color |
|---|---|
| 0ms | rgb(153, 153, 153) |
| 40ms | rgb(153, 155, 156) |
| 80ms | rgb(153, 176, 202) |
| 120ms | rgb(153, 194, 243) |
| 160ms | rgb(153, 200, 255) |
The nav links carry transition-colors, and in Tailwind v4 that utility's property list is:
color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to outline-color is in there. So the ring colour animates over 150ms from the link's own muted grey to Chrome's focus blue. Since the ring on those elements is outline-style: auto, Chrome paints its own ring and ignores outline-color entirely, so the animation is a value that changes and a pixel that does not.
It costs nothing today. It would start mattering the moment somebody replaced auto with a real colour, at which point every focus ring on the site would fade in over 150ms, which is exactly the thing you do not want a focus indicator to do.
Try it
Any guide page will do, or the features index. Click the address bar, press Tab five times, and look at the word "Resources" in the header. Press Tab once more and watch "Sign in" pick up a ring. The difference between the fifth and the sixth press is the whole post.
For the record, the counts of 35 focusable elements and seven tab stops before <main> match what we measured in an earlier contrast audit of the same pages; the ring itself is what nobody had looked at.

