Skip to content
Development

Adopting a shared component surfaced a bug the component didn't have

By Victor Da Luz
astrotailwindaccessibilitydev-logsite

Earlier this week I found a real gap: this site’s consent library had no way to link a privacy policy from the consent prompt, and there was no privacy page to link to anyway. The fix already existed, just not here yet - @vdaluz/astro-opt-in-analytics had shipped a PrivacyExplainer component in v0.4.0, and vdaluz.com already had a working /privacy page built on it. Adopting it should have been the easy part of the day: bump a version, wire up a config, copy a page structure that already works somewhere else.

It was easy, right up until the accessibility check disagreed.

Everything else said it was fine

astro check: zero errors. Production build: clean, both the English and Spanish routes generated correctly. Browser snapshot: every string in the right place, both trackers listed under “the tools behind it,” the reopen-preferences button worked, the page looked exactly like its reference implementation. I even screenshotted it - neon green on black, exactly the site’s aesthetic, nothing visually wrong to the eye.

Then I ran npm run a11y, because a UI change gets that check regardless of how confident everything else looks. 12 of 14 URLs passed. The two new pages didn’t - axe flagged three links as “not distinguishable from surrounding text by means other than color alone.”

The underline that was there and wasn’t

The links in question had class="text-accent underline hover:text-accent-strong" - a permanent underline, not a hover-only one. That should satisfy the rule outright. So why was axe complaining?

I checked the actual computed style in the browser instead of trusting the markup. text-decoration-line: none. The class was sitting right there in the HTML, doing nothing.

The cause was a gap I’d actually read a warning about earlier in this exact session, without connecting it to what was coming: this repo’s own agent notes already document that Tailwind’s content array has to explicitly list any @vdaluz/* package whose components use Tailwind utility classes, or those classes never get generated - present in the HTML, absent from the compiled CSS, zero build error anywhere. The config only listed astro-blog. The consent-gate components from astro-opt-in-analytics had always used a scoped stylesheet instead of Tailwind classes, so this never came up before. PrivacyExplainer is the first component from that package to use plain utility classes, and it walked straight into a gap nobody had needed to close yet.

What actually caught it

Nothing about the type checker, the build, or a visual screenshot would have caught this. The class existed. The page rendered. The colors were right. The only thing checking whether the underline was actually load-bearing for someone who can’t distinguish blue-green text from gray text was an automated accessibility scan running real axe rules against the real rendered DOM.

One line fixed it - adding the missing path to content. The harder part was noticing that “renders correctly to my eyes” and “renders correctly, full stop” aren’t the same claim, and only one of them has a check that actually verifies it.

Related reading