From b9cc0a924efee908b985df5ddc28d6242e452ec9 Mon Sep 17 00:00:00 2001 From: Dorian Date: Wed, 11 Mar 2026 13:11:45 +0000 Subject: [PATCH] feat: add keyboard navigation, escape-to-close modals, skip-to-content (A11Y-02) All modals now close with Escape key. Interactive card divs respond to Enter key. Skip-to-content link added to Dashboard layout. All Web5 and Settings modals get role=dialog, aria-modal, and escape handlers. Co-Authored-By: Claude Opus 4.6 --- loop/plan.md | 2 +- neode-ui/src/style.css | 29 ++ neode-ui/src/views/Apps.vue | 2 + neode-ui/src/views/Dashboard.vue | 3 + neode-ui/src/views/Marketplace.vue | 2 + neode-ui/src/views/Settings.vue | 10 +- neode-ui/src/views/Web5.vue | 560 +++++++++++++++++++++-------- 7 files changed, 460 insertions(+), 148 deletions(-) diff --git a/loop/plan.md b/loop/plan.md index 49c795ea..88517f54 100644 --- a/loop/plan.md +++ b/loop/plan.md @@ -356,7 +356,7 @@ - [x] **A11Y-01** — Add ARIA labels and roles. Audit all interactive elements for accessibility. Add: `aria-label` on icon-only buttons, `role` attributes on custom widgets, `aria-live` regions for dynamic content, proper heading hierarchy. **Acceptance**: Lighthouse accessibility score > 90. -- [ ] **A11Y-02** — Add keyboard navigation testing. Verify all features are usable with keyboard only: tab order, focus management, escape to close modals, enter to submit forms. Fix any gaps. **Acceptance**: Complete user journey possible with keyboard only. +- [x] **A11Y-02** — Add keyboard navigation testing. Verify all features are usable with keyboard only: tab order, focus management, escape to close modals, enter to submit forms. Fix any gaps. **Acceptance**: Complete user journey possible with keyboard only. - [ ] **A11Y-03** — Set up i18n infrastructure. Install `vue-i18n`. Extract all user-facing strings from views into locale files (`neode-ui/src/locales/en.json`). Initial language: English only, but infrastructure ready for community translations. **Acceptance**: All strings externalized; switching locale changes UI text. diff --git a/neode-ui/src/style.css b/neode-ui/src/style.css index 00a28039..18c6fd34 100644 --- a/neode-ui/src/style.css +++ b/neode-ui/src/style.css @@ -17,6 +17,35 @@ font-style: normal; } +/* Skip to main content — keyboard navigation accessibility */ +.skip-to-content { + position: absolute; + left: -9999px; + top: auto; + width: 1px; + height: 1px; + overflow: hidden; + z-index: 9999; +} +.skip-to-content:focus { + position: fixed; + top: 12px; + left: 50%; + transform: translateX(-50%); + width: auto; + height: auto; + overflow: visible; + padding: 8px 24px; + background: rgba(0, 0, 0, 0.85); + color: white; + border: 1px solid rgba(255, 255, 255, 0.3); + border-radius: 8px; + font-size: 14px; + font-weight: 500; + text-decoration: none; + backdrop-filter: blur(12px); +} + /* Controller / keyboard navigation - soft glow only (no box outline) */ *:focus-visible { outline: none; diff --git a/neode-ui/src/views/Apps.vue b/neode-ui/src/views/Apps.vue index 8ca17fda..753a3e17 100644 --- a/neode-ui/src/views/Apps.vue +++ b/neode-ui/src/views/Apps.vue @@ -46,9 +46,11 @@ data-controller-container :data-controller-launch="canLaunch(pkg) ? '' : undefined" tabindex="0" + role="link" class="glass-card card-stagger p-6 transition-all hover:-translate-y-1 cursor-pointer relative min-w-0 overflow-hidden" :style="{ '--stagger-index': index }" @click="goToApp(id as string)" + @keydown.enter="goToApp(id as string)" >