Accessibility audit: axe + keyboard + screen-reader flows over every route #162

Closed
opened 2026-09-26 09:40:16 +00:00 by kayg · 25 comments
Owner

Hard requirement (CLAUDE.md §Priorities 3). Add an e2e a11y sweep: axe-core on every route and main state (sidebar open/collapsed, every overlay open: palette, composer, menus, inspector, sheets, Settings sections, share dialog, public link page, login) in light and dark, 1440 and 390. Zero serious/critical violations. Plus scripted keyboard-only journeys: create a log entry, move a file, share a folder, change a setting, navigate modes, all without a pointer; focus is always visible and never lost behind a scrim; Esc restores focus to the opener; the resizable sidebar separator works by keyboard. Check prefers-reduced-motion disables motion, and 44px touch targets on coarse pointers. Fix every finding in the shared component (reuse gate), commit per fix, keep the sweep in CI scripts.

Hard requirement (CLAUDE.md §Priorities 3). Add an e2e a11y sweep: axe-core on every route and main state (sidebar open/collapsed, every overlay open: palette, composer, menus, inspector, sheets, Settings sections, share dialog, public link page, login) in light and dark, 1440 and 390. Zero serious/critical violations. Plus scripted keyboard-only journeys: create a log entry, move a file, share a folder, change a setting, navigate modes, all without a pointer; focus is always visible and never lost behind a scrim; Esc restores focus to the opener; the resizable sidebar separator works by keyboard. Check prefers-reduced-motion disables motion, and 44px touch targets on coarse pointers. Fix every finding in the shared component (reuse gate), commit per fix, keep the sweep in CI scripts.
Author
Owner

Starting #162 on job/a11y, based on 82d4c258bb. I will add the production-build axe and keyboard sweep, fix findings outside the sibling jobs’ owned areas, merge dev before final gates, and report measured gaps.

Starting #162 on job/a11y, based on 82d4c258bbcc6f34e8805d0018004fa8e621b9a5. I will add the production-build axe and keyboard sweep, fix findings outside the sibling jobs’ owned areas, merge dev before final gates, and report measured gaps.
Author
Owner

Finding from source review: FilesBrowser.svelte handles arrows, Enter, F2, Quick Look, Trash, selection, and info from its listbox keyboard handler, but exposes moving a file only through drag/drop (moveItems in transfer.ts). A keyboard-only move journey cannot complete. I will add a keyboard path and cover it in the sweep.

Finding from source review: FilesBrowser.svelte handles arrows, Enter, F2, Quick Look, Trash, selection, and info from its listbox keyboard handler, but exposes moving a file only through drag/drop (moveItems in transfer.ts). A keyboard-only move journey cannot complete. I will add a keyboard path and cover it in the sweep.
Author
Owner

Gate finding: bun run test reached 355/356 passing tests; the rendered theme contrast contract exceeded the default 5 s timeout under shared-host load (Vitest reported 9.868 s for that test). The contract is unchanged; commit 434ce8b raises only its timeout to 30 s. Focused rerun: Test Files 1 passed (1); Tests 55 passed (55).

Gate finding: bun run test reached 355/356 passing tests; the rendered theme contrast contract exceeded the default 5 s timeout under shared-host load (Vitest reported 9.868 s for that test). The contract is unchanged; commit 434ce8b raises only its timeout to 30 s. Focused rerun: Test Files 1 passed (1); Tests 55 passed (55).
Author
Owner

Keyboard move implementation committed as 5d64dbd: the Files listbox now accepts Shift+F10/ContextMenu, Ctrl/⌘+X stages the selected paths, and Ctrl/⌘+V moves them into the open folder through the existing moveItems server API. Existing menus expose Cut to move and Move here. Web gates: svelte-check found 0 errors and 0 warnings; Vitest Test Files 49 passed (49), Tests 356 passed (356). Production browser flow is next.

Keyboard move implementation committed as 5d64dbd: the Files listbox now accepts Shift+F10/ContextMenu, Ctrl/⌘+X stages the selected paths, and Ctrl/⌘+V moves them into the open folder through the existing moveItems server API. Existing menus expose Cut to move and Move here. Web gates: svelte-check found 0 errors and 0 warnings; Vitest Test Files 49 passed (49), Tests 356 passed (356). Production browser flow is next.
Author
Owner

Real-browser Axe finding (diagnostic server, production SPA): /files/trash at 1440 light with sidebar open and collapsed reports aria-valid-attr-value (critical) on #tab-files. The mode tray uses SegmentedControl tab semantics with the default aria-controls=panel-files, but the app has one actual #route-content panel. I made the minimal shared TabBar fix to point its tabs at that live panel; current-worktree production verification is pending.

Real-browser Axe finding (diagnostic server, production SPA): /files/trash at 1440 light with sidebar open and collapsed reports aria-valid-attr-value (critical) on #tab-files. The mode tray uses SegmentedControl tab semantics with the default aria-controls=panel-files, but the app has one actual #route-content panel. I made the minimal shared TabBar fix to point its tabs at that live panel; current-worktree production verification is pending.
Author
Owner

Production browser finding: the Files Trash route and six overlays returned zero serious/critical Axe violations at 1440/light after the TabBar aria-controls fix. The keyboard script then failed while typing a multi-character filename: Files typeahead resets after 700 ms, and the shared host delayed Playwright keystrokes enough to break the test's selection assumption. I changed the journey to select rows with Home and ArrowDown, which directly tests keyboard navigation without timing. Focused rerun: A11Y PASSED: 9 axe scans, keyboard and reduced-motion checks. Commit 5dc6979. The required merge from dev is in progress; one package.json script conflict keeps both additions.

Production browser finding: the Files Trash route and six overlays returned zero serious/critical Axe violations at 1440/light after the TabBar aria-controls fix. The keyboard script then failed while typing a multi-character filename: Files typeahead resets after 700 ms, and the shared host delayed Playwright keystrokes enough to break the test's selection assumption. I changed the journey to select rows with Home and ArrowDown, which directly tests keyboard navigation without timing. Focused rerun: A11Y PASSED: 9 axe scans, keyboard and reduced-motion checks. Commit 5dc6979. The required merge from dev is in progress; one package.json script conflict keeps both additions.
Author
Owner

Full production sweep finding: desktop routes with the sidebar open report Axe aria-required-children (critical) on MiniMonth. The month and year Calendar views also report it on MonthGrid and YearHeatmap; MonthGrid additionally reports aria-required-parent on all 42 date cells. Source confirms these are CSS grids of named date buttons, without ARIA row/cell navigation. I changed the three shared components to labelled groups so screen readers retain the date and item button names without a false grid contract. The pre-fix full matrix is still collecting any other findings; production verification of this change is pending its completion and rebuild.

Full production sweep finding: desktop routes with the sidebar open report Axe aria-required-children (critical) on MiniMonth. The month and year Calendar views also report it on MonthGrid and YearHeatmap; MonthGrid additionally reports aria-required-parent on all 42 date cells. Source confirms these are CSS grids of named date buttons, without ARIA row/cell navigation. I changed the three shared components to labelled groups so screen readers retain the date and item button names without a false grid contract. The pre-fix full matrix is still collecting any other findings; production verification of this change is pending its completion and rebuild.
Author
Owner

Shared calendar fix committed as 86e716b2. Focused production rerun after rebuilding the web app: A11Y PASSED: 19 axe scans, keyboard and reduced-motion checks. Calendar Today, Day, Week, Month and Year each had zero serious/critical findings with the desktop sidebar open and collapsed. The MiniMonth, MonthGrid and YearHeatmap CSS layouts now expose labelled groups rather than incomplete ARIA grids. The Year heatmap keeps the today accent ring and uses ink for its numeral on low heat tints. The complete route/theme/viewport matrix is now running.

Shared calendar fix committed as 86e716b2. Focused production rerun after rebuilding the web app: A11Y PASSED: 19 axe scans, keyboard and reduced-motion checks. Calendar Today, Day, Week, Month and Year each had zero serious/critical findings with the desktop sidebar open and collapsed. The MiniMonth, MonthGrid and YearHeatmap CSS layouts now expose labelled groups rather than incomplete ARIA grids. The Year heatmap keeps the today accent ring and uses ink for its numeral on low heat tints. The complete route/theme/viewport matrix is now running.
Author
Owner

Full production sweep finding: Axe target-size (serious) flags Year heatmap day buttons at 1440/light with the sidebar open. Every flagged selector carries data-hit-exempt="date-grid". DESIGN §35 explicitly grants this dense-grid exception because Month and Day views provide full-size targets for the same dates. The sweep now filters only target-size nodes whose element has that exact marker and logs the exempt count; all unmarked target-size nodes still fail. Verification of this test rule is pending the next full run.

Full production sweep finding: Axe target-size (serious) flags Year heatmap day buttons at 1440/light with the sidebar open. Every flagged selector carries data-hit-exempt="date-grid". DESIGN §35 explicitly grants this dense-grid exception because Month and Day views provide full-size targets for the same dates. The sweep now filters only target-size nodes whose element has that exact marker and logs the exempt count; all unmarked target-size nodes still fail. Verification of this test rule is pending the next full run.
Author
Owner

The dense date target rule is committed as 16b96999. In the current production sweep, Year at 1440/light with the sidebar open logs EXEMPT ... 365 dense date targets with equivalent views and then AXE ... 0 serious/critical. The exemption requires the exact data-hit-exempt="date-grid" marker and applies only to Axe target-size nodes; other nodes and rules still fail. The complete matrix continues.

The dense date target rule is committed as 16b96999. In the current production sweep, Year at 1440/light with the sidebar open logs `EXEMPT ... 365 dense date targets with equivalent views` and then `AXE ... 0 serious/critical`. The exemption requires the exact data-hit-exempt="date-grid" marker and applies only to Axe target-size nodes; other nodes and rules still fail. The complete matrix continues.
Author
Owner

Full production sweep finding: /tags at 1440/dark reports Axe color-contrast (serious) on TagPill .tag, both with the sidebar open and collapsed. TagPill mixes 62% of a generated mid-lightness tag hue into text against a dark tinted surface. I changed the shared TagPill dark rule to mix only 20% hue into ink, retaining the tag tint while increasing text contrast. The current sweep uses the pre-fix build; a rebuilt production scan is pending.

Full production sweep finding: /tags at 1440/dark reports Axe color-contrast (serious) on TagPill `.tag`, both with the sidebar open and collapsed. TagPill mixes 62% of a generated mid-lightness tag hue into text against a dark tinted surface. I changed the shared TagPill dark rule to mix only 20% hue into ink, retaining the tag tint while increasing text contrast. The current sweep uses the pre-fix build; a rebuilt production scan is pending.
Author
Owner

Resuming job/a11y at c0173584 (base dev merge 44b15bec). The worktree is clean. The pending shared TagPill dark contrast change is at the checkpoint; I am rebuilding and rerunning its focused production Axe scan before the full matrix and final dev merge.

Resuming job/a11y at c0173584 (base dev merge 44b15bec). The worktree is clean. The pending shared TagPill dark contrast change is at the checkpoint; I am rebuilding and rerunning its focused production Axe scan before the full matrix and final dev merge.
Author
Owner

Post-merge build finding: cargo build -p calternal-server failed with E0308 at crates/plugins/files/src/index.rs:620. The skip-upsert branch returned Ok(()) after a concurrent dev change made record_once return Result<bool, Failure>. Adjacent unchanged-file exits return Ok(false). I changed this exit to Ok(false), a one-line compile repair outside the a11y surface, and am running the files crate test before continuing.

Post-merge build finding: cargo build -p calternal-server failed with E0308 at crates/plugins/files/src/index.rs:620. The skip-upsert branch returned Ok(()) after a concurrent dev change made record_once return Result<bool, Failure>. Adjacent unchanged-file exits return Ok(false). I changed this exit to Ok(false), a one-line compile repair outside the a11y surface, and am running the files crate test before continuing.
Author
Owner

Correction to my prior build note: the helper bool means success, not write performed. Ok(false) compiled but made unchanged rows retry to 409. A previously failing stale-folder test passed after changing the no-write exit to Ok(true), and the Files lib suite passed: test result: ok. 88 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out. The one-line merge repair is committed as 92bd7371. The failed first test run produced 12 conflicts; its generated proptest addition was restored and is not committed.

Correction to my prior build note: the helper bool means success, not write performed. Ok(false) compiled but made unchanged rows retry to 409. A previously failing stale-folder test passed after changing the no-write exit to Ok(true), and the Files lib suite passed: test result: ok. 88 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out. The one-line merge repair is committed as 92bd7371. The failed first test run produced 12 conflicts; its generated proptest addition was restored and is not committed.
Author
Owner

Full matrix finding: Month today text intermittently fails Axe contrast at 1440/light while the calendar range is loading; MonthGrid applied opacity: 0.65 to every date. I replaced that fade with aria-busy, kept text at full opacity, and added a deterministic delayed-range Axe scan. It passed: AXE month while days load: 0 serious/critical. Phone/light Notes open also found aria-required-children on a status-only Notes tree. NotesExplorer now exposes a group until it has a focusable treeitem; its focused route scan reported 0 serious/critical. Fix commits: cc8c6e34 and 271c6174. Web checks: svelte-check found 0 errors and 0 warnings; Vitest Test Files 53 passed (53), Tests 418 passed (418). The full matrix is being rerun.

Full matrix finding: Month today text intermittently fails Axe contrast at 1440/light while the calendar range is loading; MonthGrid applied opacity: 0.65 to every date. I replaced that fade with aria-busy, kept text at full opacity, and added a deterministic delayed-range Axe scan. It passed: AXE month while days load: 0 serious/critical. Phone/light Notes open also found aria-required-children on a status-only Notes tree. NotesExplorer now exposes a group until it has a focusable treeitem; its focused route scan reported 0 serious/critical. Fix commits: cc8c6e34 and 271c6174. Web checks: svelte-check found 0 errors and 0 warnings; Vitest Test Files 53 passed (53), Tests 418 passed (418). The full matrix is being rerun.
Author
Owner

Expanded full matrix found one serious Axe contrast violation in NoteMentionsCard empty states at 1440/light/collapsed: 4.33:1 while the shared 160 ms disclosure reveal faded all text. I changed the shared cal-reveal-in keyframe to keep text opaque while preserving the 4 px motion. On phones, Search, Notifications, agent turns, Settings, and Quick Look cover the app sidebar toggle with a live scrim; the prior sweep tried to click behind those overlays. The sweep now detects data-scrim-live and skips only that unavailable sidebar-open state. All guest routes (/login, /signup, /recover, /reenrol, /setup, /invite and public link) reported zero serious/critical in both themes and both viewport sizes. Final rebuilt verification is running.

Expanded full matrix found one serious Axe contrast violation in NoteMentionsCard empty states at 1440/light/collapsed: 4.33:1 while the shared 160 ms disclosure reveal faded all text. I changed the shared cal-reveal-in keyframe to keep text opaque while preserving the 4 px motion. On phones, Search, Notifications, agent turns, Settings, and Quick Look cover the app sidebar toggle with a live scrim; the prior sweep tried to click behind those overlays. The sweep now detects data-scrim-live and skips only that unavailable sidebar-open state. All guest routes (/login, /signup, /recover, /reenrol, /setup, /invite and public link) reported zero serious/critical in both themes and both viewport sizes. Final rebuilt verification is running.
Author
Owner

Resuming after the planned VM restart on branch job/a11y at 89d060f6 (merge-base with dev: b74e772c6d). The worktree is clean. The prior report says the expanded production Axe matrix was running after the contrast fix, guest-route scan and overlay-state handling; I am rerunning that matrix from the committed checkpoint, then I will merge dev and run the final gates.

Resuming after the planned VM restart on branch job/a11y at 89d060f6 (merge-base with dev: b74e772c6df6f45d187be8a456144c810630a6ca). The worktree is clean. The prior report says the expanded production Axe matrix was running after the contrast fix, guest-route scan and overlay-state handling; I am rerunning that matrix from the committed checkpoint, then I will merge dev and run the final gates.
Author
Owner

Production sweep finding: all completed route/theme/viewport Axe scans reported zero serious or critical violations, but the keyboard journey failed after reaching the Composer. It typed 09:00 Accessibility keyboard audit #a11y, pressed Ctrl+Enter, then timed out after 30 seconds waiting for the dialog to hide at apps/web/e2e/a11y.mjs:218; the Composer creation check and later mode-navigation check did not run. I am tracing the submission state on the production build before deciding whether the test or UI behavior needs a fix.

Production sweep finding: all completed route/theme/viewport Axe scans reported zero serious or critical violations, but the keyboard journey failed after reaching the Composer. It typed `09:00 Accessibility keyboard audit #a11y`, pressed Ctrl+Enter, then timed out after 30 seconds waiting for the dialog to hide at `apps/web/e2e/a11y.mjs:218`; the Composer creation check and later mode-navigation check did not run. I am tracing the submission state on the production build before deciding whether the test or UI behavior needs a fix.
Author
Owner

Root cause confirmed in the production page: the browser runs at 01:09 CEST, while the keyboard fixture typed 09:00 for today. The parser treated it as a future Log. With no calendar connected, Composer correctly stayed open and announced A log entry is for now or earlier. Connect a calendar to plan events. The adjacent Composer e2e uses yesterday for this reason. I will make the a11y fixture explicitly past-dated and assert that the journal API contains the saved Log entry.

Root cause confirmed in the production page: the browser runs at 01:09 CEST, while the keyboard fixture typed `09:00` for today. The parser treated it as a future Log. With no calendar connected, Composer correctly stayed open and announced `A log entry is for now or earlier. Connect a calendar to plan events.` The adjacent Composer e2e uses `yesterday` for this reason. I will make the a11y fixture explicitly past-dated and assert that the journal API contains the saved Log entry.
Author
Owner

Continuation after VM restart: branch job/a11y, checkpoint HEAD 91e53133e1973fae8b9be6b1e13021fcae351a54, merge-base with current dev b74e772c6df6f45d187be8a456144c810630a6ca. The worktree is clean. Prior report: the production Axe route/theme/viewport matrix completed without serious or critical findings; the keyboard journey stopped at Composer because its fixture was dated today. The checkpoint changes that fixture to yesterday and verifies the saved journal entry. I am resuming production verification, then I will merge current dev and run the final gates.

Continuation after VM restart: branch `job/a11y`, checkpoint HEAD `91e53133e1973fae8b9be6b1e13021fcae351a54`, merge-base with current `dev` `b74e772c6df6f45d187be8a456144c810630a6ca`. The worktree is clean. Prior report: the production Axe route/theme/viewport matrix completed without serious or critical findings; the keyboard journey stopped at Composer because its fixture was dated today. The checkpoint changes that fixture to yesterday and verifies the saved journal entry. I am resuming production verification, then I will merge current `dev` and run the final gates.
Author
Owner

Production Axe finding after merging current dev: /analytics at 1440 px, light theme, sidebar open reports serious color-contrast violations. .svelte-10vq0n5.empty-msg has foreground #b1b5b9 on #ffffff at 2.06:1. .empty has foreground #92979d on #ffffff at 2.94:1. Both are below the 4.5:1 text threshold. The expanded route matrix now covers Analytics, Ask, all Settings sections including Admin/System, and Calendar redirect routes. I will trace these selectors to their shared component and fix the contrast before rerunning the production sweep.

Production Axe finding after merging current `dev`: `/analytics` at 1440 px, light theme, sidebar open reports serious `color-contrast` violations. `.svelte-10vq0n5.empty-msg` has foreground `#b1b5b9` on `#ffffff` at 2.06:1. `.empty` has foreground `#92979d` on `#ffffff` at 2.94:1. Both are below the 4.5:1 text threshold. The expanded route matrix now covers Analytics, Ask, all Settings sections including Admin/System, and Calendar redirect routes. I will trace these selectors to their shared component and fix the contrast before rerunning the production sweep.
Author
Owner

The Analytics empty states also fail in the dark theme: at /analytics, 1440 px, .svelte-10vq0n5.empty-msg is #656a83 on #202330 (2.93:1), and .empty is #8086a4 on #202330 (4.36:1). Both are below the 4.5:1 requirement. Source review found opacity reductions on the Analytics empty labels in TimeOfDayHeatmap.svelte and CalendarHeatmap.svelte; I removed those reductions and kept the theme's muted text color.

The Analytics empty states also fail in the dark theme: at `/analytics`, 1440 px, `.svelte-10vq0n5.empty-msg` is `#656a83` on `#202330` (2.93:1), and `.empty` is `#8086a4` on `#202330` (4.36:1). Both are below the 4.5:1 requirement. Source review found opacity reductions on the Analytics empty labels in `TimeOfDayHeatmap.svelte` and `CalendarHeatmap.svelte`; I removed those reductions and kept the theme's muted text color.
Author
Owner

The rebuilt production Files-focused sweep passes after aligning overlay checks with the merged shortcut registry and menu roles: A11Y PASSED: 25 axe scans, keyboard and reduced-motion checks. Desktop and phone Files/Recent/Trash routes and the palette, menu, context menu, share dialog, Composer, and Inspector each report zero serious/critical Axe violations. Keyboard journeys pass for focus containment and Escape restoration, sidebar resizing, file move, sharing, setting changes, Log creation, and mode navigation. I am checking the Analytics contrast fix in its own focused production pass before committing it.

The rebuilt production Files-focused sweep passes after aligning overlay checks with the merged shortcut registry and menu roles: `A11Y PASSED: 25 axe scans, keyboard and reduced-motion checks`. Desktop and phone Files/Recent/Trash routes and the palette, menu, context menu, share dialog, Composer, and Inspector each report zero serious/critical Axe violations. Keyboard journeys pass for focus containment and Escape restoration, sidebar resizing, file move, sharing, setting changes, Log creation, and mode navigation. I am checking the Analytics contrast fix in its own focused production pass before committing it.
Author
Owner

Completed

  • Branch: job/a11y. Final HEAD: b3d31642093eed803ff079baea29015c9dc99e53.
  • Merged dev at cf142cb41e69334705b32f72292daa0ac4c45d5a into the job branch and resolved the overlapping file-browser and E2E changes with both sides in mind.
  • Added a production-build accessibility sweep for the current routes and states. It checks desktop and 390px layouts, light and dark themes, signed-out and public-link pages, axe findings, keyboard journeys, Escape and focus restoration, resize separators, reduced motion, file move and share flows, Settings, Composer Log, and mode navigation.
  • Fixed both analytics empty states. Removed opacity values that lowered text contrast below WCAG AA.
  • Added the axe E2E job to CI. The production sweep completed with: A11Y PASSED: 550 axe scans, keyboard and reduced-motion checks.
  • Attached production screenshots in the job report for the visual reviewer.

Files

.forgejo/workflows/ci.yml; apps/web/e2e/a11y.mjs; apps/web/package.json; apps/web/src/calternal-app.css; apps/web/src/lib/components/analytics/CalendarHeatmap.svelte; apps/web/src/lib/components/analytics/TimeOfDayHeatmap.svelte; apps/web/src/lib/files/FilesBrowser.svelte; apps/web/src/lib/files/transfer.ts; apps/web/src/lib/notes/NotesExplorer.svelte; apps/web/src/lib/themes.test.ts; bun.lock; crates/plugins/files/src/index.rs; packages/ui/src/components/TabBar.svelte; packages/ui/src/components/TagPill.svelte; packages/ui/src/components/calendar/MiniMonth.svelte; packages/ui/src/components/calendar/MonthGrid.svelte; packages/ui/src/components/calendar/YearHeatmap.svelte.

Gates

  • cargo fmt --check: exit 0, no output.
  • cargo clippy --all-targets -- -D warnings:
    Finished \dev` profile [unoptimized + debuginfo] target(s) in 11m 02s`
  • cargo test: all 69 test targets passed; 1,236 passed, 0 failed, 12 ignored. Exact harness output included:
    test result: ok. 477 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.90s
  • bun run check:
    svelte-check found 0 errors and 0 warnings
  • bun run test:
    Test Files 69 passed (69)
    Tests 539 passed (539)
  • Production build passed (bun run build; cargo build -p calternal-server).
  • cargo clean: Removed 16188 files, 13.8GiB total. Removed apps/web/build and apps/web/.svelte-kit after attaching screenshots.

Known gaps

  • No live NVDA, VoiceOver, or TalkBack session was run. The audit checks accessibility semantics with axe and keyboard behavior in Chromium. The 390px runs use a browser viewport, not a physical touch device.
  • The initial full-workspace cargo test attempt hit a stale sccache temp directory in a sibling worktree. Retried with RUSTC_WRAPPER=; the full suite passed.

Decisions not specified by DESIGN.md

  • Built the route matrix from the current SvelteKit route tree and current Settings registry, including the merged Analytics, Ask, Admin, and System routes.
  • Used the current registered inspector shortcut, Ctrl+Shift+period, in the keyboard journey.
  • The Composer Log fixture uses yesterday because the current API rejects future log entries when there is no connected calendar.
  • The dense date-grid axe exemption remains limited to data-hit-exempt="date-grid", as documented in DESIGN.md §35.
## Completed - Branch: `job/a11y`. Final HEAD: `b3d31642093eed803ff079baea29015c9dc99e53`. - Merged `dev` at `cf142cb41e69334705b32f72292daa0ac4c45d5a` into the job branch and resolved the overlapping file-browser and E2E changes with both sides in mind. - Added a production-build accessibility sweep for the current routes and states. It checks desktop and 390px layouts, light and dark themes, signed-out and public-link pages, axe findings, keyboard journeys, Escape and focus restoration, resize separators, reduced motion, file move and share flows, Settings, Composer Log, and mode navigation. - Fixed both analytics empty states. Removed opacity values that lowered text contrast below WCAG AA. - Added the axe E2E job to CI. The production sweep completed with: `A11Y PASSED: 550 axe scans, keyboard and reduced-motion checks`. - Attached production screenshots in the job report for the visual reviewer. ## Files `.forgejo/workflows/ci.yml`; `apps/web/e2e/a11y.mjs`; `apps/web/package.json`; `apps/web/src/calternal-app.css`; `apps/web/src/lib/components/analytics/CalendarHeatmap.svelte`; `apps/web/src/lib/components/analytics/TimeOfDayHeatmap.svelte`; `apps/web/src/lib/files/FilesBrowser.svelte`; `apps/web/src/lib/files/transfer.ts`; `apps/web/src/lib/notes/NotesExplorer.svelte`; `apps/web/src/lib/themes.test.ts`; `bun.lock`; `crates/plugins/files/src/index.rs`; `packages/ui/src/components/TabBar.svelte`; `packages/ui/src/components/TagPill.svelte`; `packages/ui/src/components/calendar/MiniMonth.svelte`; `packages/ui/src/components/calendar/MonthGrid.svelte`; `packages/ui/src/components/calendar/YearHeatmap.svelte`. ## Gates - `cargo fmt --check`: exit 0, no output. - `cargo clippy --all-targets -- -D warnings`: `Finished \`dev\` profile [unoptimized + debuginfo] target(s) in 11m 02s` - `cargo test`: all 69 test targets passed; 1,236 passed, 0 failed, 12 ignored. Exact harness output included: `test result: ok. 477 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.90s` - `bun run check`: `svelte-check found 0 errors and 0 warnings` - `bun run test`: `Test Files 69 passed (69)` `Tests 539 passed (539)` - Production build passed (`bun run build`; `cargo build -p calternal-server`). - `cargo clean`: `Removed 16188 files, 13.8GiB total`. Removed `apps/web/build` and `apps/web/.svelte-kit` after attaching screenshots. ## Known gaps - No live NVDA, VoiceOver, or TalkBack session was run. The audit checks accessibility semantics with axe and keyboard behavior in Chromium. The 390px runs use a browser viewport, not a physical touch device. - The initial full-workspace `cargo test` attempt hit a stale sccache temp directory in a sibling worktree. Retried with `RUSTC_WRAPPER=`; the full suite passed. ## Decisions not specified by DESIGN.md - Built the route matrix from the current SvelteKit route tree and current Settings registry, including the merged Analytics, Ask, Admin, and System routes. - Used the current registered inspector shortcut, Ctrl+Shift+period, in the keyboard journey. - The Composer Log fixture uses yesterday because the current API rejects future log entries when there is no connected calendar. - The dense date-grid axe exemption remains limited to `data-hit-exempt="date-grid"`, as documented in DESIGN.md §35.
kayg referenced this issue from a commit 2026-09-27 09:07:19 +00:00
Author
Owner

Merged into dev at ac6d1444 (550 axe scans, keyboard and reduced-motion journeys pass; fixes in shared components). Deploy status on #203.

Merged into dev at ac6d1444 (550 axe scans, keyboard and reduced-motion journeys pass; fixes in shared components). Deploy status on #203.
kayg closed this issue 2026-09-27 09:07:21 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#162
No description provided.