squirrelscan
GitHub

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-name covers elements with role="dialog" or role="alertdialog". It fails with N ARIA dialog(s) without accessible names, up to ten items with a count of the rest. It passes with All N ARIA dialog(s) have accessible names.
  • dialog-name covers 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 with All N native <dialog>(s) have accessible names.
  • aria-dialog-name-unconfirmed and dialog-name-unconfirmed warn with N ARIA dialog(s) without accessible names may be hidden by a style (unconfirmed) and N 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 names and All native <dialog>s in the accessibility tree have accessible names, with hiddenWithoutName beside dialogsChecked.
  • 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#cart for a native dialog, and dialog#settings or alertdialog#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, or div[role="dialog"].modal for 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 IDa11y/aria-dialog-name
CategoryAccessibility
ScopePer-page
Severityerror
Weight7/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.

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.

Type to search…

↑↓ navigate↵ openesc close