Findings · 2026-09-25 · By VSNARY | Emmanuel Orta · 0 views
We failed our own contrast check. Hover states were worse.
One invisible line in a screenshot led to 55,118 measurements at rest, then 1,878 hovers and 1,834 keyboard focuses. What failed, why static checks missed the worst of it, and what we changed.
Our own site failed WCAG contrast in more places than we expected. An at-rest check in both themes found failures as low as 1:1. A second pass with a real pointer and keyboard found 65 more failure patterns, across 242 elements, that only appear on hover, focus or after scripts run, including call-to-action buttons that fell to 1.13:1 on hover because a general link rule out-ranked the button's own hover colour. All were fixed and re-measured at zero failures.
On 25 September a screenshot arrived with one line of our own marketing copy circled: “Point it at your own domain and see the same measurements, free.” It sat in a teal box at the foot of every post, and it was nearly invisible. We measured it at 1.11 to 1. The accessible minimum for text that size is 4.5 to 1. It had shipped on every blog post and guide on this site.
That one line turned into a full audit of our own pages. This is what it found, and why the worst of it was invisible to the kind of check most sites run.
The line that started it #
The text was coloured with the site’s rule token, a faint tint meant for hairlines and borders. On a teal box that tint is itself nearly teal, so the words sat almost exactly on their own background: 1.11 to 1 in one theme and 1.15 to 1 in the other. Nobody picks a pair like that on purpose. A token meant for lines was used for words, and nothing checked the pair.
Pass one: every page, both themes, at rest #
We rendered the site’s pages in light and dark mode and measured every piece of text against the background actually behind it, with semi-transparent layers and faded parents composited in. That came to 55,118 measurements.
| What failed at rest | Where | Worst contrast |
|---|---|---|
| Step numbers drawn teal on a teal disc | the forms product page | 1.0 : 1 |
| Registry badges faded to 55% opacity | 1,116 badges on the registry pages | 2.0 : 1 |
| Figure labels hard-coded in light-theme colours | charts and figures, dark mode | 2.2 : 1 |
| Gold labels on a pale background | light mode | 3.4 : 1 |
| Grey “faint” text | light mode | 4.3 : 1 |
Most of these traced back to a handful of design tokens: the faint grey, two golds and the grade colours. Darkening those in light mode and remapping the figure colours in dark mode fixed dozens of elements at once. We re-ran the check after the fix: zero failures.
Pass two: hover, focus and scripts #
A page at rest is not the page people use. We then drove a real browser across 36 pages in both themes with scripts running. It clicked every tab and step control, moved a real pointer over each distinct link and button (1,878 hovers), and moved keyboard focus onto each one (1,834 focuses). That pass found 65 failure patterns, across 242 elements, that the at-rest check had not reported.
| What failed in use | State | Worst contrast | Cause |
|---|---|---|---|
| “Get it fixed” and “Fix it all — $749” | hover | 1.13 : 1 | a link-hover rule overrode the button’s own hover colour |
| “Scan now” and “Scan your own domain” | hover | 1.77 : 1 | the same rule, on the teal buttons |
| The Share button on the demo report | hover | 1.0 : 1 | a site-wide button hover painted teal behind teal text |
| Directory filter chips and form channel rows | hover | 1.0 : 1 | the same site-wide rule |
| The focus ring in the teal box | keyboard focus | 1.61 : 1 | a teal ring on a teal box |
| Inactive steps in a walkthrough | after a click | 1.5 : 1 | faded to 30% opacity |
| A lead-form button | rendered by script | 3.38 : 1 | white text on a mid-teal brand colour |
The worst class is the first two rows. Our “Scan now” and “Fix it all” buttons were fine at rest and turned unreadable the moment someone moved a mouse onto them. These are the buttons that turn a reader into a customer. The cause was specificity: a general rule for links inside articles was more specific than the button’s own hover rule, so on hover the button kept its fill and took the paragraph’s text colour. A check of the page at rest can never see that, because at rest nothing is hovered.
The lead form we ship to other sites #
The last row mattered beyond this site. The same lead-form script runs on customers’ pages, and it drew a configured button colour with white text whether or not white was readable on it. It now checks the pair and, when it fails, deepens the button shade just enough to pass while keeping the brand hue and the chosen text colour. Of ten live configurations, three failed and were corrected; seven were left byte for byte as they were.
What we changed so it stays fixed #
- The call-to-action subtext now uses the text colour made for that box, not a border tint.
- Button hover rules inside articles and reports now set the text colour explicitly, so a more general link rule cannot win.
- Buttons styled as chips and rows set their own hover background instead of inheriting the site-wide one.
- We verify every fix in both themes, at rest and in hover and focus, before it ships and again after. The run on the fixed build before release and the run on the live site after it both found zero failures.
Why a site that measures other sites missed this #
Every one of these colours was chosen by a person, and every one looked fine in the context it was chosen for. The border tint looked right on borders. The link-hover rule looked right on links. The site-wide button hover looked right on plain buttons. The failures only appeared where two reasonable rules met on one element, and nobody looks at every element in every state. That is the case for measuring rather than looking: a check does not get used to a page, and it does not skip the states that are tedious to reach.
What this does not cover #
Contrast is one accessibility check among many. It says nothing about whether a screen reader can use the page; for that side, see the nine structural things a screen reader needs. The method we used for hover and focus is written up as a step-by-step guide.
Every figure above came out of this scanner.
Point it at your own domain and see the same measurements, free.
The main product
Found this on your own site? We fix it for $749.
Scan free to see where you stand. The fix is one site, every finding implemented and re-measured, with a sealed before and after.
Questions this post answers
Why do buttons lose contrast on hover?
Usually specificity: a more specific general rule, such as one for links inside articles, overrides the button's own hover text colour, so the fill changes but the text does not.
Can an automated contrast checker find hover problems?
Not one that measures the page at rest. Hover and focus styles only apply when an element is hovered or focused, so the check has to put the page into that state first.
What contrast ratio does WCAG require?
4.5 to 1 for normal text and 3 to 1 for large text (24px, or about 18.7px bold) under WCAG 2.1 AA. Focus indicators and other UI parts need 3 to 1 against what is next to them.
Why did transparent colours cause failures?
A tint meant for borders, such as 9 to 12 percent white, lands almost on the colour of the background behind it, so text in that colour nearly disappears.
Does fading an inactive element with opacity fail contrast?
Often. Opacity pulls the text colour toward the background; at 30 percent even dark text dropped to about 1.5 to 1.
Related findings
Comments
Comments are read before they appear. Nothing is published automatically, and no account is needed.
Writing about this? Facts, live figures and marks — every number on that page is dated and traceable to a scan.