@@ -69,6 +69,65 @@ The frontend has a **three-level hierarchy**:
6969- Sections with counts: ` "Projects list (5 total)" ` , ` "Issues (2e 1w): {path}" `
7070- Conditional content: Update aria-label when content changes
7171
72+ ### Pattern: Keyboard Accessibility for Custom Interactive Elements
73+
74+ When creating interactive elements with ` role="button" ` , ` role="tab" ` , or other interactive roles using ` <div> ` or other non-button elements, you must provide keyboard support via the ` ariaKeyDown ` utility from ` @cocalc/frontend/app/aria ` .
75+
76+ ** Why this is needed:**
77+
78+ - Native ` <button> ` elements support Enter and Space keys automatically
79+ - Custom elements with ` role="button" ` do not have native keyboard support
80+ - Screen reader users and keyboard-only users rely on this keyboard behavior
81+
82+ ** Usage pattern:**
83+
84+ ``` tsx
85+ import { ariaKeyDown } from " @cocalc/frontend/app/aria" ;
86+
87+ // For button-like divs
88+ <div
89+ role = " button"
90+ tabIndex = { 0 }
91+ onClick = { handleClick }
92+ onKeyDown = { ariaKeyDown (handleClick )}
93+ aria-label = " Delete item"
94+ >
95+ Delete
96+ </div >
97+
98+ // For tab-like divs
99+ <div
100+ role = " tab"
101+ tabIndex = { 0 }
102+ onClick = { handleSelect }
103+ onKeyDown = { ariaKeyDown (handleSelect )}
104+ aria-selected = { isActive }
105+ >
106+ Tab label
107+ </div >
108+ ```
109+
110+ ** What ariaKeyDown does:**
111+
112+ - Activates the handler when Enter or Space keys are pressed
113+ - Prevents default browser behavior (form submission, page scroll)
114+ - Provides the same keyboard experience as native buttons
115+ - Single source of truth for this common accessibility pattern
116+
117+ ** Implementation note:**
118+ Always use ` ariaKeyDown ` when you have:
119+
120+ - ` role="button" ` , ` role="tab" ` , or other interactive roles on non-button elements
121+ - An ` onClick ` handler that should also work with keyboard
122+ - A ` tabIndex={0} ` to make the element focusable
123+
124+ See ` packages/frontend/app/aria.tsx ` for the implementation and usage in:
125+
126+ - ` packages/frontend/app/nav-tab.tsx ` - Navigation tabs
127+ - ` packages/frontend/app/connection-indicator.tsx ` - Status indicator
128+ - ` packages/frontend/app/notifications.tsx ` - Notification badges
129+ - ` packages/frontend/frame-editors/frame-tree/status-bar.tsx ` - Status bar close button
130+
72131## Split Editors with Multiple Frames
73132
74133When editors are split into multiple frames, use nested regions with clear labels:
@@ -412,8 +471,9 @@ Location: `packages/frontend/project/page/`
412471** Completed** ✅:
413472
414473- [x] ** page.tsx** - Main project workspace
415- - [x] Main content area: ` <div role="main" aria-label="Content: {currentFilename}"> ` (line 389-392)
416- - [x] Activity bar sidebar: ` <aside role="complementary" aria-label="Project activity bar"> ` (line 356-371)
474+ - [x] Root container: ` <div role="region" aria-label="Project: {projectTitle}"> ` (line 420-425)
475+ - [x] Main content area: ` <div role="main" aria-label="Content: {currentFilename}"> ` (line 395-404)
476+ - [x] Activity bar sidebar: ` <aside role="complementary" aria-label="Project activity bar"> ` (line 362-376)
417477 - [x] File tabs navigation: ` <nav aria-label="Open files"> ` (line 307-313)
418478 - [x] Flyout sidebar: ` <aside role="complementary" aria-label="Project sidebar"> ` (line 262-278)
419479
@@ -503,28 +563,42 @@ Location: `packages/frontend/app/`
503563- ` packages/frontend/app/connection-indicator.tsx ` - Status live region with i18n labels
504564- ` packages/frontend/i18n/common.ts ` - Added labels.connected
505565
506- #### ** P1 - Important Improvements** ⏳ PENDING
507-
508- - [ ] ** active-content.tsx** - Content router
509- - [ ] Dynamic content: Announce when switching pages
510- - [ ] Loading states: ` aria-busy ` indication
511- - [ ] Error states: ARIA alert or live region
512-
513- - [ ] ** Banners** - Informational/warning banners (5 files)
514- - [ ] All banners: ` role="region" aria-label="..." `
515- - [ ] ` i18n-banner.tsx ` - Language selection
516- - [ ] ` verify-email-banner.tsx ` - Email verification
517- - [ ] ` version-warning.tsx ` - Version alerts
518- - [ ] ` insecure-test-mode-banner.tsx ` - Test mode warning
519- - [ ] ` warnings.tsx ` - Cookie/storage warnings
520-
521- - [ ] ** Notifications** - Notification indicators
522- - [ ] Notification badges: ` aria-label ` with count
523- - [ ] Live region: ` aria-live="polite" ` for count changes
524-
525- - [ ] ** projects-nav.tsx** - Project tabs navigation
526- - [ ] Container: ` aria-label="Open projects" `
527- - [ ] Tab semantics already handled by Ant Design Tabs
566+ #### ** P1 - Important Improvements** ✅ COMPLETED
567+
568+ - [x] ** active-content.tsx** - Content router
569+ - [x] Decision: Each active content page should have its own aria-labels (not wrapped in single region)
570+ - [x] Left as ` <>{v}</> ` - ProjectPage, ProjectsPage, AccountPage, etc. handle their own landmarks
571+
572+ - [x] ** Banners** - Informational/warning banners (5 files)
573+ - [x] ** i18n-banner.tsx** - ` role="region" aria-label="Language selection" aria-live="polite" `
574+ - [x] ** verify-email-banner.tsx** - ` aria-label="Email verification required" ` on Modal
575+ - [x] ** version-warning.tsx** - ` role="region" aria-label="Version warning" ` with dynamic aria-live (assertive if critical)
576+ - [x] ** insecure-test-mode-banner.tsx** - ` role="region" aria-label="Test mode warning" aria-live="assertive" ` on Alert
577+ - [x] ** warnings.tsx** - Both CookieWarning and LocalStorageWarning:
578+ - ` role="region" aria-label="Cookie warning" aria-live="assertive" `
579+ - ` role="region" aria-label="Local storage warning" aria-live="assertive" `
580+
581+ - [x] ** notifications.tsx** - Notification indicators with keyboard support
582+ - [x] Added ` getAriaLabel() ` function for dynamic labels:
583+ - Bell: ` "File use notifications: {count} new" `
584+ - Notifications: ` "Messages and mentions: {unreadMessages} unread, {count} mentions, {newsUnread} news" `
585+ - [x] Added ` role="button" ` for keyboard accessibility
586+ - [x] Added ` aria-live="polite" ` to announce count changes
587+ - [x] Added ` tabIndex={0} ` and ` onKeyDown ` for Enter/Space activation
588+
589+ - [x] ** projects-nav.tsx** - Project tabs navigation
590+ - [x] Added ` aria-label="Open projects" ` to Ant Design Tabs container
591+ - [x] Tab semantics already handled by Ant Design Tabs component
592+
593+ ** Files Modified (P1)** :
594+
595+ - ` packages/frontend/app/i18n-banner.tsx `
596+ - ` packages/frontend/app/verify-email-banner.tsx `
597+ - ` packages/frontend/app/version-warning.tsx `
598+ - ` packages/frontend/app/warnings.tsx `
599+ - ` packages/frontend/app/insecure-test-mode-banner.tsx `
600+ - ` packages/frontend/app/notifications.tsx `
601+ - ` packages/frontend/projects/projects-nav.tsx `
528602
529603### Phase 13: Forms & Settings ⏳ PENDING
530604
0 commit comments