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)" >