Dialog accessible name: role=dialog and the dialog element
A modal with no name leaves screen reader users guessing what just opened. squirrel checks ARIA dialogs and native dialogs separately.
What a dialog name is
A dialog interrupts the page, so a screen reader announces it on open. With no accessible name the announcement is just “dialog”, and the user has to explore to find out what appeared.
The name comes from aria-labelledby pointing at the dialog’s own heading, from aria-label, or from title. Text inside the dialog does not become its name: unlike a button, a dialog’s content is its content, not its label.
What squirrel checks
Two checks, at rule severity error. Both accept the same three name sources: a non-empty aria-label, a non-empty title, or an aria-labelledby pointing at an element that has text.
aria-dialog-namecovers elements withrole="dialog"orrole="alertdialog". It fails withN ARIA dialog(s) without accessible names, up to ten items with a count of the rest. It passes withAll N ARIA dialog(s) have accessible names.dialog-namecovers native<dialog>elements that carry no explicit dialog role. Browsers announce the native element’s role on their own, so a missing name here warns rather than fails:N native <dialog>(s) without accessible names, listed the same way. It passes withAll N native <dialog>(s) have accessible names.aria-dialog-name-unconfirmedanddialog-name-unconfirmedwarn withN ARIA dialog(s) without accessible names may be hidden by a style (unconfirmed)andN native <dialog>(s) without accessible names may be hidden by a style (unconfirmed)for unnamed dialogs a style may hide in a way squirrel cannot settle.- When unnamed dialogs were skipped as hidden, the passes read
All ARIA dialogs in the accessibility tree have accessible namesandAll native <dialog>s in the accessibility tree have accessible names, withhiddenWithoutNamebesidedialogsChecked. - When the page has neither kind, one info check reports
No dialog elements found.
A dialog out of the accessibility tree is not reported: one with the hidden attribute or aria-hidden="true", as a closed modal often carries, 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 one inside a closed <dialog> or an unopened popover. It still needs a name before it opens. A native <dialog> is the exception to its own closed state: pages serve it closed and open it with script, so a closed one is checked as it will be once open. 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 dialog hidden only by the site’s external CSS, such as a Bootstrap .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.
Each unnamed dialog is listed by a selector-like handle, with its start tag (attributes only, capped at 200 characters) as the snippet:
- with an
id:dialog#cartfor a native dialog, anddialog#settingsoralertdialog#confirm(the role, then the id) for an ARIA one; - without one: the tag and up to three of its classes, such as
dialog.newsletter-popup, ordiv[role="dialog"].modalfor an ARIA dialog; - when two dialogs of the same kind on a page get the same handle, the second and later ones carry an ordinal in document order:
dialog.drawer (2). The ordinal counts named dialogs too, so naming one drawer does not renumber the others.
The handle is the item’s identity from one audit to the next, so it depends only on the markup. Renaming a dialog’s id or classes, or reordering same-handle dialogs, gives it a new one.
How to fix it
<div role="dialog" aria-modal="true" aria-labelledby="settings-title">
<h2 id="settings-title">Settings</h2>
...
</div>Point aria-labelledby at the heading the dialog already shows. That keeps the announced name and the visible title in step, which is what a voice control user needs to open and close it by name.
A dialog rendered into the page but never opened is still reported, because the rule reads a static snapshot of the markup. A name your script adds at open time is invisible to it, so keeping the name in the markup is the simplest way to satisfy both the audit and the announcement.
| Rule ID | a11y/aria-dialog-name |
| Category | Accessibility |
| Scope | Per-page |
| Severity | error |
| Weight | 7/10 |
Enable / disable
Disable this rule
[rules]
disable = ["a11y/aria-dialog-name"]Disable all Accessibility rules
[rules]
disable = ["a11y/*"]Enable only this rule
[rules]
enable = ["a11y/aria-dialog-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/aria-hidden-focus: the page behind a modal, hidden but still tabbable.
- a11y/aria-required-attr: states a role must declare, such as
aria-checkedon a checkbox. - a11y/aria-labels: the close button most dialogs put in the corner.
- a11y/frame-title: the same naming problem for embedded frames.
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. ARIA dialogs are reported as errors and native ones as warnings, in separate checks. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.