Skip to content

Commit 8670454

Browse files
committed
a11y: respect prefers-reduced-motion for app animations
Add @media (prefers-reduced-motion: reduce) rule in globals.css that disables animations/transitions globally. Spinner role=status semantics remain intact. Document reduced-motion support in README.
1 parent bbd781b commit 8670454

3 files changed

Lines changed: 31 additions & 0 deletions

File tree

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,21 @@ The frontend communicates with the StableRoute API backend.
8484
| `npm test` | Run Jest tests |
8585
| `npm run lint` | Next.js ESLint |
8686

87+
## Accessibility
88+
89+
### Reduced Motion
90+
91+
The app respects the user's `prefers-reduced-motion` OS setting. When enabled, all CSS animations and transitions are disabled via a global media query in [`src/app/globals.css`](src/app/globals.css). The loading spinner retains its `role="status"` semantics so screen-reader users still receive feedback.
92+
93+
To test:
94+
- **Windows:** Settings → Ease of Access → Display → "Show animations in Windows"
95+
- **macOS:** System Settings → Accessibility → Display → "Reduce motion"
96+
- **Browser DevTools:** Ctrl+Shift+P → "Show Rendering" → Emulate CSS media feature `prefers-reduced-motion: reduce`
97+
98+
### ARIA Live Regions
99+
100+
Dynamic list updates (loading → loaded / loading → empty) on the pairs, events, api-keys, and webhooks pages are wrapped in `aria-live="polite"` regions so screen-reader users are notified when content arrives. Error messages continue to use `role="alert"` for assertive announcements.
101+
87102
## CI/CD
88103

89104
On every push/PR to `main`, GitHub Actions runs:

src/app/globals.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,13 @@ body {
1111
color: var(--foreground);
1212
background: var(--background);
1313
}
14+
15+
@media (prefers-reduced-motion: reduce) {
16+
*,
17+
*::before,
18+
*::after {
19+
animation-duration: 0.01ms !important;
20+
animation-iteration-count: 1 !important;
21+
transition-duration: 0.01ms !important;
22+
}
23+
}

src/components/__tests__/Spinner.test.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,10 @@ describe("Spinner", () => {
1111
render(<Spinner label="Fetching pairs" />);
1212
expect(screen.getByText(/Fetching pairs/)).toBeInTheDocument();
1313
});
14+
it("still announces its status role even when reduced-motion is active", () => {
15+
render(<Spinner />);
16+
const status = screen.getByRole("status");
17+
expect(status).toBeInTheDocument();
18+
expect(status).toHaveTextContent(/Loading/);
19+
});
1420
});

0 commit comments

Comments
 (0)