Skip to content

Introduce a shared accessible Dialog primitive and fix focus restoration across all modals #596

@1nonlypiece

Description

@1nonlypiece

📌 Description

The accessibility audit (design/accessibility-audit/findings/05-modals.md,
finding F-05-01, severity Critical, WCAG 2.4.3) documents that modals do not
restore focus to their trigger on close, and that focus/trap behavior is
re-implemented inconsistently per modal because there is no shared Dialog
primitive. Affected surfaces include CommitmentCreatedModal.tsx,
CommitmentDetailsModal.tsx, SettlementModal.tsx, and
CommitmentEarlyExitModal/.

This issue introduces a shared accessible Dialog and migrates modals onto it.

Goal: one tested Dialog primitive that traps focus, closes on Esc,
restores focus to the trigger, and is reused by every modal.

🎯 Requirements and Context

  • Build a Dialog that: captures document.activeElement on open, traps focus
    while open, closes on Esc and backdrop click, and calls
    previousActiveElement.focus() on close.
  • Apply correct ARIA (role="dialog", aria-modal, labelled title).
  • Migrate CommitmentCreatedModal.tsx, CommitmentDetailsModal.tsx,
    src/components/modals/SettlementModal.tsx, and CommitmentEarlyExitModal.
  • Satisfy the acceptance criteria in
    design/accessibility-audit/acceptance-criteria.md and
    component-spec-updates.md.

🛠️ Suggested Execution

1. Create a branch

git checkout -b feature/shared-dialog-primitive

2. Implement changes

  • Add src/components/ui/Dialog.tsx (+ module CSS).
  • Migrate the modals listed above onto it.
  • Add src/components/__tests__/Dialog.test.tsx covering focus trap, Esc,
    backdrop, and focus restoration; update affected modal tests.

3. Test and commit

  • Run npm test.
  • Edge cases: no focusable children, nested modals, Esc vs backdrop close,
    trigger removed before close.

Example commit message

fix: shared accessible Dialog primitive with focus restoration (F-05-01)

✅ Guidelines

  • Minimum 95% test coverage on the Dialog and migrated paths.
  • Clear documentation of the primitive's a11y contract.
  • Timeframe: 96 hours.

🏷️ Labels

type-security · type-refactor · area-frontend · MAYBE REWARDED · GRANTFOX OSS · OFFICIAL CAMPAIGN

💬 Community & Support

  • Join the CommitLabs contributor Discord: https://discord.gg/WV7tdYkJk
  • Introduce yourself before starting to avoid duplicate work.
  • Maintainers triage actively and review fast.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No fields configured for Task.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions