Skip to content
crawlforgeEspañol
Measurements8 August 202610 min readLeer en español

The button you couldn't read and the focus that hid

We ran our own website through a design audit and an accessibility one. Two measurable failures came out: the primary button sat at 3.09:1 contrast in dark theme, and eight of the twenty-four focusable elements disappeared under the header when moving through the page by keyboard.

This site has been written in gaps between engine releases for about a month. Before publishing it we put it through two separate reviews: a design one, hunting the tics that give a template away, and an accessibility one against WCAG 2.2. Both found things, and the worst two can be stated as a number.

3.09

The site’s primary button is blue with white text. In light theme the blue is #1B44E0, dark and saturated, and white on top of it measures 7.12:1. Comfortable — WCAG asks for 4.5:1 on normal text.

The dark theme isn’t an inversion of the light one. It’s designed separately, and there the blue lifts to #6E8BFF, because the darker one vanishes against a near-black background. That lighter blue with white text on it measures 3.09:1.

It doesn’t reach. And it doesn’t reach on the subscription form’s button, the most visible element on the home page, for everyone whose system is set to dark.

The blue isn’t what failed here. What failed was writing color: #fff inside the button and never looking at it again when the dark theme was designed. The ink that goes on top of the accent can’t be the same colour in both themes, because the accent isn’t either. Light theme wants white on dark blue; dark theme wants ink on light blue, and that measures 6.08:1.

It’s a named token now, and the button references it. A loose #fff inside a rule is how a design system stops being a system.

The second button

Token fixed, rebuilt, and the form’s button was still white.

That button doesn’t use our class. A third-party component paints it — the one that talks to the email provider — and it carries its own set of variables. We went to look at theirs with some suspicion, expecting a #fff nailed into the package.

It was fine. The component declares --accent-ink and only falls back to white if the host site says nothing. The gap was in our mapping: we set the accent colour and forgot the colour that goes on top of it. The component was doing the right thing; we were handing it half the information.

Eight out of twenty-four

The second failure is an accessibility one with its own criterion in WCAG 2.2 — 2.4.11, “Focus Not Obscured”, level AA. It’s new in 2.2 and it is alarmingly easy to miss.

This site’s header is sticky. It stays at the top as you scroll, it’s 61 pixels tall, and so far nothing unusual. Now picture moving backwards through the page with Shift+Tab. The browser has to bring the newly-focused element into view, and to do that it lifts it to the top edge of the window. Which is exactly where the header lives.

The focused element lands behind it. You press Shift+Tab, the focus ring leaves the screen, and you have no idea where you are.

This is measurable without installing anything. For each focusable element: put the scroll position below it, give it focus, let the browser do its minimum adjustment, then check whether the resulting rectangle falls inside the header’s 61 pixels.

On the home page, eight of twenty-four focusable elements were completely hidden. Among them the email field and its button — the whole form.

The fix is one line of CSS: scroll-margin-top on focusable elements, set above the header’s height. It tells the browser to leave that gap when it scrolls. We re-ran the same measurement afterwards: zero.

Worth flagging how close the first measurement came to being wrong. The first version of the script counted eight hidden elements that turned out to be the header’s own. Being inside the header is not being hidden by it. We fixed the script to exclude its descendants, measured again, and got zero — the criterion passed when scrolling down. Only by simulating backwards navigation did the real failure appear.

The rest

There was no skip link, which is criterion 2.4.1 at level A. Without one, keyboard users cross the header’s eight elements on every page before reaching the text.

The theme toggle announced itself as “Theme, botón” to a Spanish screen reader. An accessible name is interface text and gets translated like everything else; it slipped through because it never appears on screen.

And the menu didn’t mark which page you were on. An aria-current turns a list of links into a position.

What we left alone, and why

The design review noted several links at 22 pixels tall, under the 24 the target-size criterion asks for. Before changing anything we went and read the exception: an undersized target passes anyway if a 24-pixel circle centred on it doesn’t intersect its neighbour’s. The nav has 20 pixels of horizontal spacing and 12 vertical when it wraps, and at those widths the circles never touch.

So it already passed. Changing it would have meant disturbing a tested layout to improve a number that was fine.

We also checked whether prefers-reduced-motion was needed. There isn’t a single transition or animation in the whole stylesheet, so there’s no motion to reduce. And there isn’t one <img> tag across the thirty-one pages, so there’s no alt text to be missing. Both are consequences of a site built out of nothing but type.

What we couldn’t check

The reflow criterion asks that no horizontal scrolling be needed at 320 pixels wide. Chrome won’t shrink a window below 606, and nothing overflows there. The stylesheet contains no fixed width above 99 pixels, so nothing suggests it breaks — but that’s an indication, not a measurement. It stays unverified, and we’re saying so because the difference matters.

Why write this up

We could have published the site quietly and fixed things as we went. But we sell a tool that tells people what’s wrong with their website, and the least anyone can ask is that we show what was wrong with ours, with the number next to it.

For what it’s worth: three of the five faults in this entry were found by a machine running a rule, and the other two by a person looking with a specific case in hand. The second method is still winning.

Built in the open

Every two weeks: measurements, defects, worked examples. Nothing else.

← Back to the devlog