Skip to content

feat(app): skip link, focus-visible baseline and keyboard-navigable header - #46

Open
jdjioe5-cpu wants to merge 1 commit into
Kinlock-Org:mainfrom
jdjioe5-cpu:feat/accessible-nav-skip-link
Open

jdjioe5-cpu wants to merge 1 commit into
Kinlock-Org:mainfrom
jdjioe5-cpu:feat/accessible-nav-skip-link

Conversation

@jdjioe5-cpu

Copy link
Copy Markdown

What and why

Closes #39.
Implements the accessible navigation baseline under Roadmap M3-17: provides a visually-hidden skip-to-content link as the first focusable element on every page to bypass testnet banner and header navigation, wraps content in a semantic landmark with focus target management (tabIndex={-1}), adds accessible focus restoration to the disclosure navigation on Escape, and establishes WCAG 2.2 compliant :focus-visible outline rings.

Changes

  • components/a11y/SkipLink.tsx:
    • Created skip link targeting #main-content, visually hidden via .sr-only and surfaced cleanly with high-contrast badge when keyboard-focused (focus:not-sr-only focus:fixed focus:top-3 focus:left-3 ...).
    • Reads localized string t("common.skipToContent").
  • app/layout.tsx:
    • Placed <SkipLink /> as the very first element in <body> preceding the testnet banner and <SiteHeader />.
    • Wrapped {children} in semantic landmark <main id="main-content" tabIndex={-1} className="outline-none">, ensuring focus actually shifts to content upon skip activation.
  • components/nav/SiteHeader.tsx:
    • Added toggleRef to mobile menu button.
    • On Escape key press, closes the disclosure menu and restores focus back to the toggle button (toggleRef.current?.focus()).
    • Added explicit focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent across desktop/mobile nav links and buttons.
  • app/globals.css:
    • Added global :focus-visible outline rule with 2px solid var(--accent) and outline-offset: 2px, clearing WCAG 2.2 focus appearance (5.14:1 light, 6.84:1 dark >= 3:1).
  • messages/en.json:
    • Added common.skipToContent: "Skip to main content".
  • components/a11y/SkipLink.test.ts:
    • Added 7 unit tests verifying: message registration, SkipLink component props and styling, render order in app/layout.tsx before banner and header, semantic <main> landmark with tabIndex={-1}, focus restoration on Escape in SiteHeader, and globals.css focus-visible rules.
  • ROADMAP.md:
    • Added Changelog entry; M3-17 remains IN PROGRESS.

Hard rules / invariants touched

  • AGENTS.md §8.3 (Accessibility: keyboard navigation, labels, contrast).
  • Invariant numbers: none (does not touch any contract, authentication, or claim-link-fragment logic).

Keyboard Navigation Verification

  • Tab order on initial page load:
    1. Skip to main content (SkipLink, visible on focus) -> pressing Enter smoothly jumps focus directly into #main-content.
    2. Testnet banner / Brand logo -> Primary nav items (/send, /request, /verify) -> Documentation -> GitHub -> Mobile menu toggle.
  • Mobile menu disclosure keyboard test:
    • Space/Enter opens menu panel.
    • Tabbing cycles through menu navigation links.
    • Pressing Escape dismisses menu panel immediately and returns keyboard focus cleanly to the toggle button.

Tests

All verification commands executed locally and passed with zero errors and zero warnings:

$ pnpm lint
> biome check .
Checked 85 files in 21ms. No fixes applied.

$ pnpm typecheck
> tsc --noEmit

$ pnpm test
> vitest run
Test Files  16 passed (16)
     Tests  175 passed (175)
  Duration  810ms

$ pnpm build
> next build
▲ Next.js 16.3.8 (Turbopack)
✓ Compiled successfully in 486ms
✓ Generating static pages using 14 workers (6/6) in 447ms
Finalizing page optimization ...

Docs / ADR

Updated ROADMAP.md Changelog.

Roadmap

M3-17 (status unchanged, IN PROGRESS). Changelog entry added: yes.

Assumptions and open questions

None. No external dependencies added.

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.

Skip link, focus-visible baseline, and a keyboard-navigable header

2 participants