Accessibility (A11Y) Guidelines
This document describes the accessibility standards and practices used in Whity’s web frontend.
Overview
Section titled “Overview”Whity’s web frontend is built to meet WCAG 2.1 AA standards, ensuring the application is usable by everyone, including people with disabilities.
Key Standards
Section titled “Key Standards”- WCAG 2.1 Level AA: All public and authenticated pages must comply
- Testing: Automated axe-core scanning + manual keyboard navigation
- Screen Readers: Tested with NVDA (Windows), VoiceOver (Mac)
- RTL Support: Full Arabic/RTL keyboard navigation support
Focus Management
Section titled “Focus Management”- All interactive elements (buttons, links, inputs) are keyboard accessible
- Focus order follows visual layout (left-to-right, top-to-bottom)
:focus-visiblestyles provide clear focus indicators (outline or ring)- Modal dialogs trap focus within the dialog until dismissed
- Escape key closes modals and returns focus to the trigger
Semantic Markup
Section titled “Semantic Markup”- Use proper HTML elements:
<button>,<a>,<label>,<input>,<table>, not divs withroleattributes - Heading hierarchy: H1 → H2 → H3 (no skips)
- Lists use
<ul>,<ol>,<li>elements - Tables use
<table>,<thead>,<tbody>,<th>,<td>withscopeattributes
ARIA Attributes
Section titled “ARIA Attributes”Form Elements
Section titled “Form Elements”- All
<input>and<textarea>have associated<label>viahtmlFor - Error messages linked via
aria-describedby aria-invalid="true"when input is invalidaria-labelfor icon-only buttons
Navigation
Section titled “Navigation”aria-current="page"on the current navigation item- Skip links use
aria-label="Skip to main content"
Dynamic Content
Section titled “Dynamic Content”aria-live="polite"on regions that update without navigationaria-live="assertive"for high-priority announcements (rare)role="status"for loading messages, error alerts
Complex Widgets
Section titled “Complex Widgets”aria-expanded="true|false"on toggles and collapsiblesaria-haspopup="menu|listbox|dialog"on buttons that open overlaysaria-owns="id"if the popup isn’t DOM-adjacent
Screen Reader Text
Section titled “Screen Reader Text”Use the sr-only utility class for content visible only to screen readers:
<span className="sr-only">Current page</span>The CSS rule (from Tailwind):
.sr-only { position: absolute; left: -10000px; width: 1px; height: 1px; overflow: hidden;}Use for:
- Additional context (e.g., “current page” marker on nav)
- Icon button labels (if no visible text)
- Status messages in loading states
Color Contrast
Section titled “Color Contrast”- Normal text: 4.5:1 (WCAG AA)
- Large text (18pt+): 3:1 (WCAG AA)
- All colors use design tokens, never hardcoded
Check contrast in DevTools Inspect > Styles or use axe DevTools browser extension.
- All inputs have associated labels
- Required fields marked with asterisk and
aria-required="true" - Error messages appear below the input, linked via
aria-describedby - Submit buttons have clear, descriptive labels (“Save”, “Sign in”, not “Submit”)
Example:
<Input id="email" type="email" label="Email" required errorText={errors.email} value={email} onChange={(e) => setEmail(e.target.value)}/>Tables
Section titled “Tables”<table>for tabular data (not divs or grids)<caption>oraria-labelto identify the table<th scope="col">for column headers<th scope="row">for row headers if present- Filter rows should have
aria-labelon their inputs
Keyboard Navigation
Section titled “Keyboard Navigation”All interactive elements must be reachable via Tab key. Avoid tabindex > 0 unless necessary.
Escape Key
Section titled “Escape Key”- Closes modals, dropdowns, popovers
- Returns focus to the trigger element
Enter / Space
Section titled “Enter / Space”- Activates buttons and links
- Submits forms (button with type=“submit”)
Arrow Keys
Section titled “Arrow Keys”- Navigation within custom widgets (tables, menus, tabs)
- Usually handled by headless UI libraries (Radix, Headless UI)
RTL Support (Arabic)
Section titled “RTL Support (Arabic)”- Tab order follows the visual direction (right-to-left in Arabic mode)
- Focus indicators work in RTL
- Logical properties (start/end) instead of left/right where possible
- Test keyboard nav in RTL mode using
dir="rtl"on<html>
Testing
Section titled “Testing”Automated (Axe)
Section titled “Automated (Axe)”Run the accessibility test suite:
npm run test:e2e -- tests/a11y.spec.tsThis scans all major pages for:
- Missing labels
- Missing aria attributes
- Color contrast issues
- Heading hierarchy
- Keyboard traps
- Etc.
Note: Axe can’t test all aspects (e.g., screen reader announcements, focus management edge cases). Manual testing is required.
Manual Testing
Section titled “Manual Testing”-
Keyboard-only navigation (5 min per page):
- Tab through the entire page
- Verify all interactive elements are reachable
- Verify focus is always visible
- Test Escape key on modals
-
Screen reader testing:
- Windows: Narrator (built-in) or NVDA (free)
- Mac: VoiceOver (built-in)
- Check form labels are read correctly
- Check error messages are announced
- Check button purposes are clear
-
Contrast checking:
- DevTools Inspect > Styles > color picker
- Axe DevTools browser extension
- Ensure light/dark mode both pass
Component Library
Section titled “Component Library”All components in packages/ui/ are built with a11y in mind:
- Button: Focus-visible styling, aria-busy for loading, aria-pressed for toggles
- Input: Label association, aria-invalid, aria-describedby for errors
- Dialog: Focus trap, Escape closes, focus returns to trigger
- Tabs: Keyboard nav (arrows, home/end), aria-selected
- Table: Semantic HTML, scope attributes, sr-only status updates
- etc.
Adding New Components
Section titled “Adding New Components”When creating new components:
- Use semantic HTML (
<button>,<input>,<label>, etc.) - Include
aria-labelor visible label - Test keyboard navigation
- Add focus-visible styles
- Test in automated axe scanner
- Test manually with screen reader
Known Limitations
Section titled “Known Limitations”- Recharts (charting library): Limited native a11y support; we mitigate with
aria-labelon chart containers - Dynamic content: Some toasts and notifications need manual testing to verify announcements
- Color-only indicators: We avoid relying on color alone; always include text or icons
Resources
Section titled “Resources”- WCAG 2.1 Guidelines
- ARIA Authoring Practices Guide
- MDN: Accessibility
- Axe DevTools — Browser extension for scanning
- WebAIM — Articles and testing guides
Continuous Improvement
Section titled “Continuous Improvement”Accessibility is ongoing. When you:
- Fix a violation, add a test to prevent regression
- Discover a new pattern, document it here
- Test with a real user’s screen reader, share findings
Contact
Section titled “Contact”For questions about accessibility, open an issue or reach out to the team.