Skip to content

fix: improve Enhancer accessibility and viewport safety#1222

Merged
ChuxiJ merged 2 commits intomainfrom
fix/issue-1198
Mar 30, 2026
Merged

fix: improve Enhancer accessibility and viewport safety#1222
ChuxiJ merged 2 commits intomainfrom
fix/issue-1198

Conversation

@ChuxiJ
Copy link
Copy Markdown

@ChuxiJ ChuxiJ commented Mar 29, 2026

Summary

  • Add role="dialog", aria-label="AI Enhancer", aria-modal="true" to the panel root
  • Implement focus trap: auto-focus panel on mount, return focus on unmount
  • Remove dead "More options" button that had no handler
  • Add max-w-[95vw] to prevent panel overflow on narrow viewports

Test plan

  • Unit tests for ARIA attributes, focus trap behavior, and viewport constraint
  • Manual: open Enhancer, verify screen reader announces "AI Enhancer dialog"; tab through controls to verify focus stays trapped; resize window to <500px width, verify panel doesn't overflow

Closes #1198

https://claude.ai/code/session_01CzJuSwLHvtiJfKFfcXbXhC

Copilot AI review requested due to automatic review settings March 29, 2026 20:01
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Improves the AI Enhance panel’s accessibility semantics and responsive behavior to better match the DAW’s keyboard-first expectations and avoid horizontal overflow on narrow viewports.

Changes:

  • Adds dialog semantics (role="dialog", aria-label, aria-modal) to the Enhance panel root(s).
  • Introduces a Tab/Shift+Tab focus-trap handler while the panel is open.
  • Removes a dead “More options” button and adds max-w-[95vw] to reduce viewport overflow.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +242 to +265
// Focus trap — keep Tab/Shift+Tab within the panel
useEffect(() => {
if (!enhancerOpen) return;
const handleTab = (e: KeyboardEvent) => {
if (e.key !== 'Tab') return;
const panel = panelRef.current;
if (!panel) return;
const focusable = panel.querySelectorAll<HTMLElement>(
'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])',
);
if (focusable.length === 0) return;
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
};
window.addEventListener('keydown', handleTab);
return () => window.removeEventListener('keydown', handleTab);
}, [enhancerOpen]);
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

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

The Tab “focus trap” only wraps when the currently focused element is the first/last focusable inside the panel. If focus is on an element behind the dialog (or on a non-focusable element), Tab/Shift+Tab can still move focus outside, so focus is not reliably trapped. Consider: (1) on open, move focus into the dialog (store/restore the previously focused element on close), (2) on Tab, if the active element is not within the panel, preventDefault and focus the first (or last for Shift+Tab), and (3) ensure the focusables list excludes elements that are visually hidden (e.g., controls hidden via opacity-0 should either become visible on focus or be removed from tab order).

Copilot uses AI. Check for mistakes.
Comment on lines 485 to 491
<div
data-testid="enhance-panel"
className="fixed left-1/2 -translate-x-1/2 w-[780px] bg-[#1e1e22] border border-[#3a3a3a] rounded-xl shadow-2xl text-xs text-zinc-200 p-8 text-center transition-[bottom] duration-200 ease-out"
role="dialog"
aria-label="AI Enhancer"
aria-modal="true"
className="fixed left-1/2 -translate-x-1/2 w-[780px] max-w-[95vw] bg-[#1e1e22] border border-[#3a3a3a] rounded-xl shadow-2xl text-xs text-zinc-200 p-8 text-center transition-[bottom] duration-200 ease-out"
style={{ zIndex: Z.panel, bottom: `${dynamicBottom}px` }}
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

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

The no-selection guidance dialog doesn’t attach panelRef, so the focus-trap logic won’t run in this state (panelRef stays null while enhancerOpen is true). If the guidance view is intended to be a dialog too, it should participate in the same focus management (either attach the ref here as well or refactor to a single shared root).

Copilot uses AI. Check for mistakes.
Comment on lines +553 to +556
role="dialog"
aria-label="AI Enhancer"
aria-modal="true"
className="fixed left-1/2 -translate-x-1/2 w-[820px] max-w-[95vw] max-h-[60vh] bg-[#1e1e22] border border-[#3a3a3a] rounded-xl shadow-2xl flex text-xs text-zinc-200 overflow-hidden transition-[bottom] duration-200 ease-out"
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

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

PR description mentions unit tests for ARIA attributes, focus trap, and viewport constraint, but the existing EnhancePanel tests do not assert the new role="dialog"/aria-* attributes or Tab trapping behavior. Add/extend tests to cover these new accessibility and responsive requirements (including the no-selection guidance state).

Copilot uses AI. Check for mistakes.
className="fixed left-1/2 -translate-x-1/2 w-[820px] max-h-[60vh] bg-[#1e1e22] border border-[#3a3a3a] rounded-xl shadow-2xl flex text-xs text-zinc-200 overflow-hidden transition-[bottom] duration-200 ease-out"
role="dialog"
aria-label="AI Enhancer"
aria-modal="true"
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

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

aria-modal="true" indicates a modal dialog, but EnhancePanel doesn’t render a backdrop or otherwise make the rest of the page inert/unclickable. To avoid misleading assistive tech, either implement true modal behavior (e.g., backdrop + outside content inert/aria-hidden) or omit aria-modal and treat this as a non-modal dialog/panel.

Suggested change
aria-modal="true"

Copilot uses AI. Check for mistakes.
- Add role="dialog", aria-label="AI Enhancer", aria-modal="true" to panel
- Implement focus trap: Tab/Shift+Tab cycles within the panel while open
- Remove non-functional "More options" 3-dot button from result entries
- Add max-w-[95vw] to both panel variants for narrow screen support

Closes #1198

https://claude.ai/code/session_01CzJuSwLHvtiJfKFfcXbXhC
- Attach panelRef to both guidance and full panel renders
- Auto-focus first focusable element on mount
- Save and restore focus on close
- Remove aria-modal (panel is non-modal, backdrop in separate PR)
- Merge Escape and Tab keydown listeners into single handler
- Add tests for dialog role, aria-label, dead button removal, viewport class

https://claude.ai/code/session_01CzJuSwLHvtiJfKFfcXbXhC
@ChuxiJ ChuxiJ merged commit 49c6584 into main Mar 30, 2026
2 of 4 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.

enhancement: AI Enhancer panel accessibility and responsiveness gaps

3 participants