A target-size fix that WCAG didn't require
Short version: a 15px-tall breadcrumb link looks like a straightforward SC 2.5.8 Target Size (Minimum) failure — the criterion asks for 24×24 CSS px. It isn’t. Measured against the actual exception text, the Spacing exception clears it by 43px and the Equivalent exception clears it outright. The commit I labelled “Fix for WCAG22” fixed something WCAG did not require.
I still think the fix was right. But the reason isn’t the one in the commit message, and finding that out took measuring three exceptions I had skimmed past.
The commit
a79c5ff, 2 August 2026, message Fix for WCAG22. Six added lines, three in
each of two files, and no deletions:
.breadcrumb a {
+ display: inline-flex;
+ align-items: center;
+ min-height: 44px;
text-decoration: none;
}
The breadcrumb itself is one link alone in a paragraph:
<p class="meta breadcrumb"><a href="/blog/">← Devlog</a></p>
.meta sets font-family: var(--font-mono) and font-size: var(--step--1),
which is clamp(0.83rem, 0.81rem + 0.11vw, 0.9rem). Body line-height is 1.6.
The line-height doesn’t count toward the target
An inline non-replaced element’s target is its content box — the font’s ascent
plus descent — not its line box. At 13.51px with line-height: 1.6 the line box
is 21.62px, but the anchor itself measures 15.00px. Leading is not part of
the target.
That gap is the part I had wrong before measuring. The obvious arithmetic is font-size × line-height ≈ 21.6px, which reads as “a couple of pixels short of 24.” The real number is a third smaller.
Serving the built page and reading the anchor’s rect in Chrome 151:
| Viewport | font-size |
line-height |
Anchor box height |
|---|---|---|---|
| 500px | 13.51px | 21.62px | 15.00px |
| 1440px | 14.40px | 23.04px | 16.00px |
15px, not 21.6px. The extra 6.6px of leading belongs to the line box and is not part of the anchor.
To be sure that’s the hit area and not just a painting box, I walked
document.elementFromPoint down the anchor’s centre line one pixel at a time
and recorded where it stopped returning the link:
| Viewport | Rect height | Clickable band |
|---|---|---|
| 500px | 15.00px | 15px |
| 1440px | 16.00px | 16px |
Exactly the border box, both times. Leading is not clickable. So the target is
about 30% smaller than the naive calculation says, which means anyone reasoning
about target size from font-size × line-height is over-estimating their own
targets — in the safe direction for the calculation, the dangerous direction for
the user.
SC 2.5.8 is mostly exceptions
SC 2.5.8 Target Size (Minimum) is new at AA in WCAG 2.2, which is why it catches sites audited against 2.1. The normative text:
The size of the target for pointer inputs is at least 24 by 24 CSS pixels, except when:
Spacing — Undersized targets (those less than 24 by 24 CSS pixels) are positioned so that if a 24 CSS pixel diameter circle is centered on the bounding box of each, the circles do not intersect another target or the circle for another undersized target;
Equivalent — The function can be achieved through a different control on the same page that meets this criterion;
Inline — The target is in a sentence or its size is otherwise constrained by the line-height of non-target text;
User Agent Control — The size of the target is determined by the user agent and is not modified by the author;
Essential — A particular presentation of the target is essential or is legally required for the information being conveyed.
Note 1 — Targets that allow for values to be selected spatially based on position within the target are considered one target for the purpose of the success criterion. […]
Note 2 — For inline targets the line-height should be interpreted as perpendicular to the flow of text. […]
One line of requirement, five exceptions and two notes. I had read the one line and stopped, which is the actual mistake in this post.
Note 2 is worth pausing on given the previous section: line-height appears in
this criterion only inside the Inline exception, describing when a target is
excused. It is never the measurement of the target.
Spacing: clear by 43px
The Spacing exception is geometric and therefore measurable. Centre a 24px circle on the undersized target’s bounding box; it must not intersect another target, or the circle around another undersized target.
I computed the clearance against every focusable element on the page — 16 of them — and took the nearest:
| Viewport | Nearest other target | Its size | Clearance |
|---|---|---|---|
| 500px | theme toggle button | 48.0 × 44.0 | 43.50px |
| 1440px | SvitsGames wordmark link |
132.2 × 44.8 | 45.61px |
Not marginal. The nearest neighbour is more than 43px outside the circle, in a test where zero is passing. The breadcrumb sits alone in its own paragraph with a heading below it and a header band above, and that isolation is exactly the condition the Spacing exception exists for.
So the 15px breadcrumb conforms to SC 2.5.8. At 15px.
Equivalent: the header already had it
The Equivalent exception is the one that surprised me, because it makes the question about the page rather than about the element.
The breadcrumb points at /blog/. So does the header nav. Measuring every
anchor on the page with the same href:
| Viewport | Equivalent control | Size |
|---|---|---|
| 500px | header Devlog link |
73.2 × 44.0 |
| 1440px | header Devlog link |
80.8 × 44.0 |
One control, same destination, comfortably over 24×24 in both dimensions. The Equivalent exception applies independently of Spacing — and, as the next section gets to, it’s the one that matters most.
Inline: genuinely ambiguous, and I’m not going to pretend otherwise
The Inline exception reads “The target is in a sentence or its size is otherwise constrained by the line-height of non-target text.”
The breadcrumb is not in a sentence. And the second clause says non-target text — but the anchor is the entire content of its paragraph, so there is no non-target text whose line-height constrains it. On that reading the exception doesn’t apply.
The Understanding document restates it as “inline targets in sentences, or where the size of the target is constrained by the line-height of non-target text,” and explains the rationale: “text reflow based on viewport size makes it impossible for authors to anticipate where links may be positioned relative to one another.” A lone link in its own block doesn’t reflow relative to anything, which supports the same reading.
But Understanding documents are informative, not normative, and “a paragraph containing one link” is a shape the wording doesn’t obviously anticipate. I think the exception doesn’t apply here. I’m not confident enough to tell you it doesn’t, and it doesn’t matter to the outcome, because Spacing and Equivalent both do.
The AAA criterion is the stricter test, for a structural reason
SC 2.5.5 Target Size (Enhanced) is the 44×44 one, AAA, and it’s been there since WCAG 2.1. It is what the 44px in this codebase has always been aimed at.
Compare the exception lists:
| Exception | 2.5.8 (AA, 24×24) | 2.5.5 (AAA, 44×44) |
|---|---|---|
| Spacing | 24px-diameter circles must not intersect | absent |
| Equivalent | another control “that meets this criterion” | another control “at least 44 by 44 CSS pixels” |
| Inline | “in a sentence or its size is otherwise constrained by the line-height of non-target text” | “in a sentence or block of text” |
| User Agent Control | identical | identical |
| Essential | “essential or is legally required for the information” | “essential to the information” |
Three of the five differ, and the first one differs by not existing. 2.5.5 has no Spacing exception at all, so isolation buys you nothing at AAA — a lone 15px link fails on size unless some other exception catches it. Which means the higher criterion is not merely “the same test with a bigger number”; it is a structurally different test, and an element can pass AA on geometry that AAA does not recognise.
Here the Equivalent exception catches it anyway: that same 44×44 header Devlog
link satisfies 2.5.5’s Equivalent wording too. So the breadcrumb conformed to
both criteria before the commit that claimed to fix it.
Why the fix was still right
Conformance is a floor, not a description of whether something is usable. A 15px target is hard to hit with a tremor whether or not a criterion excuses it, and “there’s an equivalent link elsewhere on the page” is a conformance argument, not a user experience — the user still has to notice the other link, go to it, and know it does the same thing.
The exceptions exist because requiring 44px everywhere would wreck ordinary prose. They are not an assessment that the small target is fine. They are a statement that the cost of the rule exceeds its benefit in that specific situation. Where the cost is three lines of CSS, as it was here, the exception isn’t worth taking.
The site sets one floor at 44px rather than tracking 24 for some things and 44
for others: global.css:324 for the CTA, Layout.astro:190, Header.astro:84,
ThemeToggle.astro:112, Footer.astro:57, and both breadcrumbs. One number is
checkable — grep for min-height: 44px and count. Two numbers means deciding,
per element, which one applies, and that decision is where the exceptions creep
back in.
After the commit, the same measurement:
| Viewport | display |
Anchor box height |
|---|---|---|
| 500px | inline-flex |
44.00px |
| 1440px | inline-flex |
44.00px |
Two of those three declarations do the work. display: inline-flex is what
makes min-height apply at all — on the original display: inline anchor
min-height is ignored, because inline boxes don’t take height — and
min-height then sets the floor. align-items: center is alignment, not target
size: removing it and re-measuring gives the same 44.00px box and the same 44px
clickable band, with the label sitting at the top of it under the default
stretch instead of in the middle.
Padding alone would have expanded the hit area but left nothing whose height you can assert against.
What I took from this
The commit message was a guess dressed as a citation. “Fix for WCAG22” named a standard I hadn’t opened, to justify a change I’d made on instinct — and the instinct was right while the justification was wrong, which is the combination that never gets caught, because the code is fine.
Three specific things I’d want to know before writing that message again:
line-heightis not target height. The clickable box is the content area. Measure it; don’t calculate it.- A criterion is its exceptions. 2.5.8 is one sentence of requirement and five of exception, and skipping them produced a confident, wrong claim.
- AAA is not AA with a bigger number. 2.5.5 dropping the Spacing exception changes which arguments are available, not just the threshold.
And a smaller one: if you’re going to cite a standard in a commit message, read the clause. Otherwise say “make the breadcrumb easier to tap,” which is true, checkable, and what actually happened.
All measurements are Chrome 151 against the built site served over HTTP, at 500px
and 1440px viewport widths — the two ends of the clamp() that sets this font
size. The game-page breadcrumb (games/[...slug].astro:122) carries the same
three declarations but couldn’t be measured here: every game entry is
draft: true, so no game route is in the build yet.
Adjacent, on the same theme of a rule that looks like it’s working: a scoped Astro selector that matched nothing, where the 44px floor is what got lost, and a drop-shadow that never rendered.