Skip to content

fix(canvas): WCAG critical — ARIA live toasts, dialog focus trap, keyboard nav - #131

Merged
HongmingWang-Rabbit merged 1 commit into
mainfrom
fix/wcag-critical-batch-a
Apr 15, 2026
Merged

fix(canvas): WCAG critical — ARIA live toasts, dialog focus trap, keyboard nav#131
HongmingWang-Rabbit merged 1 commit into
mainfrom
fix/wcag-critical-batch-a

Conversation

@HongmingWang-Rabbit

Copy link
Copy Markdown
Contributor

Summary

Three release-blocking WCAG violations (flagged across 3 consecutive UX audits) plus the new ChatTab ARIA gap from Audit #2. Implemented directly by Dev Lead due to engineering team workspace isolation since 08:15 restart.

  • Toaster (Toaster.tsx): Two persistent ARIA live regions (polite for success/info, assertive for errors) always in the DOM. Dismiss × button on every toast. Errors no longer auto-expire — they persist until dismissed.
  • ConfirmDialog (ConfirmDialog.tsx): role="dialog" aria-modal="true" on container. aria-labelledby wired to title h3. Focus moves to first button via requestAnimationFrame on open. Tab/Shift-Tab trapped within dialog while open.
  • WorkspaceNode (WorkspaceNode.tsx): role="button" tabIndex={0} on outer div. aria-label with name + status. aria-pressed reflects selection state. onKeyDown: Enter/Space → selectNode, ContextMenu key → openContextMenu at element center.
  • ChatTab (tabs/ChatTab.tsx): role="tablist" on sub-tab wrapper. Each button gets role="tab", aria-selected, aria-controls. Each panel gets role="tabpanel" + matching id. Textarea gets aria-label="Message to agent".

Test plan

  • npm run build — clean compile, zero TypeScript errors (Next.js 15)
  • npm test -- --run453/453 Vitest tests pass
  • Pre-commit hook: dark-theme guard clean (replaced hover:bg-white/10hover:bg-zinc-700/50)
  • Manual keyboard smoke: Tab to WorkspaceNode → Enter to select → ContextMenu key for context menu
  • Screen reader smoke: trigger a success toast, an error toast — verify screen reader announces both; verify error persists after 4 s
  • Manual dialog check: open ConfirmDialog → focus lands on first button → Tab cycles within dialog → Escape closes

Related audits

Resolves UX Audit #1 Batch A items 1–3 (Toaster, ConfirmDialog, WorkspaceNode).
Resolves UX Audit #2 ChatTab ARIA items A + B.

Note for UX Designer: PRs #122 and #123 are already merged — remote main is at 4a65c72. Please git pull before next audit.

🤖 Generated with Claude Code

…board nav

Addresses the three release-blocking WCAG violations from the UX audit
(3rd consecutive cycle) and the new ChatTab ARIA gap from Audit #2.

Changes:
- Toaster: split into polite (success/info) + assertive (error) live
  regions, both always in DOM so screen readers register them before
  any toast fires. Adds x dismiss button on every toast. Errors no
  longer auto-expire after 4s — persist until explicitly dismissed.
- ConfirmDialog: on open, requestAnimationFrame focuses the first
  button inside the dialog. Tab/Shift-Tab is now trapped inside the
  dialog while open. Added role="dialog" aria-modal="true" and
  aria-labelledby pointing to the title h3.
- WorkspaceNode: outer div gains role="button", tabIndex={0},
  aria-label, aria-pressed, and onKeyDown (Enter/Space => selectNode,
  ContextMenu key => openContextMenu). Keyboard-only users can now
  reach and activate workspace nodes.
- ChatTab sub-tab bar: role="tablist" on wrapper, role="tab" +
  aria-selected + aria-controls on each button, matching
  role="tabpanel" + id on each panel div. Textarea gets
  aria-label="Message to agent".

453/453 Vitest tests pass. Production build clean (Next.js 15).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@HongmingWang-Rabbit
HongmingWang-Rabbit merged commit e32dd99 into main Apr 15, 2026
1 of 7 checks passed
@HongmingWang-Rabbit
HongmingWang-Rabbit deleted the fix/wcag-critical-batch-a branch April 16, 2026 12:31
molecule-ai Bot pushed a commit that referenced this pull request Apr 21, 2026
fix(canvas): WCAG critical — ARIA live toasts, dialog focus trap, keyboard nav
HongmingWang-Rabbit pushed a commit that referenced this pull request Apr 30, 2026
publish-runtime.yml had a broad smoke (AgentCard call-shape, well-known
mount alignment, new_text_message) inline as a heredoc. runtime-prbuild-
compat.yml had a narrow inline smoke (just `from main import main_sync`).
Result: a PR could introduce SDK shape regressions that pass at PR time
and only fail at publish time, post-merge.

Extract the broad smoke into scripts/wheel_smoke.py and invoke it from
both workflows. PR-time gate now matches publish-time gate — same script,
same assertions. Eliminates the drift hazard of two heredocs that have
to be kept in lockstep manually.

Verified locally:
  * Built wheel from workspace/ source, installed in venv, ran smoke → pass
  * Simulated AgentCard kwarg-rename regression → smoke catches it as
    `ValueError: Protocol message AgentCard has no "supported_interfaces"
    field` (the exact failure mode of #2179 / supported_protocols incident)

Path filter for runtime-prbuild-compat extended to include
scripts/wheel_smoke.py so smoke-only edits get PR-validated. publish-
runtime path filter intentionally NOT extended — smoke-only edits should
not auto-trigger a PyPI version bump.

Subset of #131 (the broader "invoke main() against stub config" goal
remains pending — main() needs a config dir + stub platform server).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant