Accessibility in Web and Mobile: Building for Everyone

9/18/2025Forgeora Team
Accessibility in Web and Mobile: Building for Everyone

Practical techniques for WCAG 2.2 compliance, keyboard navigation, screen reader support, and inclusive design patterns.

Accessibility is the discipline of building digital products that work for people with disabilities—visual, auditory, motor, and cognitive. Beyond being the right thing to do, accessibility is increasingly legally required (ADA, European Accessibility Act) and improves the experience for all users: captions help in noisy environments, high contrast modes help in bright sunlight, keyboard navigation helps power users. ## Understanding the Disability Landscape **Visual impairments**: Users who are blind or have low vision rely on screen readers (NVDA, JAWS, VoiceOver, TalkBack) that convert visual UI to speech or braille output. Users with low vision may use zoom, high contrast modes, or custom color schemes. **Motor impairments**: Users who cannot use a mouse rely on keyboard navigation, switch access, voice control (Dragon NaturallySpeaking), or eye-tracking devices. Tap targets must be large enough for users with tremors or reduced motor precision. **Auditory impairments**: Deaf and hard-of-hearing users need captions for video content and visual alternatives to audio notifications. **Cognitive impairments**: Users with dyslexia, ADHD, or cognitive differences benefit from clear language, consistent layouts, generous white space, and interfaces that minimize cognitive load. ## Semantic HTML as the Foundation The foundation of web accessibility is correct semantic HTML. Most accessibility problems stem from using `<div>` and `<span>` for interactive elements that should use native elements: **Use native elements**: `<button>` for buttons (not `<div onclick>`), `<a href>` for navigation links, `<input>`, `<select>`, `<textarea>` for form controls. Native elements have built-in keyboard support, focus management, and ARIA roles that `<div>` lacks. **Heading hierarchy**: Every page has exactly one `<h1>`. Subheadings follow logical nesting (`<h2>` for major sections, `<h3>` for subsections). Screen reader users navigate pages by headings—a broken heading hierarchy makes pages impossible to navigate efficiently. **Landmark elements**: `<header>`, `<nav>`, `<main>`, `<aside>`, `<footer>` create navigable landmarks that screen reader users can jump between. Every page should have exactly one `<main>`. **Form labels**: Every form input must have an associated `<label>`. Use `htmlFor` to associate labels with inputs by ID. Placeholder text is not a substitute for a label—it disappears on input and has poor contrast. ## ARIA: When HTML Isn't Enough ARIA (Accessible Rich Internet Applications) attributes augment HTML when native semantics are insufficient for custom widgets. The first rule of ARIA: don't use ARIA if a native HTML element with the correct semantics exists. **`aria-label`**: Provides an accessible name for elements that don't have visible text. Use for icon-only buttons: `<button aria-label="Close dialog"><X /></button>`. **`aria-describedby`**: Links an element to a description. Use to connect form inputs to error messages or help text. **`aria-expanded`**: Indicates whether a collapsible element (accordion, dropdown, menu) is open. Update dynamically when state changes. **`aria-live`**: Announces dynamic content changes to screen readers. Use `aria-live="polite"` for non-urgent updates (item added to cart) and `aria-live="assertive"` for critical alerts (error messages). **`role="dialog"` and focus management**: Custom modals must have `role="dialog"`, an accessible name via `aria-labelledby`, and trap focus within the dialog while open. Return focus to the trigger element when the dialog closes. ## Keyboard Navigation Every interactive element must be keyboard accessible. Tab order should follow visual reading order (left-to-right, top-to-bottom). Test your entire product using only the keyboard: - `Tab`: Move focus to next interactive element - `Shift+Tab`: Move focus to previous interactive element - `Enter`/`Space`: Activate buttons and links - `Arrow keys`: Navigate within compound widgets (menus, tabs, radio groups) - `Escape`: Close dialogs, dropdowns, and tooltips Custom components like date pickers, data tables, and autocomplete must implement the appropriate keyboard interaction patterns defined in the ARIA Authoring Practices Guide (APG). **Focus indicators**: Never remove focus outlines without replacing them with a clearly visible alternative. `outline: none` without a replacement is an accessibility violation. Use `focus-visible` pseudo-class to show focus rings for keyboard users without showing them for mouse clicks. ## Color and Visual Design **Color contrast**: WCAG AA requires a contrast ratio of at least 4.5:1 for normal text and 3:1 for large text (18pt or 14pt bold). Use tools like Stark, Colour Contrast Analyser, or axe DevTools to audit contrast. Don't rely on color alone to convey information—use icons, labels, or patterns in addition to color for status indicators. **Responsive text**: Support browser text size scaling. Never set font sizes in pixels for body text—use `rem` units so system font size preferences are respected. Test with browser text zoomed to 200%. **Motion**: Users with vestibular disorders can be affected by animations and transitions. Respect the `prefers-reduced-motion` media query to reduce or eliminate motion for users who have indicated this preference. ## Automated and Manual Testing Automated tools (axe-core, Lighthouse accessibility audit, WAVE) catch approximately 30-40% of accessibility issues. They're essential but insufficient. Manual testing is required for the rest: - **Keyboard-only testing**: Navigate the entire product using only Tab, arrow keys, and Enter. Note every place where focus is lost, the order is confusing, or an element is unreachable. - **Screen reader testing**: Test with VoiceOver (macOS/iOS), NVDA/JAWS (Windows), and TalkBack (Android). These behave differently and surface different issues. - **User testing with disabled users**: Nothing replaces testing with real users. Engage disabled testers early in the product cycle, not as a final audit. Integrate axe-core into your testing suite to catch regressions automatically: ```typescript // Playwright accessibility test test('homepage has no accessibility violations', async ({ page }) => { await page.goto('/'); const accessibilityScanResults = await new AxeBuilder({ page }).analyze(); expect(accessibilityScanResults.violations).toEqual([]); }); ```