Skip to content
easeweb
Search topics

Confirmation dialogs that don't read out their question

A delete confirmation has no name and an unlinked message, so a screen reader announces only the Delete project button that focus lands on.

WCAG 2.2
SC 4.1.2ASC 2.4.3A
Required by
Section 508 · EN 301 549 (EU)
Broken <div class="confirm" role="dialog" aria-modal="true">
The accessibility problem illustrated: Focus lands on Delete project, but the title and message are never read.
01

The problem

Screen reader users land on Delete project without hearing what it will delete.

02

Try it

Tab to a Delete button and press Enter, then press Enter again.

Watch which button has focus when the dialog opens, and read what a screen reader would say. After deleting, see where focus is.

Broken Can you fix this?

Projects

  • Spring launch · 24 files
  • Annual report · 9 files
  • Team photos · 132 files

Screen reader says:

(Tab to a Delete button and press Enter)

A recreated demo. The broken version is intentionally inaccessible.

03

The fix

Name the dialog after its title, link the message, start on Cancel, and move focus somewhere real afterwards.

  1. 1Name the dialog with aria-labelledby, pointing at its title.
  2. 2Link the message with aria-describedby, and start focus on Cancel.
  3. 3Once the item is gone, move focus to something that is still on the page.
Before A confirmation whose question is never read
<div id="page">  <h2>Projects</h2>  <ul>    <li data-name="Spring launch" data-files="24">      Spring launch · 24 files <button type="button" class="delete">Delete</button>    </li>    …  </ul></div> <div class="confirm" role="dialog" aria-modal="true" hidden>  <h2 class="confirm-title">Delete “Spring launch”?</h2>  <p class="confirm-message">This deletes the project and its 24 files. It can't be undone.</p>  <button type="button" class="cancel">Cancel</button>  <button type="button" class="ok">Delete project</button></div>
After

A native dialog

<div id="page">  <h2 id="projects-heading" tabindex="-1">Projects</h2>  <ul>    <li data-name="Spring launch" data-files="24">      Spring launch · 24 files <button type="button" class="delete">Delete</button>    </li>    …  </ul></div> <dialog id="confirm-delete" role="alertdialog"  aria-labelledby="confirm-title" aria-describedby="confirm-message">  <form method="dialog">    <h2 id="confirm-title">Delete “Spring launch”?</h2>    <p id="confirm-message">This deletes the project and its 24 files. It can't be undone.</p>    <button value="cancel" autofocus>Cancel</button>    <button value="delete">Delete project</button>  </form></dialog>

Why this fix: Use native HTML first

Fixing this with an AI coding assistant? Get this guide as Markdown

04

Verify the fix

5 checks, no mouse.

Test with: Keyboard, Screen reader, Zoom, Forced colors

0 of 5 checked

Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the shared dialog component changes.

Limits

This is a starting pattern, not a guarantee for every application. The snippets delete only from the page and leave out the request to the server, its errors, and what happens if it fails. Screen readers differ in how much of an alert dialog they read on their own, so test the complete journey with your target assistive technology.

This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.

Keep this fixed as your code changes.

Turn guides like this into rules your coding agents and CI follow, then have the result retested with a keyboard and screen readers.

How retests work