squirrelscan
GitHub

aria-label: which elements need one and how to check

Buttons, links and inputs with no visible text and no aria-label have no name for screen readers. squirrel finds every one on every page.

What aria-label is

aria-label gives an element a name that assistive technology reads out when the element has no visible text. A button whose only content is an unlabeled icon, an <a> wrapping an image with no alt, or an <svg> with a click handler and no <title> has nothing a screen reader can announce. The user hears “button” and nothing else.

Most elements do not need it. Visible text is the accessible name, so <button>Save</button> is complete. aria-label is for the cases where the visible label is a picture, or the visible text is not the whole story (“Close” on a dialog is clearer as aria-label="Close settings"). aria-labelledby does the same job by pointing at existing text, and <title> inside an <svg> names the graphic.

What squirrel checks

The rule runs on every crawled page and reports two checks:

  • aria-buttons: every <button> and every element with role="button". A button fails when it has no text content, no aria-label, no aria-labelledby, no title, no <img alt> inside it and no <svg><title> inside it. Report message: N button(s) without accessible names, with the id or class of each one. Severity error. aria-buttons-unconfirmed warns with N button(s) without accessible names may be hidden by a style (unconfirmed) for unnamed buttons a style may hide in a way squirrel cannot settle, and the pass reads All buttons in the accessibility tree have accessible names, with hiddenWithoutName, when unnamed buttons were skipped as hidden.
  • aria-svg: every <svg> that carries an onclick or a tabindex. It warns when the svg has no aria-label, no aria-labelledby and no <title> child. Report message: N interactive SVG(s) without accessible names.

A button out of the accessibility tree has nothing to announce, so it is not reported: one with the hidden attribute (except hidden="until-found"), aria-hidden="true", display: none on itself or an ancestor or visibility: hidden on itself or an ancestor, set inline or by a <style> rule squirrel can decide (such as .closed { display: none }), or inside a closed <dialog> or an unopened popover. It still needs a name before it can be shown. Hiding squirrel cannot settle is reported as unconfirmed instead: an inline style it cannot read exactly, a <style> rule it cannot decide, and, on a page that also loads an external stylesheet, hiding from a <style> rule or visibility inherited from an ancestor. External stylesheets are not read, so a button hidden only by the site’s external CSS, such as a closed modal, is still reported. See What squirrel can see for the details. An interactive SVG inside a hidden ancestor, or with aria-hidden="true", is not counted by aria-svg either.

A page past squirrel’s style limits (more than 5,000 style rules, a <style> block over the size limit, a very long selector, a no-break or other non-ASCII space in a style block, or a rule nested inside another) is read as unconfirmed for every element a style might hide: the rules past the limit could hide or show any of them, so an unnamed control is reported as unconfirmed, not as a failure. CSS nesting is not read: a nested rule that sets display or visibility (b { @media (min-width: 0) { display: none } }, .a { & .b { display: none } }) leaves the elements it could reach unconfirmed, and a nested rule that sets neither changes nothing.

A control that carries a no-js class, itself or on an ancestor other than <html>, is reported as unconfirmed on a page with an external stylesheet or an @import, with the reason that it may be hidden when JavaScript runs. A theme usually hides that fallback with a rule such as .js .no-js { display: none } in a stylesheet squirrel does not read. The same control on a page whose styles were all read keeps its verdict, and a class such as no-js-hidden is not a marker.

How to fix it

<button aria-label="Close settings">
  <svg aria-hidden="true">...</svg>
</button>

Put the label on the interactive element, not on the icon inside it, and hide the icon from assistive technology with aria-hidden="true". For an svg that is itself clickable, add <title> as its first child or an aria-label on the <svg>.

A common false positive is a button whose only content is an icon font glyph in a pseudo-element. squirrel sees an empty button. Add aria-label, which is the correct fix anyway.

Rule IDa11y/aria-labels
CategoryAccessibility
ScopePer-page
Severityerror
Weight6/10

Enable / disable

Disable this rule

[rules]
disable = ["a11y/aria-labels"]

Disable all Accessibility rules

[rules]
disable = ["a11y/*"]

Enable only this rule

[rules]
enable = ["a11y/aria-labels"]

enable replaces the default ["*"], so this is the only rule selected. Leave disable out: a rule that matches any disable pattern is skipped even when enable names it. Only an enabled setting under a rule’s own [rule_options] table overrides both lists.

  • a11y/button-name: the same defect checked over a wider set, including <input type="button"> and nested labels.
  • a11y/link-text: links whose text is “click here” or an image with no alt.
  • a11y/form-labels: inputs with no <label> or aria-label.
  • a11y/aria-hidden-focus: focusable elements hidden from assistive technology, the mirror problem.

Accessibility findings ship in every audit next to the SEO, performance and agent experience rules. See Website security scan with AI for how an agent works through a report.

References

Check your site

Run squirrel audit https://example.com and open the Accessibility section of the report. Every unlabeled button is listed by page. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.

Type to search…

↑↓ navigate↵ openesc close