DEV Community

Cover image for What a 100 Accessibility Score Didn't Check
Tarek Elzoghby | طارق الزغبي
Tarek Elzoghby | طارق الزغبي

Posted on Originally published at tarek-elzoghby.hashnode.dev

What a 100 Accessibility Score Didn't Check

I built a one-page site for a made-up potter as a practice brief: hero, about, gallery, contact. The client and the name are fictional. I worked with an AI as a design partner throughout, mostly to review my HTML and talk through decisions before I made them. The code is on GitHub. This post is only about the last stretch, after everything was built, when I went back through it for accessibility.

The baseline

Lighthouse gave me 99 Performance, 95 Accessibility, 100 Best Practices, and 91 SEO. Accessibility had exactly one complaint: the three contact links had low color contrast.

The links were set in terracotta (#B5603F) on a cream background, which comes to about 3.9:1. Normal-size text needs 4.5:1 to pass AA. Terracotta was the single accent color in the whole design, reserved for real calls to action, so I didn't want to drop it. The palette already had --color-primary-dark, a darker shade I'd originally made for hover states. I pointed the links at that instead. Accessibility went to 100 from that one change.

The link color had looked fine to me by eye, and it failed anyway. That's why I ran the audit at all.

What the 100 covers

A Lighthouse score covers the checks Lighthouse runs. It says nothing about the ones it doesn't. So I went through the site by hand, looking for gaps.

Decorative icons

The contact section has three inline SVG icons (email, phone, Instagram), each next to a text label that says the same thing. Screen readers handle inline SVGs inconsistently. Some announce "graphic," some read out raw markup, some skip them. Nothing breaks, but the experience is unpredictable, and the icons carry no information the label doesn't already carry.

<svg class="contact-icon" aria-hidden="true" focusable="false" ...>
Enter fullscreen mode Exit fullscreen mode

aria-hidden="true" removes the icon from the accessibility tree. focusable="false" is a safety net for older IE and Edge, where SVGs could be focusable by default and pick up a stray tab stop.

Landmarks

Lighthouse checks that basic page structure is present. It doesn't check whether landmarks are useful. <main> gave me one landmark for free. The four blocks inside it (hero, about, gallery, contact) weren't landmarks at all. A plain <section> only becomes a region landmark once it has an accessible name, and <address> has no landmark role by default.

So the question I worked out before touching any markup was which sections are places someone would want to jump to. Having a heading wasn't the test.

The hero is where a screen reader user already lands, so making it a landmark would only add a "you are here" entry to the jump list. About, Gallery, and Contact are content someone might skip straight to. Those three got landmark status.

For About and Gallery, I gave the <h2> inside each an id and pointed the parent section at it:

<section aria-labelledby="about-heading">
  <h2 id="about-heading">About me</h2>
  ...
</section>
Enter fullscreen mode Exit fullscreen mode

Contact was different. Its wrapper was an <address>, and aria-labelledby on an element with no landmark role names something that was never a landmark. I added role="region" alongside it:

<address role="region" aria-labelledby="contact-heading">
  <h2 id="contact-heading">Contact</h2>
  ...
</address>
Enter fullscreen mode Exit fullscreen mode

Contact is as much a jump-to destination as the other two, and its tag choice shouldn't make it the odd one out. That got it into the landmark list, and it was still wrong. <address> is for contact details, and the HTML spec doesn't allow heading elements inside it, so the <h2> there was invalid markup. I found this later, when the AI reviewed the repo again before I wrote this post. Nothing I had run flagged it, and NVDA listed the region as if nothing were off.

The fix was smaller than I expected. I changed the wrapper from <address> to <section> and removed role="region". Everything else stayed the same:

<section class="contact" aria-labelledby="contact-heading">
  <div class="address-content">
    <h2 id="contact-heading">Contact</h2>
    ...
  </div>
</section>
Enter fullscreen mode Exit fullscreen mode

A <section> with an accessible name is already a region, so once the tag was right the role had nothing left to do.

Checking it

Reading markup only tells me what I intended. I opened NVDA's Elements List (NVDA+F7) and filtered to Landmarks. It showed the implicit main plus three named regions: About me, Recent Work, and Contact. The hero was correctly absent and nothing was duplicated.

That check passed on the version with the invalid <address>. It tells me what a screen reader is given, not whether the markup is valid. Validity is a separate check, and the W3C validator is the one that covers it.

What's still open

Accessibility is at 100. I left SEO at 91 on purpose. What remains there depends mostly on the meta description and real content, which is better written once real copy and photos exist than guessed at now.

Top comments (4)

Collapse
 
carllowman profile image
SerpSpur •

I viewed a site recently with a 100 Accessibility Score, so at first I thought everything was fine. But after a deeper audit, we found issues the score didn’t cover—broken links, missing metadata, crawlability problems, and other technical SEO gaps.

That reminded me that one perfect score doesn’t mean the whole website is healthy. Accessibility is important, but it’s only one part of the picture.

I’ve found SerpSpur useful for going beyond a single score and checking broader technical SEO issues in one audit.

Score → investigate → verify → fix is a much better approach than relying on one number.

Thanks for sharing this—it’s a good reminder to look beyond the headline score.

Collapse
 
svgicons profile image
Svg/icons •

Do you keep the decorative/meaningful decision at the usage site rather than inside the icon component? The same SVG can be decorative in one context and semantic in another.

Collapse
 
tarek_elzoghby profile image
Tarek Elzoghby | طارق الزغبي •

Good question. There's no icon component here, just three inline SVGs in plain HTML, so the decision sits at each usage site by default. All three are decorative because each has a text label beside it. I haven't had an icon used on its own yet, but I'd expect the accessible name to go on the link or button around it, so the SVG itself can stay neutral.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.