Skip to content

fix(a11y): associate Settings labels with custom widgets (D6) - #56

Merged
BrianGenisio merged 1 commit into
mainfrom
fix/a11y-d6-settings-labels
Aug 12, 2026
Merged

fix(a11y): associate Settings labels with custom widgets (D6)#56
BrianGenisio merged 1 commit into
mainfrom
fix/a11y-d6-settings-labels

Conversation

@BrianGenisio

Copy link
Copy Markdown
Contributor

Summary

Closes #50 ([a11y][D6]). Temperature and Thinking in Settings were orphaned labels: custom widgets rendered into separate containers, so AT never got the visible label or description.

Changes

App-only fix. Each row label/description now has a stable id. After constructing the widgets we set:

  • Temperature slider (role="slider"): aria-labelledby + aria-describedby
  • Thinking toggle and portaled listbox: same pairing (aria-labelledby wins over the DS default slider aria-label)

No design-system API change. D2–D5 and D7 left alone.

Test plan

  • npm test (DOM harness asserts label/description association for both widgets)
  • axe shrink-only gate (A11Y_CI=1) — passed; no baseline shrink (rule was already absent from the checked-in baseline)
  • Keyboard/VoiceOver: open Settings in light and dark; Temperature and Thinking announce their labels (and descriptions where exposed)
  • Confirm System Prompt still works via for="customInstructionsEl"

Wire Temperature and Thinking row labels/descriptions to their slider and
dropdown via aria-labelledby / aria-describedby so assistive tech gets real
names (D6).

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The settings modal now assigns stable IDs to the Temperature and Thinking labels and descriptions. The Temperature slider references its label and description. The Thinking dropdown toggle and portaled listbox reference their label and description. DOM tests verify these associations and confirm the listbox role.

Suggested reviewers: aimemp

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the accessibility fix for Settings labels and custom widgets.
Description check ✅ Passed The description accurately explains the accessibility issue, implementation, tests, and remaining verification tasks.
Linked Issues check ✅ Passed The changes address issue #50 by associating labels and descriptions with Temperature and Thinking widgets and preserving axe compliance.
Out of Scope Changes check ✅ Passed The changes are limited to the Settings accessibility fix and its DOM tests, with no unrelated scope identified.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/dom/render.test.js (1)

567-584: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Open the Thinking menu before asserting the portaled listbox.

The test queries the first [id^="portal-dropdown-menu-"] immediately after Settings opens. It does not exercise the toggle's open path, and it can inspect a hidden or unrelated portaled menu. Click toggle, call settle(), then select the open listbox by its role and thinkingLabel association.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/dom/render.test.js` around lines 567 - 584, Update the test around
“associates Thinking label and description...” to click the Thinking dropdown
toggle, await settle(), then query the open portaled listbox by role="listbox"
and its aria-labelledby="thinkingLabel" association before asserting its
attributes. Keep the existing label, description, and toggle assertions
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@public/app.js`:
- Around line 681-693: Update the ARIA description wiring for the thinking
toggle and menu so both include thinkingNote when it is visible, while retaining
thinkingDesc. Recompute both aria-describedby values whenever
thinkingNote.hidden changes, using the existing thinkingDropdownInstance and
thinkingNote symbols.

---

Nitpick comments:
In `@tests/dom/render.test.js`:
- Around line 567-584: Update the test around “associates Thinking label and
description...” to click the Thinking dropdown toggle, await settle(), then
query the open portaled listbox by role="listbox" and its
aria-labelledby="thinkingLabel" association before asserting its attributes.
Keep the existing label, description, and toggle assertions unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3fc63166-c96e-4bee-9879-bc24d1662d40

📥 Commits

Reviewing files that changed from the base of the PR and between 346202f and 3ec5252.

📒 Files selected for processing (2)
  • public/app.js
  • tests/dom/render.test.js

Comment thread public/app.js
Comment on lines +681 to +693
// Custom widgets are not labelable via <label for>, so wire the
// visible row label/description through ARIA (D6).
const thinkingToggleValue = thinkingDropdownInstance.toggle.querySelector('.dropdown-toggle-label');
if (thinkingToggleValue && !thinkingToggleValue.id) {
thinkingToggleValue.id = 'thinkingToggleValue';
}
thinkingDropdownInstance.toggle.setAttribute(
'aria-labelledby',
thinkingToggleValue?.id ? 'thinkingLabel thinkingToggleValue' : 'thinkingLabel',
);
thinkingDropdownInstance.toggle.setAttribute('aria-describedby', 'thinkingDesc');
thinkingDropdownInstance.menu.setAttribute('aria-labelledby', 'thinkingLabel');
thinkingDropdownInstance.menu.setAttribute('aria-describedby', 'thinkingDesc');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Include the visible unsupported-state note in aria-describedby.

When canThink is false, thinkingNote becomes visible at Lines 700-703. The toggle and listbox still reference only thinkingDesc at Lines 691-693. Assistive technology does not receive the visible explanation as the widget description. Update both aria-describedby values whenever thinkingNote.hidden changes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@public/app.js` around lines 681 - 693, Update the ARIA description wiring for
the thinking toggle and menu so both include thinkingNote when it is visible,
while retaining thinkingDesc. Recompute both aria-describedby values whenever
thinkingNote.hidden changes, using the existing thinkingDropdownInstance and
thinkingNote symbols.

@BrianGenisio
BrianGenisio merged commit cbb9a6f into main Aug 12, 2026
2 checks passed
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.

[a11y][D6] Settings labels are orphaned from their custom widgets

1 participant