Skip to content

fix: give every dialog an accessible name and description - #981

Merged
petyosi merged 1 commit into
mdx-editor:mainfrom
EvanProgramming:fix/dialog-descriptions
Sep 26, 2026
Merged

petyosi merged 1 commit into
mdx-editor:mainfrom
EvanProgramming:fix/dialog-descriptions

Conversation

@EvanProgramming

Copy link
Copy Markdown
Contributor

Problem

Three dialogs render a Radix Dialog.Content without a Dialog.Description, so Radix logs

Warning: Missing `Description` or `aria-describedby={undefined}` for {DialogContent}.

and screen reader users get no description for the dialog. The shared DialogButton primitive was missing Dialog.Title as well, so the dialogs built on it had no accessible name at all.

This was reported in #792 for the image dialog. Reading the source showed the same defect in the frontmatter dialog and in the shared toolbar dialog primitive, so the fix covers the defect class rather than the single reported dialog.

Root cause

@radix-ui/react-dialog requires an accessible name and description on DialogContent, and warns at runtime when either is missing. Verified in the current tree:

File Title Description
src/plugins/image/ImageDialog.tsx present missing
src/plugins/frontmatter/FrontmatterEditor.tsx present missing
src/plugins/toolbar/primitives/DialogButton.tsx missing missing

The one-line way to silence the warning is aria-describedby={undefined}, but that only mutes the console message — the dialogs remain undescribed for assistive technology, and DialogButton still has no accessible name. This PR closes the accessibility gap itself.

Changes

  • src/styles/ui.module.css — add a .visuallyHidden utility class (standard clip-based visually-hidden pattern) so a description can be rendered for assistive technology without altering the visual design.
  • src/plugins/image/ImageDialog.tsx — add Dialog.Description using a new translatable key uploadImage.dialogDescription.
  • src/plugins/frontmatter/FrontmatterEditor.tsx — add Dialog.Description using a new translatable key frontmatterEditor.dialogDescription.
  • src/plugins/toolbar/primitives/DialogButton.tsx — add a visually hidden Dialog.Title (from the existing tooltipTitle prop) and Dialog.Description (from the existing dialogInputPlaceholder prop), so the primitive names and describes itself from props it already receives.
  • src/test/dialog-a11y.test.tsx — regression test that opens all three dialogs, resolves each by role="dialog" and accessible name, and asserts aria-describedby points at an element carrying the expected description.

No public API change: the DialogButton prop contract is untouched, and the two new translation keys fall back to their English defaults when a consumer provides no translation.

Verification

Red/green on the new test, run with npx vitest --run src/test/dialog-a11y.test.tsx:

  • Before the fix — Tests 3 failed (3). The shared-primitive case failed with Unable to find an accessible element with the role "dialog" and name "Insert YouTube video", independently confirming the missing accessible name.
  • After the fix — Tests 3 passed (3).

Full local gate on the branch:

  • npm run lint — clean
  • npm run typecheck — clean
  • npm run test:once — full suite green (100 passed | 1 skipped), no regressions

Regression risk

Low, and confined to dialog semantics. The descriptions are visually hidden and reuse existing props or default strings, so no rendering or layout changes are expected. The added test guards the wiring against future refactors.

Closes #792

The image, frontmatter and shared toolbar dialogs each rendered a Radix DialogContent without a DialogDescription, so Radix emitted an accessibility warning and screen readers got no description for the dialog. The shared DialogButton primitive was missing DialogTitle as well, which left those dialogs without an accessible name entirely.

Each dialog now carries a visually hidden description, the shared primitive carries a title, and a regression test asserts the name/description wiring on all three dialogs. No public API change.
@EvanProgramming

Copy link
Copy Markdown
Contributor Author

Heads-up: the workflows for this PR have not started. As a first-time contribution from a fork they sit behind maintainer approval ("Approve and run all workflows"), so there is currently no CI signal on the PR either way.

Verified locally in the meantime:

  • New test src/test/dialog-a11y.test.tsx: Tests 3 failed (3) before the fix — including Unable to find an accessible element with the role "dialog" and name "Insert YouTube video" for the DialogButton case — → Tests 3 passed (3) after.
  • Full suite: 100 passed | 1 skipped, no regressions.
  • lint and typecheck clean.

@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 4.3.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

pull Bot pushed a commit to LeeeeeeM/editor that referenced this pull request Sep 26, 2026
The English locale is the reference translators copy from, but it was
missing keys the UI already uses (strikethrough, sub/superscript, the
inline code block language, the dialog close button, the URL-only image
instructions) and the dialog descriptions added in mdx-editor#981. Translations
could not cover strings they never saw.
pull Bot pushed a commit to LeeeeeeM/editor that referenced this pull request Sep 26, 2026
…is set

The dialog description added in mdx-editor#981 always mentioned uploading from the
device, but without an imageUploadHandler the dialog only accepts a URL.
Screen reader users were told about an option that is not there.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] imagePlugin dialog missing aria-describedby / DialogDescription

2 participants