Skip to content

Dialogs need a name

  • Rule: axe/aria-dialog-name
  • Checks: ARIA dialog and alertdialog nodes should have an accessible name
  • WCAG: none; a best practice, not a WCAG success criterion
  • Fix class: content: A fix needs authored text.
  • People who use a screen reader or a braille display, most of them blind.

Point aria-labelledby at the title by its id. Screen readers announce the name as focus moves into the dialog, so people know what opened. A native <dialog> element needs a name the same way.

<div role="dialog" aria-modal="true" aria-labelledby="delete-title">
<h2 id="delete-title">Delete this file?</h2>
<p>You cannot undo this.</p>
<button type="button">Delete</button>
<button type="button">Cancel</button>
</div>

Add a visible heading and point aria-labelledby at it, so everyone sees the same name. Where the design has no room for one, name the dialog with aria-label in words that say what it is for, such as “Cookie settings”.

aria-labelledby points at an id that is missing or empty

Section titled “aria-labelledby points at an id that is missing or empty”

Make sure the referenced element exists, with text, whenever the dialog is shown, and that its id is unique on the page. A dialog component rendered more than once needs a distinct id for each copy.

Naming the dialog for its role, such as aria-label="Dialog" or aria-label="Modal"

Section titled “Naming the dialog for its role, such as aria-label="Dialog" or aria-label="Modal"”

Screen readers already announce the role. The name has to say what this dialog is about.

axe accepts it, but a mouse user then gets a tooltip wherever the pointer rests inside the dialog, and the name is tied to no visible heading.

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

A dialog component hard-codes the id its aria-labelledby points at

Section titled “A dialog component hard-codes the id its aria-labelledby points at”

Generate the title’s id with useId inside the component and use it for both id and aria-labelledby, so each dialog on the page finds its own title.

import { useId } from 'react';
function ConfirmDialog({ title, children }) {
const titleId = useId();
return (
<div role="dialog" aria-modal="true" aria-labelledby={titleId}>
<h2 id={titleId}>{title}</h2>
{children}
</div>
);
}

Generating the id with Math.random() or a counter

Section titled “Generating the id with Math.random() or a counter”

The value differs between the server and the client, which causes a hydration mismatch, and it changes on every render, which breaks any reference that kept an earlier value. useId stays stable.

axe-core’s documentation of the rule

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