Skip to content

feat: add accessibility ARIA attributes in Ace2Editor.init()#7350

Closed
deepshekhardas wants to merge 2 commits intoether:developfrom
deepshekhardas:fix/accessibility-aria
Closed

feat: add accessibility ARIA attributes in Ace2Editor.init()#7350
deepshekhardas wants to merge 2 commits intoether:developfrom
deepshekhardas:fix/accessibility-aria

Conversation

@deepshekhardas
Copy link
Copy Markdown

Summary

Screen readers currently struggle with Etherpad because the editor iframes and content areas lack proper ARIA attributes. Users relying on assistive technology can only cycle through line numbers with no way to read or interact with the actual pad content.

This PR adds appropriate ARIA roles and labels to improve screenreader navigation.

Closes #7255

Changes

  • Added role="application" and aria-label="Etherpad editor" to the outer iframe
  • Added aria-hidden="true" to #sidediv (line numbers) to hide from screenreaders
  • Added role="document" and aria-label="Pad content" to the inner iframe
  • Added role="textbox", aria-multiline="true", and aria-label="Pad content" to #innerdocbody

Testing

  • Inspected DOM elements in Chrome DevTools to verify ARIA attributes are present
  • Verified screenreader navigation announces editor content correctly
  • Verified line numbers are correctly hidden from assistive technology

This is a focused split from #7341 as discussed with maintainers.

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

Review Summary by Qodo

Add accessibility ARIA attributes to improve screenreader support

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add ARIA attributes to outer iframe for screenreader support
• Hide line numbers from assistive technology with aria-hidden
• Add ARIA attributes to inner iframe and content area
• Fix code formatting and indentation throughout file
Diagram
flowchart LR
  A["Outer iframe"] -->|role=application<br/>aria-label| B["Etherpad editor"]
  C["Line numbers div"] -->|aria-hidden=true| D["Hidden from screenreaders"]
  E["Inner iframe"] -->|role=document<br/>aria-label| F["Pad content"]
  G["Content body"] -->|role=textbox<br/>aria-multiline| H["Pad content"]
Loading

Grey Divider

File Changes

1. src/static/js/ace.ts ✨ Enhancement +26/-18

Add ARIA attributes for accessibility

• Added role="application" and aria-label="Etherpad editor" to outer iframe element
• Added aria-hidden="true" to sidediv element to hide line numbers from screenreaders
• Added role="document" and aria-label="Pad content" to inner iframe element
• Added role="textbox", aria-multiline="true", and aria-label="Pad content" to innerdocbody
 element
• Fixed code formatting and indentation throughout the file for consistency

src/static/js/ace.ts


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects bot commented Mar 4, 2026

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (5) 📎 Requirement gaps (1)

Grey Divider


Action required

1. accessibilityAttributes default true 📘 Rule violation ⛯ Reliability ⭐ New
Description
The new accessibility behavior is enabled by default, changing runtime behavior without an opt-in
flag defaulting to disabled. Additionally, some ARIA behavior (aria-hidden on #sidediv) is
applied even when the flag is disabled.
Code

src/node/utils/Settings.ts[R407-410]

    alwaysShowChat: false,
    chatAndUsers: false,
    lang: null,
+    accessibilityAttributes: true,
Evidence
Compliance ID 6 requires new functionality to be behind a feature flag and disabled by default; the
PR introduces accessibilityAttributes but sets it to true in defaults and template, and also
applies at least one accessibility attribute unconditionally in the editor DOM setup.

src/node/utils/Settings.ts[407-410]
settings.json.template[264-266]
src/static/js/ace.ts[229-233]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR introduces a new `accessibilityAttributes` behavior but enables it by default and also applies at least one ARIA-related attribute unconditionally, which violates the requirement that new features must be feature-flagged and disabled by default.

## Issue Context
A new config option (`padOptions.accessibilityAttributes`) is added and used to add ARIA attributes, but the defaults/template set it to `true` and `#sidediv` is always marked `aria-hidden=&quot;true&quot;`.

## Fix Focus Areas
- src/node/utils/Settings.ts[407-410]
- settings.json.template[264-266]
- src/static/js/ace.ts[229-233]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. No skip-to-content link 📎 Requirement gap ✓ Correctness ⭐ New
Description
The PR adds ARIA roles/labels but does not add a discoverable skip-to-content mechanism that moves
focus to the main editor/content area. Screen reader and keyboard users still lack a direct way to
bypass surrounding UI to reach the pad content.
Code

src/static/js/ace.ts[R187-196]

    outerFrame.name = 'ace_outer';
    outerFrame.frameBorder = 0; // for IE
    outerFrame.title = 'Ether';
+    if (clientVars.padOptions.accessibilityAttributes) {
+      outerFrame.setAttribute('role', 'application');
+      outerFrame.setAttribute('aria-label', html10n.get('pad.aria.editor'));
+    }
    // Some browsers do strange things unless the iframe has a src or srcdoc property:
    //   - Firefox replaces the frame's contentWindow.document object with a different object after
    //     the frame is created. This can be worked around by waiting for the window's load event
Evidence
Compliance ID 2 requires a skip-to-content mechanism; the updated editor initialization shows ARIA
attributes being added to frames and #sidediv, but no skip link or focus-jump control is created
as part of the accessible navigation improvements.

Provide a skip-to-content mechanism for screen reader users
src/static/js/ace.ts[186-193]
src/static/js/ace.ts[229-233]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
There is no skip-to-content mechanism for screen reader/keyboard users to jump directly to the main pad content.

## Issue Context
The PR adds ARIA roles/labels to the editor iframes and content body but does not introduce a skip link or equivalent focus-jump mechanism.

## Fix Focus Areas
- src/static/js/ace.ts[186-233]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Tabs added in qqq.json 📘 Rule violation ✓ Correctness ⭐ New
Description
New localization entries are indented with tab characters, violating the no-tabs / 2-space
indentation style requirement. This can cause inconsistent formatting and diff churn.
Code

src/locales/qqq.json[R90-91]

+	"pad.aria.editor": "ARIA label for the outer iframe that contains the editor.",
+	"pad.aria.pad_content": "ARIA label for the inner iframe and the content body of the pad.",
Evidence
Compliance ID 8 forbids tabs and requires consistent 2-space indentation; the added lines use tab
indentation.

src/locales/qqq.json[90-91]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Tabs were introduced in `src/locales/qqq.json`, violating the repository style requirement.

## Issue Context
The new `pad.aria.*` localization documentation strings are tab-indented.

## Fix Focus Areas
- src/locales/qqq.json[90-91]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (4)
4. Tests use 4-space indent 📘 Rule violation ✓ Correctness ⭐ New
Description
New test files use 4-space indentation instead of the required 2-space indentation. This violates
the code style compliance rule.
Code

src/tests/frontend-new/specs/accessibility.spec.ts[R3-9]

+test.describe('Accessibility ARIA attributes', () => {
+    test.beforeEach(async ({ page }) => {
+        // Navigate to a new pad
+        await page.goto('http://localhost:9001/p/accessibility-test');
+        // Wait for the pad to load
+        await expect(page.locator('#editorcontainer')).toHaveClass(/initialized/);
+    });
Evidence
Compliance ID 8 requires 2-space indentation; the newly added test code is indented with 4 spaces.

src/tests/frontend-new/specs/accessibility.spec.ts[3-9]
src/tests/frontend/specs/accessibility.js[3-7]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new accessibility tests are formatted with 4-space indentation instead of the required 2-space indentation.

## Issue Context
Both the Playwright test and the legacy frontend test added in this PR use 4-space indentation.

## Fix Focus Areas
- src/tests/frontend-new/specs/accessibility.spec.ts[3-29]
- src/tests/frontend/specs/accessibility.js[3-28]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. html10n import interop 🐞 Bug ✓ Correctness ⭐ New
Description
ace.ts uses require('.../vendors/html10n') and then calls html10n.get(...), but html10n is a
default export so require() will typically return an object with a .default property. This can
throw at runtime when ARIA attributes are enabled, breaking editor initialization.
Code

src/static/js/ace.ts[R190-193]

+    if (clientVars.padOptions.accessibilityAttributes) {
+      outerFrame.setAttribute('role', 'application');
+      outerFrame.setAttribute('aria-label', html10n.get('pad.aria.editor'));
+    }
Evidence
ace.ts calls html10n.get(...) after importing html10n via bare require(), but the vendor
module is a TS default export (export default html10n), which is normally accessed as
require(...).default (a pattern used elsewhere in the repo). Without .default, html10n.get can
be undefined and the ARIA-label setAttribute path will crash.

src/static/js/ace.ts[28-36]
src/static/js/ace.ts[186-193]
src/static/js/vendors/html10n.ts[992-1003]
src/node/hooks/express/openapi.ts[21-23]
src/tsconfig.json[7-11]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`src/static/js/ace.ts` uses a bare CommonJS `require()` to import `vendors/html10n`, but the module is a TypeScript **default export**. As a result, `html10n.get` may be undefined at runtime, causing `Ace2Editor.init()` to throw when ARIA attributes are enabled.

### Issue Context
- `src/static/js/vendors/html10n.ts` exports `default`.
- Elsewhere in the repo, default exports imported via `require()` use `.default`.

### Fix Focus Areas
- src/static/js/ace.ts[28-36]
- src/static/js/ace.ts[186-193]
- src/static/js/ace.ts[243-249]
- src/static/js/ace.ts[295-302]

### Suggested change
- Change the import to one of:
 - `const html10n = require(&#x27;ep_etherpad-lite/static/js/vendors/html10n&#x27;).default;`
 - or `import html10n from &#x27;./vendors/html10n&#x27;;` (if relative import works with your bundling)
- When setting ARIA labels, consider:
 - `const label = html10n.get(&#x27;pad.aria.editor&#x27;) || &#x27;Etherpad editor&#x27;;`
 - similarly for `pad.aria.pad_content`

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Hardcoded Playwright URL 🐞 Bug ⛯ Reliability ⭐ New
Description
The new Playwright test navigates to http://localhost:9001/... directly instead of using the
shared helper/baseURL conventions used by other frontend-new specs. This is likely to fail in CI or
in environments that run Etherpad on a different host/port.
Code

src/tests/frontend-new/specs/accessibility.spec.ts[R4-9]

+    test.beforeEach(async ({ page }) => {
+        // Navigate to a new pad
+        await page.goto('http://localhost:9001/p/accessibility-test');
+        // Wait for the pad to load
+        await expect(page.locator('#editorcontainer')).toHaveClass(/initialized/);
+    });
Evidence
Most frontend-new tests use helper utilities such as goToNewPad(page) (which typically respects
configured baseURL and handles pad creation). Hardcoding localhost:9001 bypasses that and makes the
test environment-dependent.

src/tests/frontend-new/specs/accessibility.spec.ts[4-9]
src/tests/frontend-new/specs/bold.spec.ts[1-8]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The new Playwright spec hardcodes `http://localhost:9001`, which can break when the test runner uses a different baseURL/port.

### Issue Context
Other `src/tests/frontend-new/specs/*.spec.ts` files use `goToNewPad(page)` from `../helper/padHelper`.

### Fix Focus Areas
- src/tests/frontend-new/specs/accessibility.spec.ts[4-9]

### Suggested change
- Replace the hardcoded `page.goto(&#x27;http://localhost:9001/...&#x27;)` with either:
 - `await goToNewPad(page);` (preferred, consistent with suite)
 - or `await page.goto(&#x27;/p/accessibility-test&#x27;);` if baseURL is configured
- (Optional hardening) Instead of asserting exact English strings, assert `aria-label` is present and non-empty, or set the language cookie explicitly to ensure deterministic strings.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Ace2Editor.init() missing ARIA test 📘 Rule violation ⛯ Reliability
Description
This PR changes editor accessibility behavior by adding ARIA attributes but does not add a
regression test that would fail if the fix were reverted. Without automated coverage, the screen
reader fix can regress unnoticed.
Code

src/static/js/ace.ts[R189-193]

+    outerFrame.setAttribute('role', 'application');
+    outerFrame.setAttribute('aria-label', 'Etherpad editor');
  // Some browsers do strange things unless the iframe has a src or srcdoc property:
  //   - Firefox replaces the frame's contentWindow.document object with a different object after
  //     the frame is created. This can be worked around by waiting for the window's load event
Evidence
PR Compliance ID 2 requires a regression test for bug fixes. The PR description states this change
fixes a screen reader accessibility issue, and the changed code adds ARIA attributes in
Ace2Editor.init() without any corresponding test changes in the provided diff.

src/static/js/ace.ts[185-195]
src/static/js/ace.ts[226-230]
src/static/js/ace.ts[240-245]
src/static/js/ace.ts[290-296]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
This PR fixes a screen reader accessibility bug by adding ARIA attributes, but it does not include a regression test to ensure the ARIA attributes remain present.
## Issue Context
Per compliance requirements, bug fixes must include a test that fails if the fix is reverted.
## Fix Focus Areas
- src/static/js/ace.ts[185-296]
- src/tests/frontend-new/specs[1-999999]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

8. ARIA not feature-flagged📘 Rule violation ✓ Correctness
Description
New ARIA roles/labels are applied unconditionally in Ace2Editor.init() with no feature flag or
default-off toggle. This violates the requirement that new features be gated and disabled by
default.
Code

src/static/js/ace.ts[R189-190]

+    outerFrame.setAttribute('role', 'application');
+    outerFrame.setAttribute('aria-label', 'Etherpad editor');
Evidence
PR Compliance ID 6 requires new features to be implemented behind a feature flag and disabled by
default. The new ARIA attributes are set unconditionally during init with no conditional or flag
check.

src/static/js/ace.ts[188-190]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New ARIA attribute behavior is enabled unconditionally, but compliance requires new features to be behind a feature flag and disabled by default.
## Issue Context
Consider introducing a settings/clientVars flag (default `false`) and wrapping the `setAttribute()` calls so disabling the flag yields the same DOM attributes as before this PR.
## Fix Focus Areas
- src/static/js/ace.ts[185-296]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. ARIA labels bypass i18n🐞 Bug ✓ Correctness
Description
Ace2Editor.init() introduces hard-coded English aria-label strings, so assistive-technology
announcements won’t respect Etherpad’s localization (including runtime language changes). This is an
accessibility/i18n consistency regression for non-English users.
Code

src/static/js/ace.ts[R293-295]

+    innerDocument.body.setAttribute('role', 'textbox');
+    innerDocument.body.setAttribute('aria-multiline', 'true');
+    innerDocument.body.setAttribute('aria-label', 'Pad content');
Evidence
The PR sets aria-label values directly in code (English literals). Etherpad has a localization
system (html10n) that supports runtime language changes, and it even sets aria-label automatically
when translating elements, indicating that accessibility labels are intended to be localizable when
user-facing.

src/static/js/ace.ts[185-295]
src/static/js/pad.ts[142-148]
src/static/js/vendors/html10n.ts[641-667]
src/static/js/timeslider.ts[164-167]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New `aria-label` values for the editor iframes/body are hard-coded English strings. Etherpad supports localization and runtime language changes, so these accessibility announcements should come from locale files and be refreshed when the language changes.
## Issue Context
- `pad.ts` supports switching language at runtime via `html10n.localize([val, &amp;amp;#x27;en&amp;amp;#x27;])`.
- `html10n.translateElement()` also sets `aria-label` from localized strings, so accessibility labels are part of the i18n story.
## Fix Focus Areas
- src/static/js/ace.ts[185-296]
- src/locales/en.json[36-110]
- src/locales/en-gb.json[1-120]
(Then add equivalent keys to other locales as needed, or ensure a fallback strategy.)

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Grey Divider

Previous review results

Review updated until commit ef0264e

Results up to commit N/A


🐞 Bugs (1) 📘 Rule violations (2) 📎 Requirement gaps (0)

Grey Divider
Action required
1. Ace2Editor.init() missing ARIA test 📘 Rule violation ⛯ Reliability
Description
This PR changes editor accessibility behavior by adding ARIA attributes but does not add a
regression test that would fail if the fix were reverted. Without automated coverage, the screen
reader fix can regress unnoticed.
Code

src/static/js/ace.ts[R189-193]

+    outerFrame.setAttribute('role', 'application');
+    outerFrame.setAttribute('aria-label', 'Etherpad editor');
   // Some browsers do strange things unless the iframe has a src or srcdoc property:
   //   - Firefox replaces the frame's contentWindow.document object with a different object after
   //     the frame is created. This can be worked around by waiting for the window's load event
Evidence
PR Compliance ID 2 requires a regression test for bug fixes. The PR description states this change
fixes a screen reader accessibility issue, and the changed code adds ARIA attributes in
Ace2Editor.init() without any corresponding test changes in the provided diff.

src/static/js/ace.ts[185-195]
src/static/js/ace.ts[226-230]
src/static/js/ace.ts[240-245]
src/static/js/ace.ts[290-296]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
This PR fixes a screen reader accessibility bug by adding ARIA attributes, but it does not include a regression test to ensure the ARIA attributes remain present.
## Issue Context
Per compliance requirements, bug fixes must include a test that fails if the fix is reverted.
## Fix Focus Areas
- src/static/js/ace.ts[185-296]
- src/tests/frontend-new/specs[1-999999]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
2. ARIA not feature-flagged📘 Rule violation ✓ Correctness
Description
New ARIA roles/labels are applied unconditionally in Ace2Editor.init() with no feature flag or
default-off toggle. This violates the requirement that new features be gated and disabled by
default.
Code

src/static/js/ace.ts[R189-190]

+    outerFrame.setAttribute('role', 'application');
+    outerFrame.setAttribute('aria-label', 'Etherpad editor');
Evidence
PR Compliance ID 6 requires new features to be implemented behind a feature flag and disabled by
default. The new ARIA attributes are set unconditionally during init with no conditional or flag
check.

src/static/js/ace.ts[188-190]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New ARIA attribute behavior is enabled unconditionally, but compliance requires new features to be behind a feature flag and disabled by default.
## Issue Context
Consider introducing a settings/clientVars flag (default `false`) and wrapping the `setAttribute()` calls so disabling the flag yields the same DOM attributes as before this PR.
## Fix Focus Areas
- src/static/js/ace.ts[185-296]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. ARIA labels bypass i18n🐞 Bug ✓ Correctness
Description
Ace2Editor.init() introduces hard-coded English aria-label strings, so assistive-technology
announcements won’t respect Etherpad’s localization (including runtime language changes). This is an
accessibility/i18n consistency regression for non-English users.
Code

src/static/js/ace.ts[R293-295]

+    innerDocument.body.setAttribute('role', 'textbox');
+    innerDocument.body.setAttribute('aria-multiline', 'true');
+    innerDocument.body.setAttribute('aria-label', 'Pad content');
Evidence
The PR sets aria-label values directly in code (English literals). Etherpad has a localization
system (html10n) that supports runtime language changes, and it even sets aria-label automatically
when translating elements, indicating that accessibility labels are intended to be localizable when
user-facing.

src/static/js/ace.ts[185-295]
src/static/js/pad.ts[142-148]
src/static/js/vendors/html10n.ts[641-667]
src/static/js/timeslider.ts[164-167]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New `aria-label` values for the editor iframes/body are hard-coded English strings. Etherpad supports localization and runtime language changes, so these accessibility announcements should come from locale files and be refreshed when the language changes.
## Issue Context
- `pad.ts` supports switching language at runtime via `html10n.localize([val, &amp;#x27;en&amp;#x27;])`.
- `html10n.translateElement()` also sets `aria-label` from localized strings, so accessibility labels are part of the i18n story.
## Fix Focus Areas
- src/static/js/ace.ts[185-296]
- src/locales/en.json[36-110]
- src/locales/en-gb.json[1-120]
(Then add equivalent keys to other locales as needed, or ensure a fallback strategy.)

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider Grey Divider

Qodo Logo

Comment on lines 189 to 193
outerFrame.setAttribute('role', 'application');
outerFrame.setAttribute('aria-label', 'Etherpad editor');
// Some browsers do strange things unless the iframe has a src or srcdoc property:
// - Firefox replaces the frame's contentWindow.document object with a different object after
// the frame is created. This can be worked around by waiting for the window's load event
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. ace2editor.init() missing aria test 📘 Rule violation ⛯ Reliability

This PR changes editor accessibility behavior by adding ARIA attributes but does not add a
regression test that would fail if the fix were reverted. Without automated coverage, the screen
reader fix can regress unnoticed.
Agent Prompt
## Issue description
This PR fixes a screen reader accessibility bug by adding ARIA attributes, but it does not include a regression test to ensure the ARIA attributes remain present.

## Issue Context
Per compliance requirements, bug fixes must include a test that fails if the fix is reverted.

## Fix Focus Areas
- src/static/js/ace.ts[185-296]
- src/tests/frontend-new/specs[1-999999]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@JohnMcLear
Copy link
Copy Markdown
Member

There ye go @deepshekhardas - our slop machine is fighting yours ;) Please take action based on the feedback

@JohnMcLear
Copy link
Copy Markdown
Member

/review

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects bot commented Mar 5, 2026

Persistent review updated to latest commit ef0264e

Comment on lines 407 to +410
alwaysShowChat: false,
chatAndUsers: false,
lang: null,
accessibilityAttributes: true,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. accessibilityattributes default true 📘 Rule violation ⛯ Reliability

The new accessibility behavior is enabled by default, changing runtime behavior without an opt-in
flag defaulting to disabled. Additionally, some ARIA behavior (aria-hidden on #sidediv) is
applied even when the flag is disabled.
Agent Prompt
## Issue description
The PR introduces a new `accessibilityAttributes` behavior but enables it by default and also applies at least one ARIA-related attribute unconditionally, which violates the requirement that new features must be feature-flagged and disabled by default.

## Issue Context
A new config option (`padOptions.accessibilityAttributes`) is added and used to add ARIA attributes, but the defaults/template set it to `true` and `#sidediv` is always marked `aria-hidden="true"`.

## Fix Focus Areas
- src/node/utils/Settings.ts[407-410]
- settings.json.template[264-266]
- src/static/js/ace.ts[229-233]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines 187 to 196
outerFrame.name = 'ace_outer';
outerFrame.frameBorder = 0; // for IE
outerFrame.title = 'Ether';
if (clientVars.padOptions.accessibilityAttributes) {
outerFrame.setAttribute('role', 'application');
outerFrame.setAttribute('aria-label', html10n.get('pad.aria.editor'));
}
// Some browsers do strange things unless the iframe has a src or srcdoc property:
// - Firefox replaces the frame's contentWindow.document object with a different object after
// the frame is created. This can be worked around by waiting for the window's load event
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. No skip-to-content link 📎 Requirement gap ✓ Correctness

The PR adds ARIA roles/labels but does not add a discoverable skip-to-content mechanism that moves
focus to the main editor/content area. Screen reader and keyboard users still lack a direct way to
bypass surrounding UI to reach the pad content.
Agent Prompt
## Issue description
There is no skip-to-content mechanism for screen reader/keyboard users to jump directly to the main pad content.

## Issue Context
The PR adds ARIA roles/labels to the editor iframes and content body but does not introduce a skip link or equivalent focus-jump mechanism.

## Fix Focus Areas
- src/static/js/ace.ts[186-233]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +90 to +91
"pad.aria.editor": "ARIA label for the outer iframe that contains the editor.",
"pad.aria.pad_content": "ARIA label for the inner iframe and the content body of the pad.",
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Tabs added in qqq.json 📘 Rule violation ✓ Correctness

New localization entries are indented with tab characters, violating the no-tabs / 2-space
indentation style requirement. This can cause inconsistent formatting and diff churn.
Agent Prompt
## Issue description
Tabs were introduced in `src/locales/qqq.json`, violating the repository style requirement.

## Issue Context
The new `pad.aria.*` localization documentation strings are tab-indented.

## Fix Focus Areas
- src/locales/qqq.json[90-91]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +3 to +9
test.describe('Accessibility ARIA attributes', () => {
test.beforeEach(async ({ page }) => {
// Navigate to a new pad
await page.goto('http://localhost:9001/p/accessibility-test');
// Wait for the pad to load
await expect(page.locator('#editorcontainer')).toHaveClass(/initialized/);
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

4. Tests use 4-space indent 📘 Rule violation ✓ Correctness

New test files use 4-space indentation instead of the required 2-space indentation. This violates
the code style compliance rule.
Agent Prompt
## Issue description
The new accessibility tests are formatted with 4-space indentation instead of the required 2-space indentation.

## Issue Context
Both the Playwright test and the legacy frontend test added in this PR use 4-space indentation.

## Fix Focus Areas
- src/tests/frontend-new/specs/accessibility.spec.ts[3-29]
- src/tests/frontend/specs/accessibility.js[3-28]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +190 to +193
if (clientVars.padOptions.accessibilityAttributes) {
outerFrame.setAttribute('role', 'application');
outerFrame.setAttribute('aria-label', html10n.get('pad.aria.editor'));
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

5. Html10n import interop 🐞 Bug ✓ Correctness

ace.ts uses require('.../vendors/html10n') and then calls html10n.get(...), but html10n is a
default export so require() will typically return an object with a .default property. This can
throw at runtime when ARIA attributes are enabled, breaking editor initialization.
Agent Prompt
### Issue description
`src/static/js/ace.ts` uses a bare CommonJS `require()` to import `vendors/html10n`, but the module is a TypeScript **default export**. As a result, `html10n.get` may be undefined at runtime, causing `Ace2Editor.init()` to throw when ARIA attributes are enabled.

### Issue Context
- `src/static/js/vendors/html10n.ts` exports `default`.
- Elsewhere in the repo, default exports imported via `require()` use `.default`.

### Fix Focus Areas
- src/static/js/ace.ts[28-36]
- src/static/js/ace.ts[186-193]
- src/static/js/ace.ts[243-249]
- src/static/js/ace.ts[295-302]

### Suggested change
- Change the import to one of:
  - `const html10n = require('ep_etherpad-lite/static/js/vendors/html10n').default;`
  - or `import html10n from './vendors/html10n';` (if relative import works with your bundling)
- When setting ARIA labels, consider:
  - `const label = html10n.get('pad.aria.editor') || 'Etherpad editor';`
  - similarly for `pad.aria.pad_content`

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +4 to +9
test.beforeEach(async ({ page }) => {
// Navigate to a new pad
await page.goto('http://localhost:9001/p/accessibility-test');
// Wait for the pad to load
await expect(page.locator('#editorcontainer')).toHaveClass(/initialized/);
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

6. Hardcoded playwright url 🐞 Bug ⛯ Reliability

The new Playwright test navigates to http://localhost:9001/... directly instead of using the
shared helper/baseURL conventions used by other frontend-new specs. This is likely to fail in CI or
in environments that run Etherpad on a different host/port.
Agent Prompt
### Issue description
The new Playwright spec hardcodes `http://localhost:9001`, which can break when the test runner uses a different baseURL/port.

### Issue Context
Other `src/tests/frontend-new/specs/*.spec.ts` files use `goToNewPad(page)` from `../helper/padHelper`.

### Fix Focus Areas
- src/tests/frontend-new/specs/accessibility.spec.ts[4-9]

### Suggested change
- Replace the hardcoded `page.goto('http://localhost:9001/...')` with either:
  - `await goToNewPad(page);` (preferred, consistent with suite)
  - or `await page.goto('/p/accessibility-test');` if baseURL is configured
- (Optional hardening) Instead of asserting exact English strings, assert `aria-label` is present and non-empty, or set the language cookie explicitly to ensure deterministic strings.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@JohnMcLear
Copy link
Copy Markdown
Member

@deepshekhardas are you going to be able to maintain these changes should they require input?

@SamTV12345
Copy link
Copy Markdown
Member

@deepshekhardas Because you are still spamming other repositories I believe you are not a real user. You do not reply to our suggestions. Please check our guidelines for using AI.

@SamTV12345 SamTV12345 closed this Mar 8, 2026
@SamTV12345
Copy link
Copy Markdown
Member

@deepshekhardas Because you are still spamming other repositories I believe you are not a real user. You do not reply to our suggestions. Please check our guidelines for using AI.

@SamTV12345 SamTV12345 reopened this Mar 8, 2026
@SamTV12345 SamTV12345 closed this Mar 8, 2026
@qodo-free-for-open-source-projects
Copy link
Copy Markdown

Review Summary by Qodo

Add ARIA accessibility attributes with feature flag and i18n support

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add ARIA accessibility attributes to editor iframes and content areas
• Implement feature flag accessibilityAttributes in settings (enabled by default)
• Add i18n support with localized labels for screen readers
• Include comprehensive test coverage for accessibility features
Diagram
flowchart LR
  A["Settings Configuration"] -->|accessibilityAttributes flag| B["Ace2Editor.init()"]
  B -->|Apply ARIA attributes| C["Outer iframe<br/>role=application"]
  B -->|Apply ARIA attributes| D["Inner iframe<br/>role=document"]
  B -->|Apply ARIA attributes| E["innerdocbody<br/>role=textbox"]
  F["i18n Localization<br/>html10n"] -->|Provide labels| C
  F -->|Provide labels| D
  F -->|Provide labels| E
  G["Test Suites"] -->|Verify attributes| H["Frontend Tests"]
Loading

Grey Divider

File Changes

1. src/node/utils/Settings.ts ⚙️ Configuration changes +329/-327

Add accessibility feature flag to settings configuration

• Add accessibilityAttributes: boolean field to padOptions type definition (default: true)
• Update default settings object to include accessibilityAttributes: true
• Extensive code formatting and indentation fixes throughout the file (spaces around imports,
 consistent indentation)

src/node/utils/Settings.ts


2. src/static/js/ace.ts ✨ Enhancement +33/-18

Implement ARIA attributes in editor initialization

• Import html10n module for internationalization support
• Add conditional ARIA attributes to outer iframe when feature is enabled (role="application",
 aria-label)
• Add aria-hidden="true" to sidediv (line numbers) to hide from screen readers
• Add conditional ARIA attributes to inner iframe (role="document", aria-label)
• Add conditional ARIA attributes to innerdocbody (role="textbox", aria-multiline="true",
 aria-label)
• Minor code formatting fixes (spacing consistency)

src/static/js/ace.ts


3. src/tests/frontend-new/specs/accessibility.spec.ts 🧪 Tests +30/-0

Add Playwright accessibility test suite

• Create new Playwright-based test suite for accessibility features
• Test ARIA attributes on outer iframe (role, aria-label)
• Test ARIA attributes on inner iframe (role, aria-label)
• Test ARIA attributes on innerdocbody (role, aria-multiline, aria-label)
• Verify attributes are present by default

src/tests/frontend-new/specs/accessibility.spec.ts


View more (4)
4. src/tests/frontend/specs/accessibility.js 🧪 Tests +29/-0

Add frontend accessibility test suite

• Create new frontend test suite for accessibility ARIA attributes
• Test outer frame has correct role and aria-label
• Test inner frame has correct role and aria-label
• Test innerdocbody has correct role, aria-multiline, and aria-label
• Verify all attributes are present when feature is enabled

src/tests/frontend/specs/accessibility.js


5. settings.json.template ⚙️ Configuration changes +3/-1

Add accessibility setting to template configuration

• Add accessibilityAttributes: true setting to padOptions section
• Add comment explaining the purpose of the accessibility attributes setting

settings.json.template


6. src/locales/en.json 📝 Documentation +5/-28

Add English localization for ARIA labels

• Add two new i18n keys: pad.aria.editor ("Etherpad editor") and pad.aria.pad_content ("Pad
 content")
• Remove extra blank lines for formatting consistency
• Fix trailing newline at end of file

src/locales/en.json


7. src/locales/qqq.json 📝 Documentation +3/-1

Add translator documentation for ARIA labels

• Add documentation for pad.aria.editor key explaining it's the ARIA label for outer iframe
• Add documentation for pad.aria.pad_content key explaining it's the ARIA label for inner iframe
 and content body
• Fix trailing newline at end of file

src/locales/qqq.json


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects bot commented Mar 8, 2026

Code Review by Qodo

🐞 Bugs (7) 📘 Rule violations (7) 📎 Requirement gaps (1)

Grey Divider


Action required

1. Flag not fully respected 🐞 Bug ✓ Correctness ⭐ New
Description
The new accessibilityAttributes flag is meant to toggle ARIA accessibility attributes, but #sidediv
is always set to aria-hidden="true" regardless of the flag. This makes the toggle incomplete and
prevents fully disabling the accessibility behavior.
Code

src/static/js/ace.ts[232]

+    sideDiv.setAttribute('aria-hidden', 'true');
Evidence
settings.json.template documents accessibilityAttributes as an enable/disable toggle, but
Ace2Editor.init() unconditionally applies aria-hidden to the sidediv element regardless of that
flag.

settings.json.template[253-267]
src/static/js/ace.ts[226-233]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`padOptions.accessibilityAttributes` is intended to enable/disable ARIA accessibility attributes, but `#sidediv` is always marked `aria-hidden=&quot;true&quot;`, even when the flag is disabled.

### Issue Context
This makes the setting ineffective for users who want to fully disable the ARIA changes.

### Fix Focus Areas
- src/static/js/ace.ts[226-233]
- settings.json.template[253-267]

### Suggested change
Wrap the `aria-hidden` assignment with the same `clientVars.padOptions.accessibilityAttributes` conditional used for the other ARIA attributes, and optionally remove the attribute when disabled to avoid stale state.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. accessibilityAttributes default true 📘 Rule violation ⛯ Reliability
Description
The new accessibility behavior is enabled by default, changing runtime behavior without an opt-in
flag defaulting to disabled. Additionally, some ARIA behavior (aria-hidden on #sidediv) is
applied even when the flag is disabled.
Code

src/node/utils/Settings.ts[R407-410]

   alwaysShowChat: false,
   chatAndUsers: false,
   lang: null,
+    accessibilityAttributes: true,
Evidence
Compliance ID 6 requires new functionality to be behind a feature flag and disabled by default; the
PR introduces accessibilityAttributes but sets it to true in defaults and template, and also
applies at least one accessibility attribute unconditionally in the editor DOM setup.

src/node/utils/Settings.ts[407-410]
settings.json.template[264-266]
src/static/js/ace.ts[229-233]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR introduces a new `accessibilityAttributes` behavior but enables it by default and also applies at least one ARIA-related attribute unconditionally, which violates the requirement that new features must be feature-flagged and disabled by default.
## Issue Context
A new config option (`padOptions.accessibilityAttributes`) is added and used to add ARIA attributes, but the defaults/template set it to `true` and `#sidediv` is always marked `aria-hidden=&amp;quot;true&amp;quot;`.
## Fix Focus Areas
- src/node/utils/Settings.ts[407-410]
- settings.json.template[264-266]
- src/static/js/ace.ts[229-233]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Ace2Editor.init() missing ARIA test 📘 Rule violation ⛯ Reliability
Description
This PR changes editor accessibility behavior by adding ARIA attributes but does not add a
regression test that would fail if the fix were reverted. Without automated coverage, the screen
reader fix can regress unnoticed.
Code

src/static/js/ace.ts[R189-193]

+    outerFrame.setAttribute('role', 'application');
+    outerFrame.setAttribute('aria-label', 'Etherpad editor');
  // Some browsers do strange things unless the iframe has a src or srcdoc property:
  //   - Firefox replaces the frame's contentWindow.document object with a different object after
  //     the frame is created. This can be worked around by waiting for the window's load event
Evidence
PR Compliance ID 2 requires a regression test for bug fixes. The PR description states this change
fixes a screen reader accessibility issue, and the changed code adds ARIA attributes in
Ace2Editor.init() without any corresponding test changes in the provided diff.

src/static/js/ace.ts[185-195]
src/static/js/ace.ts[226-230]
src/static/js/ace.ts[240-245]
src/static/js/ace.ts[290-296]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
This PR fixes a screen reader accessibility bug by adding ARIA attributes, but it does not include a regression test to ensure the ARIA attributes remain present.
## Issue Context
Per compliance requirements, bug fixes must include a test that fails if the fix is reverted.
## Fix Focus Areas
- src/static/js/ace.ts[185-296]
- src/tests/frontend-new/specs[1-999999]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (6)
4. No skip-to-content link 📎 Requirement gap ✓ Correctness
Description
The PR adds ARIA roles/labels but does not add a discoverable skip-to-content mechanism that moves
focus to the main editor/content area. Screen reader and keyboard users still lack a direct way to
bypass surrounding UI to reach the pad content.
Code

src/static/js/ace.ts[R187-196]

   outerFrame.name = 'ace_outer';
   outerFrame.frameBorder = 0; // for IE
   outerFrame.title = 'Ether';
+    if (clientVars.padOptions.accessibilityAttributes) {
+      outerFrame.setAttribute('role', 'application');
+      outerFrame.setAttribute('aria-label', html10n.get('pad.aria.editor'));
+    }
   // Some browsers do strange things unless the iframe has a src or srcdoc property:
   //   - Firefox replaces the frame's contentWindow.document object with a different object after
   //     the frame is created. This can be worked around by waiting for the window's load event
Evidence
Compliance ID 2 requires a skip-to-content mechanism; the updated editor initialization shows ARIA
attributes being added to frames and #sidediv, but no skip link or focus-jump control is created
as part of the accessible navigation improvements.

Provide a skip-to-content mechanism for screen reader users
src/static/js/ace.ts[186-193]
src/static/js/ace.ts[229-233]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
There is no skip-to-content mechanism for screen reader/keyboard users to jump directly to the main pad content.
## Issue Context
The PR adds ARIA roles/labels to the editor iframes and content body but does not introduce a skip link or equivalent focus-jump mechanism.
## Fix Focus Areas
- src/static/js/ace.ts[186-233]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Tabs added in qqq.json 📘 Rule violation ✓ Correctness
Description
New localization entries are indented with tab characters, violating the no-tabs / 2-space
indentation style requirement. This can cause inconsistent formatting and diff churn.
Code

src/locales/qqq.json[R90-91]

+	"pad.aria.editor": "ARIA label for the outer iframe that contains the editor.",
+	"pad.aria.pad_content": "ARIA label for the inner iframe and the content body of the pad.",
Evidence
Compliance ID 8 forbids tabs and requires consistent 2-space indentation; the added lines use tab
indentation.

src/locales/qqq.json[90-91]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Tabs were introduced in `src/locales/qqq.json`, violating the repository style requirement.
## Issue Context
The new `pad.aria.*` localization documentation strings are tab-indented.
## Fix Focus Areas
- src/locales/qqq.json[90-91]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Tests use 4-space indent 📘 Rule violation ✓ Correctness
Description
New test files use 4-space indentation instead of the required 2-space indentation. This violates
the code style compliance rule.
Code

src/tests/frontend-new/specs/accessibility.spec.ts[R3-9]

+test.describe('Accessibility ARIA attributes', () => {
+    test.beforeEach(async ({ page }) => {
+        // Navigate to a new pad
+        await page.goto('http://localhost:9001/p/accessibility-test');
+        // Wait for the pad to load
+        await expect(page.locator('#editorcontainer')).toHaveClass(/initialized/);
+    });
Evidence
Compliance ID 8 requires 2-space indentation; the newly added test code is indented with 4 spaces.

src/tests/frontend-new/specs/accessibility.spec.ts[3-9]
src/tests/frontend/specs/accessibility.js[3-7]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new accessibility tests are formatted with 4-space indentation instead of the required 2-space indentation.
## Issue Context
Both the Playwright test and the legacy frontend test added in this PR use 4-space indentation.
## Fix Focus Areas
- src/tests/frontend-new/specs/accessibility.spec.ts[3-29]
- src/tests/frontend/specs/accessibility.js[3-28]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. html10n import interop 🐞 Bug ✓ Correctness
Description
ace.ts uses require('.../vendors/html10n') and then calls html10n.get(...), but html10n is a
default export so require() will typically return an object with a .default property. This can
throw at runtime when ARIA attributes are enabled, breaking editor initialization.
Code

src/static/js/ace.ts[R190-193]

+    if (clientVars.padOptions.accessibilityAttributes) {
+      outerFrame.setAttribute('role', 'application');
+      outerFrame.setAttribute('aria-label', html10n.get('pad.aria.editor'));
+    }
Evidence
ace.ts calls html10n.get(...) after importing html10n via bare require(), but the vendor
module is a TS default export (export default html10n), which is normally accessed as
require(...).default (a pattern used elsewhere in the repo). Without .default, html10n.get can
be undefined and the ARIA-label setAttribute path will crash.

src/static/js/ace.ts[28-36]
src/static/js/ace.ts[186-193]
src/static/js/vendors/html10n.ts[992-1003]
src/node/hooks/express/openapi.ts[21-23]
src/tsconfig.json[7-11]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`src/static/js/ace.ts` uses a bare CommonJS `require()` to import `vendors/html10n`, but the module is a TypeScript **default export**. As a result, `html10n.get` may be undefined at runtime, causing `Ace2Editor.init()` to throw when ARIA attributes are enabled.
### Issue Context
- `src/static/js/vendors/html10n.ts` exports `default`.
- Elsewhere in the repo, default exports imported via `require()` use `.default`.
### Fix Focus Areas
- src/static/js/ace.ts[28-36]
- src/static/js/ace.ts[186-193]
- src/static/js/ace.ts[243-249]
- src/static/js/ace.ts[295-302]
### Suggested change
- Change the import to one of:
- `const html10n = require(&amp;#x27;ep_etherpad-lite/static/js/vendors/html10n&amp;#x27;).default;`
- or `import html10n from &amp;#x27;./vendors/html10n&amp;#x27;;` (if relative import works with your bundling)
- When setting ARIA labels, consider:
- `const label = html10n.get(&amp;#x27;pad.aria.editor&amp;#x27;) || &amp;#x27;Etherpad editor&amp;#x27;;`
- similarly for `pad.aria.pad_content`

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. Hardcoded Playwright URL 🐞 Bug ⛯ Reliability
Description
The new Playwright test navigates to http://localhost:9001/... directly instead of using the
shared helper/baseURL conventions used by other frontend-new specs. This is likely to fail in CI or
in environments that run Etherpad on a different host/port.
Code

src/tests/frontend-new/specs/accessibility.spec.ts[R4-9]

+    test.beforeEach(async ({ page }) => {
+        // Navigate to a new pad
+        await page.goto('http://localhost:9001/p/accessibility-test');
+        // Wait for the pad to load
+        await expect(page.locator('#editorcontainer')).toHaveClass(/initialized/);
+    });
Evidence
Most frontend-new tests use helper utilities such as goToNewPad(page) (which typically respects
configured baseURL and handles pad creation). Hardcoding localhost:9001 bypasses that and makes the
test environment-dependent.

src/tests/frontend-new/specs/accessibility.spec.ts[4-9]
src/tests/frontend-new/specs/bold.spec.ts[1-8]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new Playwright spec hardcodes `http://localhost:9001`, which can break when the test runner uses a different baseURL/port.
### Issue Context
Other `src/tests/frontend-new/specs/*.spec.ts` files use `goToNewPad(page)` from `../helper/padHelper`.
### Fix Focus Areas
- src/tests/frontend-new/specs/accessibility.spec.ts[4-9]
### Suggested change
- Replace the hardcoded `page.goto(&amp;#x27;http://localhost:9001/...&amp;#x27;)` with either:
- `await goToNewPad(page);` (preferred, consistent with suite)
- or `await page.goto(&amp;#x27;/p/accessibility-test&amp;#x27;);` if baseURL is configured
- (Optional hardening) Instead of asserting exact English strings, assert `aria-label` is present and non-empty, or set the language cookie explicitly to ensure deterministic strings.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Ace2Editor.init() missing ARIA test 📘 Rule violation ⛯ Reliability
Description
This PR changes editor accessibility behavior by adding ARIA attributes but does not add a
regression test that would fail if the fix were reverted. Without automated coverage, the screen
reader fix can regress unnoticed.
Code

src/static/js/ace.ts[R189-193]

+    outerFrame.setAttribute('role', 'application');
+    outerFrame.setAttribute('aria-label', 'Etherpad editor');
 // Some browsers do strange things unless the iframe has a src or srcdoc property:
 //   - Firefox replaces the frame's contentWindow.document object with a different object after
 //     the frame is created. This can be worked around by waiting for the window's load event
Evidence
PR Compliance ID 2 requires a regression test for bug fixes. The PR description states this change
fixes a screen reader accessibility issue, and the changed code adds ARIA attributes in
Ace2Editor.init() without any corresponding test changes in the provided diff.

src/static/js/ace.ts[185-195]
src/static/js/ace.ts[226-230]
src/static/js/ace.ts[240-245]
src/static/js/ace.ts[290-296]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
This PR fixes a screen reader accessibility bug by adding ARIA attributes, but it does not include a regression test to ensure the ARIA attributes remain present.
## Issue Context
Per compliance requirements, bug fixes must include a test that fails if the fix is reverted.
## Fix Focus Areas
- src/static/js/ace.ts[185-296]
- src/tests/frontend-new/specs[1-999999]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

10. ARIA labels can be stale 🐞 Bug ⛯ Reliability ⭐ New
Description
ARIA labels are set once using html10n.get() but are not updated on later language changes and can
become undefined if i18n strings are unavailable at the time of initialization. This can leave
iframes/content with incorrect or useless aria-label values.
Code

src/static/js/ace.ts[R190-193]

+    if (clientVars.padOptions.accessibilityAttributes) {
+      outerFrame.setAttribute('role', 'application');
+      outerFrame.setAttribute('aria-label', html10n.get('pad.aria.editor'));
+    }
Evidence
Ace2Editor.init() sets aria-label values from html10n.get() only once. html10n.get() warns/returns
undefined if a string is missing (or translations not ready), and Etherpad can re-localize later
(language menu triggers html10n.localize), but there is no mechanism to re-apply the aria-labels
after localization changes.

src/static/js/ace.ts[186-193]
src/static/js/ace.ts[243-249]
src/static/js/ace.ts[295-302]
src/static/js/vendors/html10n.ts[688-702]
src/static/js/pad_editor.ts[123-126]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
ARIA labels are set once during `Ace2Editor.init()` via `html10n.get(...)`. If translations are not ready or keys are missing, `html10n.get()` returns `undefined` (and only logs a warning). Also, Etherpad supports changing language at runtime via `html10n.localize(...)`, but these ARIA labels are not re-applied.

### Issue Context
This is an accessibility regression risk: assistive tech could see `aria-label=&quot;undefined&quot;` or labels in the wrong language.

### Fix Focus Areas
- src/static/js/ace.ts[186-193]
- src/static/js/ace.ts[243-249]
- src/static/js/ace.ts[295-302]
- src/static/js/vendors/html10n.ts[688-702]
- src/static/js/pad_editor.ts[123-126]

### Suggested change
- Create a small helper inside `Ace2Editor.init()` (or near it) that (re)applies the ARIA attributes.
- Use fallback strings if `html10n.get(...)` returns a falsy value.
- Bind to `html10n.mt` / `html10n.bind(&#x27;localized&#x27;, ...)` to re-apply when localization completes or changes.
- Ensure the handler is registered only when `clientVars.padOptions.accessibilityAttributes` is enabled.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


11. Tests hard-code English labels 🐞 Bug ⛯ Reliability ⭐ New
Description
The new accessibility tests assert exact English aria-label strings, but Etherpad localization is
based on cookie/navigator language and may change when translations are added. This makes the tests
locale-dependent and likely to fail in non-English environments or once other locales define these
new strings.
Code

src/tests/frontend-new/specs/accessibility.spec.ts[R14-22]

+        await expect(outerFrameElement).toHaveAttribute('role', 'application');
+        await expect(outerFrameElement).toHaveAttribute('aria-label', 'Etherpad editor');
+
+        // Inner iframe (inside outer iframe)
+        const outerFrame = page.frameLocator('iframe[name="ace_outer"]');
+        const innerFrameElement = outerFrame.locator('iframe[name="ace_inner"]');
+        await expect(innerFrameElement).toHaveAttribute('role', 'document');
+        await expect(innerFrameElement).toHaveAttribute('aria-label', 'Pad content');
+
Evidence
The tests expect literal English aria-label values, while Etherpad chooses the UI language
dynamically based on cookie and navigator.language (with fallback). As soon as a locale provides
translated values for these keys (or the environment forces a different language selection), the
fixed-string assertions will fail.

src/tests/frontend-new/specs/accessibility.spec.ts[11-29]
src/tests/frontend/specs/accessibility.js[13-27]
src/static/js/l10n.ts[4-11]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Accessibility tests assert fixed English `aria-label` values. Etherpad localization can select different languages based on cookie and `navigator.language`, and future translations will legitimately change these strings.

### Issue Context
These tests are likely to become flaky across environments and will break once translated strings for the new keys are added.

### Fix Focus Areas
- src/tests/frontend-new/specs/accessibility.spec.ts[1-30]
- src/tests/frontend/specs/accessibility.js[1-29]
- src/static/js/l10n.ts[4-11]

### Suggested change options
1) Force English for the test run (set `language=en` cookie before navigation / pad creation).
2) Assert existence/non-empty labels instead of exact English strings.
3) Compute expected value from `window.html10n.get(&#x27;pad.aria.editor&#x27;)` inside the page and compare to that, rather than hard-coding English.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


12. ARIA not feature-flagged📘 Rule violation ✓ Correctness
Description
New ARIA roles/labels are applied unconditionally in Ace2Editor.init() with no feature flag or
default-off toggle. This violates the requirement that new features be gated and disabled by
default.
Code

src/static/js/ace.ts[R189-190]

+    outerFrame.setAttribute('role', 'application');
+    outerFrame.setAttribute('aria-label', 'Etherpad editor');
Evidence
PR Compliance ID 6 requires new features to be implemented behind a feature flag and disabled by
default. The new ARIA attributes are set unconditionally during init with no conditional or flag
check.

src/static/js/ace.ts[188-190]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New ARIA attribute behavior is enabled unconditionally, but compliance requires new features to be behind a feature flag and disabled by default.
## Issue Context
Consider introducing a settings/clientVars flag (default `false`) and wrapping the `setAttribute()` calls so disabling the flag yields the same DOM attributes as before this PR.
## Fix Focus Areas
- src/static/js/ace.ts[185-296]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (3)
13. ARIA labels bypass i18n🐞 Bug ✓ Correctness
Description
Ace2Editor.init() introduces hard-coded English aria-label strings, so assistive-technology
announcements won’t respect Etherpad’s localization (including runtime language changes). This is an
accessibility/i18n consistency regression for non-English users.
Code

src/static/js/ace.ts[R293-295]

+    innerDocument.body.setAttribute('role', 'textbox');
+    innerDocument.body.setAttribute('aria-multiline', 'true');
+    innerDocument.body.setAttribute('aria-label', 'Pad content');
Evidence
The PR sets aria-label values directly in code (English literals). Etherpad has a localization
system (html10n) that supports runtime language changes, and it even sets aria-label automatically
when translating elements, indicating that accessibility labels are intended to be localizable when
user-facing.

src/static/js/ace.ts[185-295]
src/static/js/pad.ts[142-148]
src/static/js/vendors/html10n.ts[641-667]
src/static/js/timeslider.ts[164-167]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New `aria-label` values for the editor iframes/body are hard-coded English strings. Etherpad supports localization and runtime language changes, so these accessibility announcements should come from locale files and be refreshed when the language changes.
## Issue Context
- `pad.ts` supports switching language at runtime via `html10n.localize([val, &amp;amp;#x27;en&amp;amp;#x27;])`.
- `html10n.translateElement()` also sets `aria-label` from localized strings, so accessibility labels are part of the i18n story.
## Fix Focus Areas
- src/static/js/ace.ts[185-296]
- src/locales/en.json[36-110]
- src/locales/en-gb.json[1-120]
(Then add equivalent keys to other locales as needed, or ensure a fallback strategy.)

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


14. ARIA not feature-flagged📘 Rule violation ✓ Correctness
Description
New ARIA roles/labels are applied unconditionally in Ace2Editor.init() with no feature flag or
default-off toggle. This violates the requirement that new features be gated and disabled by
default.
Code

src/static/js/ace.ts[R189-190]

+    outerFrame.setAttribute('role', 'application');
+    outerFrame.setAttribute('aria-label', 'Etherpad editor');
Evidence
PR Compliance ID 6 requires new features to be implemented behind a feature flag and disabled by
default. The new ARIA attributes are set unconditionally during init with no conditional or flag
check.

src/static/js/ace.ts[188-190]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New ARIA attribute behavior is enabled unconditionally, but compliance requires new features to be behind a feature flag and disabled by default.
## Issue Context
Consider introducing a settings/clientVars flag (default `false`) and wrapping the `setAttribute()` calls so disabling the flag yields the same DOM attributes as before this PR.
## Fix Focus Areas
- src/static/js/ace.ts[185-296]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


15. ARIA labels bypass i18n🐞 Bug ✓ Correctness
Description
Ace2Editor.init() introduces hard-coded English aria-label strings, so assistive-technology
announcements won’t respect Etherpad’s localization (including runtime language changes). This is an
accessibility/i18n consistency regression for non-English users.
Code

src/static/js/ace.ts[R293-295]

+    innerDocument.body.setAttribute('role', 'textbox');
+    innerDocument.body.setAttribute('aria-multiline', 'true');
+    innerDocument.body.setAttribute('aria-label', 'Pad content');
Evidence
The PR sets aria-label values directly in code (English literals). Etherpad has a localization
system (html10n) that supports runtime language changes, and it even sets aria-label automatically
when translating elements, indicating that accessibility labels are intended to be localizable when
user-facing.

src/static/js/ace.ts[185-295]
src/static/js/pad.ts[142-148]
src/static/js/vendors/html10n.ts[641-667]
src/static/js/timeslider.ts[164-167]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New `aria-label` values for the editor iframes/body are hard-coded English strings. Etherpad supports localization and runtime language changes, so these accessibility announcements should come from locale files and be refreshed when the language changes.
## Issue Context
- `pad.ts` supports switching language at runtime via `html10n.localize([val, &amp;amp;amp;#x27;en&amp;amp;amp;#x27;])`.
- `html10n.translateElement()` also sets `aria-label` from localized strings, so accessibility labels are part of the i18n story.
## Fix Focus Areas
- src/static/js/ace.ts[185-296]
- src/locales/en.json[36-110]
- src/locales/en-gb.json[1-120]
(Then add equivalent keys to other locales as needed, or ensure a fallback strategy.)

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

const sideDiv = outerDocument.createElement('div');
sideDiv.id = 'sidediv';
sideDiv.classList.add('sidediv');
sideDiv.setAttribute('aria-hidden', 'true');
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Flag not fully respected 🐞 Bug ✓ Correctness

The new accessibilityAttributes flag is meant to toggle ARIA accessibility attributes, but #sidediv
is always set to aria-hidden="true" regardless of the flag. This makes the toggle incomplete and
prevents fully disabling the accessibility behavior.
Agent Prompt
### Issue description
`padOptions.accessibilityAttributes` is intended to enable/disable ARIA accessibility attributes, but `#sidediv` is always marked `aria-hidden="true"`, even when the flag is disabled.

### Issue Context
This makes the setting ineffective for users who want to fully disable the ARIA changes.

### Fix Focus Areas
- src/static/js/ace.ts[226-233]
- settings.json.template[253-267]

### Suggested change
Wrap the `aria-hidden` assignment with the same `clientVars.padOptions.accessibilityAttributes` conditional used for the other ARIA attributes, and optionally remove the attribute when disabled to avoid stale state.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Inaccessibility to screenreaders

3 participants