How to build accessible web applications: a practical frontend tutorial
Creating accessible web experiences: a practical guide from day one
WCAG foundations and semantic HTML
- Start with semantic elements: use header, nav, main, article, section, aside, footer to convey structure. Screen readers rely on landmarks to navigate quickly.
- Use proper headings: a real document outline requires h1 through h6 in logical order. One h1 per page, then descending headings to create sections.
- Prefer native semantics over custom roles: many roles (e.g., role="navigation") are unnecessary if you’ve used a semantic element. Reserve ARIA for cases where native HTML can’t express the meaning.
- Ensure meaningful content order: the DOM order should reflect the visual order. If you must change ordering for styling, use CSS but keep the reading order intact or provide a logical outline via ARIA when needed.
ARIA: roles, properties, and when to use them
- Roles: use ARIA roles only to fill gaps left by native HTML. Overusing roles can confuse assistive technologies.
- Landmarks: use landmark roles (role="navigation", role="main", role="contentinfo") only when native elements aren’t available or when you’re building non-semantic wrappers.
- States and properties: keep ARIA states (aria-pressed, aria-expanded, aria-selected) in sync with the actual UI state. Do not mark non-interactive elements as interactive with ARIA unless necessary.
- Relationships: use aria-labelledby and aria-describedby to provide clear labeling and descriptions when native labeling is insufficient.
Keyboard navigation and focus management
- Ensure all interactive elements are focusable: links, buttons, inputs, selects, textareas, and any custom controls must be operable via keyboard (Tab/Shift+Tab, Enter/Space where appropriate).
- Logical focus order: tab order should follow the visual reading order. Use CSS for layout, not tab order hacks.
- Visible focus indicators: provide clear focus styles (outline or custom focus ring) that meet contrast requirements. Avoid removing focus indicators for interactive controls.
- Accessible components: build or adopt components with proper keyboard support (e.g., dropdowns open with Enter/Space, menus navigable with arrow keys).
Color contrast and visual accessibility
- Contrast ratios: text and images of text should have a contrast ratio of at least 4.5:1 for normal text and 3:1 for large text (WCAG 2.1 AA). For UI components, ensure sufficient contrast between text/icons and their backgrounds.
- Non-text content: provide text alternatives for meaningful images; decorative images can be ignored by screen readers but still respect contrast in surrounding UI.
- Color as information: never rely on color alone to convey essential information. Use text labels, icons, or patterns to differentiate states (e.g., red for error with explicit error text).
Accessible forms and validation
- Labels and instructions: every form control must have an associated label. Use and matching id attributes.
- Fieldsets and legends: group related controls with and describe them with a .
- Inline validation: provide immediate feedback with accessible messages (aria-live="polite" or role="status"). Ensure the message is programmatically associated with the control (aria-describedby).
- Error handling: when errors occur, place focus on the first invalid control and provide a clear, actionable message describing how to fix it.
Testing: automated tools and manual testing
- Automated audits: run tools like Lighthouse, axe-core (accessible via browser dev tools), and WAVE to catch obvious issues (contrast, missing labels, semantic HTML gaps).
- Manual checks: skim by keyboard only (no mouse) to verify focus order, visibility, and interactivity. Use a screen reader (NVDA, JAWS on Windows; VoiceOver on macOS/iOS) to test real-world usage.
- Realistic content tests: ensure form submissions announce success or errors to assistive tech; verify dynamic content announces changes (aria-live regions) without causing excessive noise.
- Cross-browser and cross-device checks: test on desktop and mobile, with different browsers to catch quirks in assistive technology behavior.
Building an accessibility mindset from the start
- Treat accessibility as a default, not an afterthought: embed semantic HTML and accessible patterns in the design system.
- Document accessibility decisions: record why you used certain ARIA roles, labeling, or contrast choices in your project docs.
- Prioritize progressive enhancement: start with a solid accessible baseline in plain HTML, then enhance with CSS/JS without breaking accessibility.
- Involve users with disabilities: gather feedback from real users or accessibility experts during usability testing.
Real-world examples and patterns
- Accessible modal dialog: use aria-modal="true" or role="dialog" with proper aria-labelledby, trap focus within the modal, and restore focus to the triggering element on close.
- Accessible tabs: implement a tablist with role="tablist", role="tab", and role="tabpanel". Manage focus with arrow keys and ensure the active tab has aria-selected="true".
- Live region updates: show status messages in an element with aria-live="polite" and aria-atomic="true" to announce changes without interrupting the user.
Quick-start checklist
- Semantic HTML first: replace non-semantic wrappers with appropriate HTML elements.
- Labels and instructions: ensure every form control has a visible label and descriptive helper text.
- Keyboard happy: confirm all interactive UI is reachable and operable with the keyboard, with clear focus styles.
- Color and contrast: verify text and interactive elements meet contrast thresholds; provide non-color cues.
- Test plan: run automated checks, then perform manual keyboard and screen reader testing; fix issues iteratively.
If you’d like, I can tailor this into a ready-to-use checklist for your project or draft a minimal accessible component (like a modal or a form) with code samples and explanations. Would you prefer a JavaScript-free baseline example or a lightweight React/Vue component with accessibility built in?
-
Rizwan Saleem | https://rizwansaleem.co
Top comments (0)