diff --git a/docs/AccessibilityGuide.md b/docs/AccessibilityGuide.md new file mode 100644 index 00000000..5a2c4ac8 --- /dev/null +++ b/docs/AccessibilityGuide.md @@ -0,0 +1,353 @@ +# Accessibility Guide + +DocuElevate targets **WCAG 2.1 Level AA** compliance across all user-facing pages. This guide documents the accessibility standards, patterns, and practices used in the project. + +## Table of Contents + +- [Standards Overview](#standards-overview) +- [ARIA Patterns Used](#aria-patterns-used) +- [Keyboard Navigation](#keyboard-navigation) +- [Color and Contrast](#color-and-contrast) +- [Forms and Inputs](#forms-and-inputs) +- [Tables](#tables) +- [Modals and Dialogs](#modals-and-dialogs) +- [Dynamic Content](#dynamic-content) +- [Images and Icons](#images-and-icons) +- [Testing and Validation](#testing-and-validation) +- [Developer Checklist](#developer-checklist) + +## Standards Overview + +DocuElevate follows [WCAG 2.1 Level AA](https://www.w3.org/WAI/WCAG21/quickref/?currentsidebar=%23col_overview&levels=aaa) guidelines. The key principles are: + +| Principle | Description | +|-----------|-------------| +| **Perceivable** | Information and UI components must be presentable in ways all users can perceive | +| **Operable** | UI components and navigation must be operable via keyboard and assistive technologies | +| **Understandable** | Information and UI operation must be understandable | +| **Robust** | Content must be robust enough for a wide variety of user agents and assistive technologies | + +## ARIA Patterns Used + +### Landmarks + +`base.html` provides the following landmark structure on every page: + +```html + + +
{% block content %}{% endblock %}
+ +``` + +### Navigation + +- Active page links use `aria-current="page"` +- The admin dropdown uses `aria-haspopup="true"`, `aria-expanded`, `role="menu"`, and `role="menuitem"` +- The mobile menu toggle has `aria-label="Toggle navigation menu"` and `aria-expanded` + +### Decorative Icons + +Font Awesome icons that appear next to descriptive text must have `aria-hidden="true"`: + +```html + + + Settings + + + + +``` + +## Keyboard Navigation + +### Skip Link + +Every page inherits a skip-to-content link from `base.html` that becomes visible on keyboard focus: + +```css +.skip-link:focus { + position: fixed; + top: 0; + left: 0; + /* ... visible styles ... */ +} +``` + +### Focus Indicators + +Global `focus-visible` styles are defined in `frontend/static/styles.css`: + +```css +a:focus-visible, +button:focus-visible, +input:focus-visible, +select:focus-visible, +textarea:focus-visible, +[tabindex]:focus-visible { + outline: 2px solid #2563eb; + outline-offset: 2px; +} +``` + +> **Warning:** Never remove or suppress focus indicators with `outline: none` unless you provide an equally visible alternative. + +### Interactive Custom Elements + +When using `
` or `` as interactive elements (avoid when possible), ensure: + +```html +
+``` + +## Color and Contrast + +### Minimum Ratios (WCAG 1.4.3) + +| Text Type | Minimum Contrast Ratio | +|-----------|----------------------| +| Normal text (< 18pt) | 4.5:1 | +| Large text (≥ 18pt or ≥ 14pt bold) | 3:1 | +| UI components and graphical objects | 3:1 | + +### Dark Mode + +Dark mode overrides in `styles.css` are verified for WCAG AA contrast ratios. When adding new color values: + +1. Verify light mode contrast at [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/) +2. Verify dark mode contrast for the corresponding dark override +3. Never rely on color alone — always pair with text, icons, or patterns + +### Status Badges + +Status indicators use both color **and** text: + +```html +Completed +Failed +``` + +## Forms and Inputs + +### Labels (WCAG 1.3.1, 3.3.2) + +Every form input **must** have an associated label: + +```html + + + + + + +``` + +### Error Messages (WCAG 3.3.1) + +Error messages must be announced to screen readers: + +```html + +``` + +### Search Forms + +Use `role="search"` and proper labelling: + +```html + +``` + +## Tables + +### Required Structure (WCAG 1.3.1) + +```html + + + + + + + + + + + + + + + +
FilenameSizeStatus
document.pdf1.2 MBCompleted
+``` + +### Sortable Columns + +Add `aria-sort` to indicate the current sort state: + +```html + + Filename + +``` + +## Modals and Dialogs + +### Required Attributes (WCAG 4.1.2) + +```html + +``` + +### Focus Management + +When opening a modal: +1. Move focus into the dialog (first focusable element or the dialog itself) +2. Trap focus within the dialog while open +3. Return focus to the trigger element when closed + +## Dynamic Content + +### Live Regions (WCAG 4.1.3) + +Content that updates dynamically must be announced to screen readers: + +```html + +
+ +
+ + +
+ +
+ + +
+ +
+``` + +| Attribute | Use When | +|-----------|----------| +| `aria-live="polite"` | Updates that don't require immediate attention (search results, progress) | +| `aria-live="assertive"` | Critical updates that require immediate attention (errors) | +| `role="status"` | Status information (equivalent to `aria-live="polite"`) | +| `role="alert"` | Important messages that require immediate attention | + +## Images and Icons + +### Content Images + +```html +DocuElevate Logo +``` + +### Decorative Images + +```html + +``` + +### Icon-Only Buttons + +```html + +``` + +### Icons with Adjacent Text + +```html + + Upload + +``` + +## Testing and Validation + +### Automated Checks (CI Pipeline) + +The CI pipeline runs `djlint` on every pull request: + +```bash +# Run locally before committing +djlint frontend/templates/ --lint +``` + +Configuration is in `pyproject.toml` under `[tool.djlint]`. The linter enforces: + +| Rule | Description | +|------|-------------| +| H005 | `` tag must have `lang` attribute | +| H013 | `` tag must have `alt` attribute | +| H016 | Document must have `` tag | +| H025 | Tag should not be an orphan | +| H026 | Empty `id` and `class` attributes should be removed | + +### Manual Testing + +For each new feature or UI change, verify: + +1. **Keyboard navigation**: Tab through all interactive elements; confirm logical focus order +2. **Screen reader**: Test with a screen reader (VoiceOver on macOS, NVDA on Windows, Orca on Linux) +3. **Color contrast**: Check new color combinations with [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/) +4. **Zoom**: Test at 200% zoom; ensure content remains usable +5. **Responsive**: Test at mobile viewport widths (320px, 375px) + +### Browser Testing Tools + +- **Chrome DevTools Accessibility panel** — audit ARIA attributes and contrast +- **axe DevTools** browser extension — automated WCAG scanning +- **Lighthouse** — includes accessibility audit in Performance tab + +## Developer Checklist + +Use this checklist when creating or modifying UI templates: + +- [ ] All images have appropriate `alt` attributes +- [ ] All decorative icons have `aria-hidden="true"` +- [ ] All icon-only buttons have `aria-label` +- [ ] All form inputs have associated labels (via `for`/`id` or `aria-label`) +- [ ] All tables have `aria-label` or `<caption>` and `scope` on headers +- [ ] All modals have `role="dialog"`, `aria-modal="true"`, and `aria-labelledby` +- [ ] Dynamic content areas have `aria-live="polite"` or appropriate role +- [ ] Color contrast meets 4.5:1 for normal text, 3:1 for large text +- [ ] All interactive elements are keyboard-accessible +- [ ] Focus indicators are visible on all interactive elements +- [ ] `djlint frontend/templates/ --lint` passes with zero errors +- [ ] Page heading hierarchy is correct (h1 → h2 → h3, no skipped levels)