You're In The Right Place If…
- Lighthouse Accessibility lists "Buttons must have discernible text" and/or "Links must have discernible text"
- The failing elements are icon-only controls: hamburger menus, search magnifiers, social media icons, carousel arrows, close (×) buttons
- A screen reader announces them as just "button" or "link" with no hint of what they do
- An "agentic browsing" or accessibility-tree audit reports controls without accessible names
What "discernible Text" Means In The Accessibility Tree
For every interactive element, the browser computes an accessible name, in a fixed priority order: aria-labelledby (points at another element's text), then aria-label (a literal string), then the element's own text content (including alt text of images inside it), then fallbacks like title. If every source comes up empty, the name is an empty string — and the audit fails, because the tree now contains a control nobody can identify.
This is why icon-only controls fail so consistently: an SVG or icon-font glyph has no text content. And there is a trap that makes it worse — icon SVGs are routinely (and correctly) marked aria-hidden="true" so screen readers do not announce their internal paths. But if the SVG was the link's ONLY content, hiding it removes the last possible name source. The icon is now hidden AND the link is nameless: a double fail from one well-intentioned attribute.
The Fixes, Ranked
Three patterns cover every case. Pick one per element — do not stack them.
<!-- hamburger menu: name + state -->
<button type="button" class="hamburger"
aria-label="Open menu" aria-expanded="false">
<span class="hamburger-box" aria-hidden="true">…</span>
</button>
<!-- icon-only social link: the SVG is aria-hidden, so name the link -->
<a href="https://www.linkedin.com/company/yourco/" target="_blank"
rel="noopener" aria-label="YourCo on LinkedIn">
<svg aria-hidden="true" …>…</svg>
</a>
/* sr-only utility, if you prefer hidden text over aria-label */
.sr-only { position:absolute; width:1px; height:1px; padding:0; margin:-1px;
overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0; } - aria-label — the workhorse for icon-only controls: <button aria-label="Open menu">. One attribute, done. Use action language ("Open menu", not "Hamburger icon")
- Visually-hidden text — a <span class="sr-only">Open menu</span> inside the control. Slightly more robust (it gets translated by browser translation tools, aria-label often does not) but needs the utility CSS class
- A real visible label — the most usable option of all: the word "Menu" next to the icon helps everyone, not just the tree. Worth considering for primary navigation
Name Toggles Properly: State Belongs In Aria-Expanded
A menu button is a toggle, and the tree can express that too. Keep the name constant ("Open menu" or simply "Menu") and let aria-expanded="true|false" carry the state — screen readers announce "Menu, button, collapsed" and "Menu, button, expanded" automatically. Flipping the label text between "Open menu"/"Close menu" instead of using aria-expanded works, but state attributes are what both assistive tech and AI agents parse most reliably. If your menu script already toggles a CSS class, toggling the attribute in the same line is a two-minute change.
Where These Fixes Live On Hosted Platforms
- HubSpot: hamburger and footer social markup lives in the theme's Header/Footer modules, not in page content. Edit via Design Manager; if it is an unmodified marketplace theme, clone the module (or create a child theme), add the attributes, and select the cloned module in your template. Check first — some themes expose an "aria label" or "link title" field per social icon
- WordPress: good themes label these already; otherwise a child theme override of header.php / the nav walker, or the social-icons block's built-in label field
- Wix / Squarespace / Shopify: built-in social elements are usually labelled automatically — failures typically come from custom-code blocks, where you control the HTML anyway
- Hand-coded / templates: add the attribute at the source, then add a lint rule or an axe-core check in CI so nameless controls cannot ship again
The AI-Agent Angle: Your Site's Second Audience
Agentic browsing is moving from demo to daily use: assistants that book, buy, research, and fill forms on a user's behalf. These agents overwhelmingly operate the page through its semantics — roles, accessible names, states — because that is cheaper and far more reliable than guessing from pixels. A checkout button named "Continue to payment" is an instruction an agent can follow; a nameless icon button is a dead end that makes the agent guess, or fail.
The practical takeaway is convenient: there is no separate "AI optimization" to do here. The same WCAG basics — discernible text on every control, correct roles, honest states — are exactly what agents consume. Sites that fixed accessibility years ago woke up already agent-ready. That is the quiet compounding return on this class of fix: one attribute serves the screen-reader user today and every AI intermediary that shops on your site tomorrow.
The Fix, Step By Step
- 1
Collect the nameless controls from the report
Expand both audits in Lighthouse — each lists the exact failing elements. Typical suspects: hamburger, search icon, social icons, carousel arrows, modal close buttons.
- 2
Check what killed the name
Icon-only content is the usual cause; an aria-hidden="true" SVG as the sole content of a link is the classic double-trap. Inspect the element and confirm there is no text, no aria-label, and no labelled child.
- 3
Add one name source per element
aria-label with action language ("Open menu", "Search", "YourCo on LinkedIn") is the one-attribute fix. Keep aria-hidden on the SVG itself — the label belongs on the button or link, not the icon.
- 4
Add state to toggles
Menu and dropdown buttons get aria-expanded, toggled by the same script that opens them. Name stays constant; state carries the rest.
- 5
Verify in the real tree, then re-run Lighthouse
DevTools → Elements → Accessibility panel shows each element's computed name and role — confirm the name is what you wrote. Then re-run the audit and sweep other templates (headers and footers repeat site-wide, so one fix usually clears every page).
From the trenches
How we hit this on a real production site
This week's audit of a B2B marketing site flagged exactly this pair. The mobile hamburger was a <button> containing only decorative spans — computed accessible name: empty. And the footer's LinkedIn link was a textbook case of the double-trap: its only content was an SVG correctly marked aria-hidden="true", which left the link itself with no name at all. To a screen reader: "button" and "link". To an AI agent asked to open the menu or find the company's LinkedIn: dead ends.
The fixes were two attributes: aria-label="Open menu" (plus aria-expanded for state) on the button, and aria-label="…on LinkedIn" on the anchor — the SVG stays hidden, exactly as it should be. The only real work was locating WHERE to apply them: on a HubSpot marketplace theme, header and footer markup lives in theme modules, so the change goes through the Design Manager (or a cloned module) rather than any page editor.
Because headers and footers render on every page, this two-attribute fix clears the audit site-wide in one edit. That is the general shape of discernible-text findings: minutes of code, most of the effort is knowing which file your platform hides it in. Stuck finding yours? Send us the failing element from your report — pointing you at the right template is a ten-minute favour.
Frequently Asked Questions
Should I put the aria-label on the SVG icon or on the button/link?
On the button or link — the interactive element is what needs the name. Keep aria-hidden="true" on the SVG so its internals stay silent. Labelling the icon while the control stays nameless does not fix the audit.
aria-label vs. visually-hidden text — which is better?
Both pass. Visually-hidden text is slightly more robust (it survives browser translation; aria-label often is not translated) but needs a CSS utility. aria-label is the pragmatic one-attribute fix for icon controls. Do not use both on one element.
Do AI agents really use the accessibility tree, not screenshots?
Mainstream agent tooling reads DOM semantics and accessibility information first — names, roles, states — because it is precise and cheap, with vision as a fallback. Either way, a control with a clear accessible name is the reliable path for the agent; a nameless icon forces guessing.
Why does the title attribute not fix this?
title is the weakest name source: inconsistently exposed to assistive tech, invisible on touch devices, and only shown on hover. Lighthouse may pass it in some cases, but aria-label or real text is the dependable fix.
The failing element is in my theme, which I cannot edit. Now what?
Marketplace themes on HubSpot/WordPress/Shopify all support an override path: clone the module, use a child theme, or override the template file. Failing that, a small script that adds the attributes on load is an imperfect but working patch — and worth an upstream feature request to the theme author.
Still stuck? Send us the exact error.
Paste the exact error message you're seeing and where it happens. We'll take a look — if it's quick we'll point you at the fix, and if it's deeper we'll tell you honestly what it takes.