Skip to content

Custom buttons, links and menu items need a name

  • Rule: axe/aria-command-name
  • Checks: ARIA commands must have an accessible name
  • WCAG: 4.1.2
  • Fix class: content: A fix needs authored text.
  • People who use a screen reader or a braille display, most of them blind.
  • People who control the page by speaking.

Name the control for its action with aria-label, and hide the icon from assistive technology with aria-hidden="true". Where a native <button> or <a href> fits, use it instead of the role: it brings keyboard support and focus with it.

<div role="button" tabindex="0" aria-label="Close">
<svg aria-hidden="true" focusable="false"><use href="#icon-close"></use></svg>
</div>

The words are in a hidden element or drawn with CSS

Section titled “The words are in a hidden element or drawn with CSS”

Put the words inside the control as text, hidden visually if need be with a class that clips them but keeps them in the accessibility tree; text inside display: none, visibility: hidden or aria-hidden="true" does not count. axe also ignores text drawn with CSS content. An icon font character written into the markup passes axe but reads as nothing or a stray symbol, so it does not name the control.

<div role="menu" aria-label="Order actions">
<div role="menuitem" tabindex="-1">
<svg aria-hidden="true" focusable="false"><use href="#icon-trash"></use></svg>
<span class="visually-hidden">Delete order</span>
</div>
</div>

Visible text elsewhere on the page already names the action

Section titled “Visible text elsewhere on the page already names the action”

Point aria-labelledby at that text by its id, so the name and the visible words stay the same.

axe accepts it, but it shows only while a mouse hovers, so keyboard, touch and voice control users never see what to call the control.

Describing the icon rather than the action, such as aria-label="Arrow"

Section titled “Describing the icon rather than the action, such as aria-label="Arrow"”

People hear what the control looks like, not what it does. Name the result: “Next page”, “Close”.

The rule stops matching, but the element still reacts to clicks and now has no role or name for assistive technology at all.

The part of the fix that differs in React, in its syntax. The rest of the guidance holds.

A menu item or custom button component drops the aria-label its caller passes

Section titled “A menu item or custom button component drops the aria-label its caller passes”

Spread the props the component does not use onto the element that has the role, so a caller’s aria-label or aria-labelledby reaches the DOM.

function MenuItem({ icon, ...props }) {
return (
<li role="menuitem" tabIndex={-1} {...props}>
<svg aria-hidden="true" focusable="false">
<use href={`#icon-${icon}`} />
</svg>
</li>
);
}
function OrderMenu() {
return (
<ul role="menu" aria-label="Order actions">
<MenuItem icon="trash" aria-label="Delete order" />
</ul>
);
}

Adding aria-label where the component is used, when the component does not pass it on

Section titled “Adding aria-label where the component is used, when the component does not pass it on”

The prop stops at the component, so the rendered element still has no name. Check the rendered DOM, not the JSX.

Writing the prop in camel case, as ariaLabel

Section titled “Writing the prop in camel case, as ariaLabel”

React renders it as an unknown arialabel attribute, which names nothing. ARIA attributes keep their hyphens in JSX: aria-label.

axe-core’s documentation of the rule

CORA’s fix guidance adapts content from accessibility-agents, under the MIT License: see its notice.