The linter was pointing at the right line for the wrong reason
While fixing something unrelated, I noticed my linter failing on a line nobody had touched in a while: tabindex="0" on a <pre> element, flagged as “should only be declared on interactive elements.” Easy fix, I figured. Add an exception, move on.
Before I did, I asked a question the linter couldn’t answer: does this attribute still need to exist at all? I’d just shortened the text inside that <pre> from a long dependency URL to a short install command. Shorter text, presumably no more overflow, presumably no more need for keyboard scrolling. I almost deleted it on that assumption.
Measuring instead of assuming
At the desktop width my accessibility suite runs at, the text fit fine. No overflow. At a phone-sized 375 pixels, the longest of my four install commands still overflowed its box by about a hundred pixels. The attribute was still doing real work. My test suite had just never looked at a narrow enough screen to notice either way - not whether the bug existed, not whether my change had made it disappear.
The reframe
That changed what the actual problem was. The linter’s complaint was never really “remove this attribute.” It was “this attribute exists without doing its job properly.” A focusable element that a screen reader announces as nothing is worse than one that isn’t focusable at all: a stop with no explanation, the same shape as a badge that had nowhere to go.
So the real fix wasn’t silencing the linter. It was giving that scrollable box a name - a region role and a label - so landing on it with a keyboard tells you what you’ve landed on.
What’ll stick
My automated accessibility check passed both before and after every version of this, the broken one and the fixed one. It only ever checks one screen size. If your content’s length or your layout depends on viewport width, and plenty does, that check is confirming nothing about the width where the actual users are - which I’d already learned once on a real phone and apparently needed to learn again on a browser.
Related reading
The Dependabot PR that broke the build, and the one that broke accessibility
A grouped update bundling a TypeScript major bump with a parser that silently drops attributes before the a11y linter ever sees them.
A false 34 of 36 pass, and the sentence that said the same thing twice
An accessibility suite that quietly scanned a different site entirely, because something else was already listening on the port it wanted.
Adopting a shared component surfaced a bug the component didn't have
A privacy-policy page from a shared library rendered perfectly by every check I ran, except the one that actually mattered.