Command roles: buttons, links and menu items need names
Anything that performs an action has to announce what it does. squirrel lists every command element on a page with no accessible name.
What a command element is
ARIA groups the roles that perform a single action as commands: button, link, menuitem, menuitemcheckbox and menuitemradio. Native <button> and <a href> carry the first two implicitly.
A command with no accessible name is announced as bare role, so the user hears “menu item” with no idea what it does. Visible text is usually enough; icon-only commands need aria-label, aria-labelledby, a nested <img> with alt, or an <svg><title>.
What squirrel checks
One check, aria-command-name, at severity error. It covers three sets: every element with one of the five command roles, every <button> with no explicit role, and every <a href> with no explicit role.
- Fail when such an element has none of the following: a non-empty
aria-label; anaria-labelledbypointing at an element with text; a non-emptytitle; text content; a nested<img>with non-emptyalt; a nested<svg><title>with text. Report message:N command element(s) without accessible names. Items are listed asdiv[role="menuitem"],button[type="submit"]ora[href="/checkout"], truncating a longhrefat 30 characters, with up to ten shown and a count of the rest. aria-command-name-unconfirmedwarns withN command element(s) without accessible names may be hidden by a style (unconfirmed)for unnamed elements a style may hide in a way squirrel cannot settle.- Pass with
All command elements have accessible names, orAll command elements in the accessibility tree have accessible nameswithhiddenWithoutNamewhen unnamed elements were skipped as hidden.
A command element 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 command element hidden only by the site’s external CSS, such as a closed modal, is still reported. See What squirrel can see for the details.
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.
This rule overlaps a11y/button-name and a11y/link-text on purpose. It is the one that also reaches menu items built from <div> and <span>.
How to fix it
<div role="menuitem" aria-label="Duplicate row">
<svg aria-hidden="true">...</svg>
</div>Put the name on the element that carries the role, not on a child, and hide any decorative icon inside it with aria-hidden="true".
A link wrapping only a background-image logo is the usual false positive. There is no markup for squirrel to read, and there is nothing for a screen reader to read either, so the fix is the same: add aria-label.
| Rule ID | a11y/aria-command-name |
| Category | Accessibility |
| Scope | Per-page |
| Severity | error |
| Weight | 7/10 |
Enable / disable
Disable this rule
[rules]
disable = ["a11y/aria-command-name"]Disable all Accessibility rules
[rules]
disable = ["a11y/*"]Enable only this rule
[rules]
enable = ["a11y/aria-command-name"]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.
Related rules
- a11y/button-name: the same defect scoped to buttons, including
<input type="button">. - a11y/link-text: links that have a name but a useless one.
- a11y/aria-labels: buttons and interactive
<svg>elements. - a11y/aria-required-parent: menu items that are named but sit outside a menu.
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
- The button role, MDN
- Accessible name and description computation, W3C
- Providing accessible names and descriptions, WAI-ARIA Authoring Practices
Check your site
Run squirrel audit https://example.com and open the Accessibility section of the report. Every unnamed command is listed with a selector you can search for. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.