# 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
Skip to main content{% 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
CompletedFailed
```
## 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
{{ error_message }}
```
### Search Forms
Use `role="search"` and proper labelling:
```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
```
### 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 `
` 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)