This article provides practical guidance for experienced developers on how to build accessible web applications. It covers how to recognize, prioritize, and fix the most common and critical accessibility issues in line with WCAG 2.1 AA standards.
Why accessibility matters
Accessibility is the practice of ensuring your digital products are usable by everyone, regardless of their abilities. It's not just a compliance requirement; it's a core component of creating high-quality products.
- Human impact: Approximately 1 in 4 adults in the U.S. has a disability. Inaccessible products can exclude them from essential services, information, and opportunities.
- Business impact: An accessible product reaches a larger audience, improves the user experience for everyone, and enhances your brand's reputation.
- Legal impact: Laws like the Americans with Disabilities Act (ADA) require digital properties to be accessible, and non-compliance can carry legal risks.
Core principles: The critical four
Focus on these four areas to prevent the most significant barriers for users.
Keyboard accessibility
This is the foundation of an accessible website. If a component works with a mouse, it must also work with a keyboard.
- Visible focus: A clear visual indicator must always be present on the interactive element that currently has keyboard focus. Never use outline: none; without providing a highly visible alternative style for the :focus state.
- Keyboard traps: A user must be able to navigate into and, crucially, out of any component using only the keyboard. A common failure is trapping users inside a modal window.
Semantic HTML
Using HTML elements for their intended purpose provides a huge amount of accessibility for free. Screen readers rely on the HTML structure to give users context and navigational shortcuts.
- Headings (<h1>-<h6>): Use headings to create a logical outline for your page content. Do not skip heading levels (e.g., jumping from an <h1> to an <h3>), as this disrupts the document structure for screen reader users.
- Buttons vs. links: A link (<a>) navigates to a new location. A button (<button>) performs an action on the current page. Using a <div> with a click handler is not an acceptable substitute for a <button>, as it lacks the built-in focus behavior and semantics.
- Landmarks (<main>, <nav>, <header>, <footer>): Use these elements to define the major regions of your page. This allows assistive technology users to jump directly to the section they need.
It’s important to note that HTML elements have specific attributes and parent/child relationships that are allowed. If in doubt, refer to the Mozilla elements reference site.
Mozilla elements reference
Alternative text (alt attribute) provides a textual description of an image for users who cannot see it.
- Informative images: If an image conveys information (e.g., a chart, a product photo), the alt text must describe its content and purpose. (e.g., alt="Red running shoe with white laces, side view.").
- Decorative images: If an image is purely decorative and adds no informational value, it must still have an alt attribute, but it should be empty (alt=""). This tells the screen reader to ignore it.
Programmatic labels and names
Every interactive control (inputs, buttons, links) must have a name that assistive technology can announce.
- Form inputs: The element, correctly associated with an input via the for and id attributes, is the standard and best way to provide a name. A placeholder is not a substitute for a label.
- Icon-only buttons: An icon by itself is meaningless to a screen reader. You must provide a programmatic name using aria-label or visually hidden text.
Essential tools 🛠️
Integrate these tools into your development workflow to efficiently find and fix issues.
- Automated checkers: Browser extensions like axe DevTools are excellent for catching about 30-50% of common issues, such as most color contrast failures or missing form labels. The axe DevTools extension is built on axe-core and rarely has false positives. Do not rely on WAVE. It often has false positives.
- Manual checks: Your keyboard is your most powerful testing tool. Navigate through your page using only the keyboard (Tab, Shift+Tab, Enter, Space, arrow keys). Can you see where you are and operate every control? The ANDI accessibility tool provides valuable information as you test manually.
- Assistive technology: The best way to understand the user experience is to test with a screen reader. NVDA (free for Windows) and VoiceOver (built-in on macOS/iOS) are the most common.
- Color contrast: The WebAIM Contrast Checker can be used from a website and does not require an installation on your system. It is also free to use.
Common mistakes and best practices
Hiding content correctly: aria-hidden vs. CSS
How you hide off-screen content (like in modals or accordions) has major accessibility implications.
- display: none;: This is the correct choice for most use cases. It hides the element visually, removes it from the page layout, and hides it from screen readers.
- aria-hidden="true": This only hides an element from the accessibility tree (screen readers). The element remains visible and can still be interactive.
The danger zone ⚠️
Never use aria-hidden="true" on a focusable, interactive element. A sighted keyboard user can still tab to it, but a screen reader user will skip right over it, creating a completely broken experience.
The golden rule for hiding content:
- To hide something completely from all users: Use display: none;.
- To hide something from screen readers only (because it is purely decorative or redundant to adjacent text): Use aria-hidden="true".
| Property | Visually hidden? | Removed from layout? | Hidden from screen reader? | Interactive? |
| display: none; | Yes | Yes | Yes | No |
| visibility: hidden; | Yes | No | Yes | No |
| aria-hidden="true"; | No | No | Yes | Yes |
Providing context with ARIA (aria-label, labelledby, describedby)
Use ARIA to fill semantic gaps, not to duplicate information that is already present and correctly associated (e.g., a button with visible text, a properly linked <label>).
- aria-label: Provides a string as the accessible name when no visible label exists. It overrides any text content inside the element. This is perfect for icon-only buttons.
HTML
HTML
HTML
Accessibility in data visualizations
For data-heavy applications, ensuring reports are accessible is critical.
- Use high-contrast colors or patterns with a legend: Start with a high-contrast theme from the View tab.
- Add alt text to visuals: Go to Format → General → Alt Text and describe the chart's purpose and key insight.
- Set a logical tab order: Use the View → Selection panel to drag elements into a logical top-to-bottom, left-to-right order.
- Avoid relying on color alone: Use patterns, markers, and direct data labels in addition to color to convey information.
- Avoid using tables for everything: Nested tables are very difficult or impossible to navigate with a screen reader.
Workflow: Prioritizing and documenting issues
Once issues are found, use Severity and Reach to prioritize them.
- Severity: How much does this block a user from completing a task? (Critical, Serious, Moderate, Minor).
- Reach: How many users will encounter this? (High, Medium, Low).
A Critical issue with High Reach (e.g., a keyboard trap on the login form) is always the highest priority.
Writing an effective Jira ticket
A good ticket accelerates the fix. Include these five elements:
- Clear, Actionable Title: "Login Modal is a Keyboard Trap"
- Location: URL the issue occurred on
- Severity Level: Critical, Serious, Moderate, or Minor
- Reach: High, Medium, Low
- User impact: "Keyboard-only users cannot access the login form fields."
- Steps to Reproduce (STR): Clear, numbered steps.
- xpected vs. Actual Results: What should happen vs. what currently happens.
- Acceptance Criteria: A clear definition of "done."
Practical example: Finding and fixing bugs
This case study demonstrates how to apply these principles. Use your IDE or a site like CodePen to enter the code and see the results.
The problematic component ("before")
This component uses non-semantic elements, is missing key attributes, and has CSS that harms accessibility.
HTML
The accessible solution ("after")
The fixes use semantic HTML, ARIA for context, and accessibility-friendly CSS and JavaScript.