fix(docs): the phone nav toggle needs a keyboard, not a mouse

A regression the previous commit introduced, found in review. The checkbox
driving the collapsed nav carried `hidden`, which is `display:none`, which
removes it from the tab order AND from the accessibility tree. A <label> is
not focusable on its own -- the checkbox it labels is what carries focus -- so
at <=900px the navigation could be clicked and reached no other way, and the
accessibility snapshot contained neither the checkbox nor any of the 25 links.

The toggle is now visually hidden but focusable at narrow widths, and
display:none at wide ones. Keeping display:none on the wide side matters:
merely un-hiding it puts focus on an invisible control whose label is
display:none, which is its own wart.

Measured at 390, 900, 901 and 1280, because the regression lives on one side
of a media query. At 390 and 900 the tab order is brand -> navtoggle, the
toggle is focusable, the a11y tree carries a checkbox node, and Space takes
visible nav links 1 -> 26. At 901 and 1280 the toggle is display:none, not
focusable, absent from the a11y tree, and tab runs straight through the 26
links -- desktop identical to before. 0 of 23 pages scroll horizontally at
320, 390 and 1280, so the absolutely-positioned 1px input introduces nothing.

WHAT IS WORTH MORE THAN THE FIX, and why doc-site.md gains a paragraph rather
than the code gaining a comment: my probe for the previous commit counted
VISIBLE nav links. That answers "does operating the toggle reveal the links" --
correct and complete for a mouse -- and is silent on whether any non-mouse
route exists. It could not have seen this, however sharp the visibility
instrument was. Last round's lesson was that a measurement which cannot
distinguish the two states is not a measurement; this round adds the half it
was missing: a measurement of the wrong PROPERTY is not a measurement either.
checkVisibility fixed the visibility instrument; it does not turn a visibility
count into an operability check. The instrument here is tab order, .focus()
and the accessibility snapshot.

Gates: 1378 rust, 26 node --test, 16 playwright (3 skipped, webkit OPFS), all
three check-comment-runs modes, 3462 site links / 0 broken, 0 nested anchors,
0 of 23 pages overflow at 320/390/1280.

improvise-djoo

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Edward Langley
2026-09-20 14:09:37 -07:00
parent 41f694f535
commit c0b786cf0d
2 changed files with 58 additions and 5 deletions
+53
View File
@@ -220,3 +220,56 @@ the first guess was wrong: `getBoundingClientRect` reported every element
inside the viewport, because the text overflows its box without widening it.
Only `scrollWidth` per element names the culprit. Measure the property that
actually overflowed rather than the one that usually does.
## 7. The phone nav is a checkbox, and that is not arbitrary
At phone width the sidebar's 25 links stacked above the content, so every page
opened on roughly 1500px of nav. The wall was not inherited: **all of this CSS
is new**, so it shipped for the first time with the site, which is the test for
whether a defect is pre-existing and the one most easily got wrong in the
direction of declining work you introduced.
`<details>` is the obvious control and it is **wrong here**, for a reason no
spec-level reading predicts. The sidebar layout has to force the disclosure
*open* at wide widths, and a closed `<details>` hides its content through
`::details-content` — which `display` cannot override. Measured in Chromium,
the desktop sidebar came out collapsed to a single link with its toggle also
hidden: unusable, and in the half nobody would think to re-check after a
phone-only change. A hidden checkbox plus a `<label>` puts the hiding in *our*
media query, so forcing it open is ordinary cascade. Anyone reading the markup
will see a checkbox where `<details>` obviously belongs; this paragraph is why
it must not be tidied into one.
**And the first probe of the fix could not see the difference.** Counting nav
links whose `getBoundingClientRect().height > 0` reported **26 visible in both
the collapsed and the expanded state**, because Chrome reports a height for
`content-visibility`-hidden children — so it would have passed the broken
`<details>` build. `checkVisibility({contentVisibilityAuto: true,
visibilityProperty: true})` discriminates.
That is the same instrument failing twice: §6's overflow fix also measured
clean by `getBoundingClientRect` while the page still scrolled. **A measurement
that cannot distinguish the two states is not a measurement** — the repo's
signature defect, a test that passes while the thing it names is broken,
arriving through the instrument rather than through the assertion. Before
trusting a browser measurement, injure the thing being measured and check the
number moves.
**And the fix's own probe had the same shape one level up.** Counting *visible*
nav links answers "does operating the toggle reveal the links" — correct and
complete for a mouse — and is silent on whether any non-mouse route exists. The
first version of the checkbox carried `hidden`, which is `display:none`, which
removes it from the tab order *and* from the accessibility tree: the label is
not focusable on its own, so at ≤900px the navigation could be clicked and
reached no other way, and the accessibility snapshot contained neither the
checkbox nor any of the 25 links. A visibility count cannot see that, however
sharp the visibility instrument. The toggle is therefore visually hidden but
focusable at narrow widths and `display:none` at wide ones, and the check is
tab order, `.focus()` and the accessibility snapshot — measured at 390, 900,
901 and 1280, because the regression lives on one side of a media query.
So the rule from §6 has a second half: **a measurement of the wrong property is
not a measurement either, however precise it is.** `checkVisibility` fixed the
visibility instrument; it does not turn a visibility count into an operability
check. Ask what the defect would *do* to a user, then pick the instrument that
observes that.