fix(a11y): expose plain pad lines as paragraphs to assistive tech (#7778) - #8240
JohnMcLear wants to merge 2 commits into
Conversation
) Each pad line renders as <div class="ace-line">, which browsers expose in the accessibility tree as an anonymous `generic` node. Screen readers then flatten the whole pad into a single run of text, so users can't step through it line by line or reach links inside a given line. Give plain lines role="paragraph" in domline's writeHTML. Lines wrapped in block markup (lists, plugin headings via aceDomLine*ProcessLineAttributes) keep their native semantics and get no role. No tag change, so plugin selectors on div.ace-line are unaffected. This is the follow-up step proposed in #7782. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012kA75NPq8nGRidAwhPXeCi
|
ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing |
PR Summary by QodoExpose plain pad lines as accessible paragraphs
AI Description
Diagram
High-Level Assessment
Files changed (2)
|
Code Review by Qodo
1.
|
) Address Qodo review: any non-empty preHtml/postHtml from a line-attribute hook was treated as a block wrapper, so plugins that wrap a line in inline/styling markup silently dropped the line out of paragraph navigation. Check the rendered node for elements that actually carry block semantics (lists, headings, pre, blockquote, table, ...) instead. Adds a jsdom backend test covering plain, list, plugin-heading and plugin-inline-wrapper lines. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012kA75NPq8nGRidAwhPXeCi
Fixes #7778
Root cause
#7782 removed
role="textbox"frominnerdocbody, but @StrangeGirlMurph's VoiceOver re-test afterwards said line-by-line reading still doesn't work. The accessibility tree shows why: every pad line is a<div class="ace-line">with no role, and browsers expose that as an anonymousgenericnode. Screen readers flatten the pad into one run of text, with no line boundaries to step between.I dumped Chromium's accessibility tree for the
ace_innerframe with CDPAccessibility.getFullAXTreeon a 3-line pad with a link on line 2:Before (develop):
After:
Fix
domline.tswriteHTMLnow setsrole="paragraph"on plain lines. A line skips the role only if its rendered markup contains an element with its own block semantics (p,ul,ol,li,dl,h1–h6,pre,blockquote,table,figure,hr), so lists and ep_headings2 headings keep their native roles and a list never sits inside a paragraph. Inline or styling-only wrappers from plugin line-attribute hooks don't count, so those lines stay paragraphs. #7782 had already named this as the next step.div.ace-lineare unaffected. This is not the AT mirror or a shadow DOM approach.optDocumentpath). Export and the non-DOMcreateDomLinepath are unchanged.Tests
New Playwright test
pad lines are exposed to AT as separate paragraphs with navigable links (#7778)ina11y_dialogs.spec.ts:paragraphroles with the right text.linkwhose accessible name is its visible text and whosehrefis correct.listitemholds "Third line".New backend jsdom test
tests/backend/specs/domline_line_role.tscovers four cases: a plain line, a list line, a plugin<h1>wrapper and a plugin inline<span>wrapper. On develop 2 of the 4 fail. After the first commit the inline-wrapper case still failed; that was Qodo's finding. With the second commit all 4 pass.Evidence (local, chromium):
getByRole('paragraph')expected 3, received 0.a11y_dialogs.spec.tspasses, including the Inaccessibility to screenreaders #7255/a11y: Provide an AT-only read view of pad content (line-by-line + link navigation) #7778 regressions. The new test also passes on firefox.domline_list_start.ts). Vitest: 840 passed.tsc --noEmit: no new errors.Still needs a real screen reader
The automated tree now has line boundaries. Only a VoiceOver/NVDA user can confirm that reading line by line and moving between links actually works inside the contenteditable iframe. @StrangeGirlMurph, could you re-test on pad-dev once this is deployed?
Related AT noise I found but left out of this PR: the editor iframe documents are titled
Empty(fromstatic/empty.html), and the frames carry the hardcoded titlesEther/pad. ThePad contentaria-label is hardcoded English. A screen reader may read these out when it enters the editor.🤖 Generated with Claude Code
https://claude.ai/code/session_012kA75NPq8nGRidAwhPXeCi