DEV Community

Rizwan Saleem
Rizwan Saleem

Posted on

How to build accessible web applications: a practical frontend tutorial

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)