Skip to content

Engineering

Accessible Names and false confidence from a quick glance

A page can look labeled at a glance and still leave controls without usable accessible names once you check the rendered accessibility tree.

· by Henry Smith

Accessible Names and false confidence from a quick glance

I still eyeball a page and decide the buttons are fine. There is text near the control. The icon is obvious to me. Then I open the accessibility pane and the computed name is blank. That gap is the whole topic.

SlaySlop's Accessible names check "finds controls and content that lack usable accessible labels." I like the wording because it does not flatter a quick glance. Usable labels either resolve in the rendered tree or they do not.

Outcome I want from a naming pass

I want fewer cases where visual QA signs off and assistive tech gets a shrug. Concretely: primary controls on the URLs I care about expose a usable accessible name after JavaScript and layout finish. Secondary noise can wait.

Boundaries I keep repeating to myself

A glance is not a check. A curl is not a check for this signal either. I reach for curl when I want headers and status. Names live in the DOM after render.

SlaySlop is read-only on public pages and needs permission. It is not a pentest. Accessible names sits with other accessibility checks in the catalog: Accessibility audit on a rendered page, Document semantics for structure, Viewport configuration for mobile viewport behavior. Passing one row never meant the others are done.

I also refuse to invent precision the site does not publish. The glossary gives that one sentence for Accessible names. Product pages say quality audits run after a real-browser render and that findings stay linked to the page where they were observed. That is enough to work with.

Loading diagram.

Happy path that actually changes my mind

Pick a URL I am allowed to test. Load it in a browser. Focus the suspicious control. Read the computed name. If I am using SlaySlop, open the finding and jump to the affected page instead of guessing which template is wrong.

bash
# My usual first five seconds: is the host even answering?
curl -sI "https://www.example.com/account" | sed -n '1,20p'
http
HTTP/2 200
content-type: text/html; charset=utf-8
x-frame-options: DENY

Useful. Incomplete. Next step is still the rendered page.

When I fix markup, I try the smallest honest change:

html
<!-- Glance says the heading labels the action. Tree says otherwise. -->
<section>
  <h2>Invoice history</h2>
  <button type="button" class="icon-download">
    <svg aria-hidden="true"><!-- ... --></svg>
  </button>
</section>

<!-- Same UI, name now resolves -->
<section>
  <h2 id="invoice-history-heading">Invoice history</h2>
  <button type="button" class="icon-download" aria-labelledby="invoice-history-heading">
    <svg aria-hidden="true"><!-- ... --></svg>
  </button>
</section>

Sometimes visible text inside the button is clearer. Sometimes aria-label="Download invoice history" is clearer. I care that the tree gets a usable string, not that I won an API style argument.

SlaySlop's Performance and accessibility workflow matches the order I already trust: render key pages, run quality audits, attach findings to routes, start with the worst. Anonymous primary actions beat footer polish.

Where false confidence comes from

Nearby text that is not programmatically associated. My eyes group a caption with a button. The accessibility tree does not. That single mismatch explains most of the "but it looks labeled" arguments I have had with myself.

Designer mocks with annotation that never landed in the component API. The Figma note said "aria-label: Open filters." The React props never grew that field. Reviewers approved the mock. Production shipped the hole.

Dark mode screenshots and motion-reduced recordings in a handoff deck prove pixels. They do not prove names. I have scrolled those decks, nodded, and still found an empty computed name five minutes later in DevTools.

A prior lab run on a different build is another trap. Staging had text buttons. Production got the icon redesign Friday afternoon. I have shipped that regression myself.

Third-party embeds confuse ownership. The chat launcher looks like part of the site. Its shadow DOM or iframe may not expose a name you control. Knowing the boundary saves a useless afternoon spent patching the wrong repo.

There is also the "I tabbed once on desktop" pass. Focus moved, something lit up, I assumed the name was fine. The affordance can be focusable and still announce as a generic button. The glance checked focus rings. It never checked the string.

Pitfalls when reading a clean report

No Accessible names findings on the pages crawled is not the same as "we are accessible." Structure can still be wrong. The broader Accessibility audit can still surface other automated issues. Keyboard behavior still needs a human sitting at the keyboard.

A finding count of zero on the marketing homepage does not clear the app shell you did not scan. Public read-only coverage follows what was rendered and discovered. If the crawl never opened the billing route, the billing icon button never got a vote.

"We added aria-labels everywhere" can create its own mess. Duplicate speech. Wrong language on a translated page. Stale labels after copy changes. Usable means maintained, not sprayed once before launch.

Related next step

When a glance and a tree disagree, trust the tree. Fix the control. Retest the same URL. If you want the product's one-line definition while writing the ticket, it lives at Accessible names. The surrounding catalog is on checks. I keep both open less for inspiration and more so I do not over-claim what the scanner saw.