Build & Push Docker Image / build (push) Has been cancelled
Refresh the homepage around new hero/services/roadmap sections, shared design tokens, and updated brand assets. Co-authored-by: Cursor <cursoragent@cursor.com>
88 lines
2.7 KiB
Markdown
88 lines
2.7 KiB
Markdown
# Accessibility
|
||
|
||
Accessibility is priority #1 in the design system hierarchy. Visual polish never overrides perceivable, operable UI.
|
||
|
||
## Color contrast
|
||
|
||
- Body text (`foreground` on `background`) and muted text must remain readable in both themes.
|
||
- Do not place `muted-foreground` on muted backgrounds without checking contrast.
|
||
- Primary buttons use white label on `#29a19c`—do not lower primary opacity on labels.
|
||
- Never convey meaning by color alone (pair with text/icon).
|
||
|
||
## Focus ring
|
||
|
||
Visible focus is mandatory for keyboard users.
|
||
|
||
| Control | Pattern |
|
||
| --- | --- |
|
||
| Button | `focus-visible:ring-[3px] focus-visible:ring-ring/40 focus-visible:border-ring` |
|
||
| Input | `focus-visible:ring-[3px] focus-visible:ring-primary/25 focus-visible:border-primary` |
|
||
| Generic | Prefer ring tokens; never `outline-none` without replacement |
|
||
|
||
Do not remove focus styles from cards/links that are interactive.
|
||
|
||
## Keyboard navigation
|
||
|
||
- All buttons, links, and controls reachable via Tab.
|
||
- Menus / sheets / dialogs use Radix primitives (focus trap + Escape).
|
||
- Maintain logical RTL tab order; avoid positive `tabIndex` except when necessary.
|
||
- Icon-only controls need accessible names.
|
||
|
||
## ARIA expectations
|
||
|
||
| Case | Requirement |
|
||
| --- | --- |
|
||
| Icon button | `aria-label` |
|
||
| Decorative icon/image | `aria-hidden` / empty `alt` |
|
||
| Expandable | `aria-expanded` + controls id |
|
||
| Dialogs / sheets | Titled (`SheetTitle`, `DialogTitle`) — `sr-only` if visually hidden |
|
||
| Invalid fields | `aria-invalid` + error text |
|
||
| Landmark nav | `aria-label` on primary `<nav>` |
|
||
| Decorative sections | `aria-hidden` on `.section-decor` |
|
||
|
||
Prefer native semantics (`button`, `a`, `label`) before ARIA.
|
||
|
||
## Reduced motion
|
||
|
||
- Honor `prefers-reduced-motion`.
|
||
- `MotionReveal` already disables motion.
|
||
- New CSS animations need a reduced-motion override.
|
||
- Do not autoplay motion-critical content.
|
||
|
||
## Touch targets
|
||
|
||
| Control | Minimum |
|
||
| --- | --- |
|
||
| Default button | 40×40 (`h-10`) |
|
||
| Large button | 48×48 |
|
||
| Icon button | 40×40 |
|
||
| Compact button | 32×32 — avoid for primary mobile actions |
|
||
| Input | ≥ 36px height (`h-9`); 48px when paired with lg submit |
|
||
|
||
## Forms
|
||
|
||
- Visible labels (not placeholder-only).
|
||
- Error text associated with fields.
|
||
- Preserve browser autofill / zoom-safe 16px text on mobile inputs.
|
||
|
||
## Images & media
|
||
|
||
- Meaningful `alt`.
|
||
- Do not flash high-contrast animations.
|
||
|
||
## Checklist snippet
|
||
|
||
- [ ] Keyboard only can complete the flow
|
||
- [ ] Focus visible on all interactive elements
|
||
- [ ] Contrast OK in light and dark
|
||
- [ ] Reduced motion OK
|
||
- [ ] SR labels on icon buttons
|
||
- [ ] No keyboard trap outside modals
|
||
|
||
## Related
|
||
|
||
- [forms.md](./forms.md)
|
||
- [motion.md](./motion.md)
|
||
- [dark-mode.md](./dark-mode.md)
|
||
- Cursor rule: `.cursor/rules/accessibility.mdc`
|