Skip to main content
Saved
Foundation

Accessibility as a Foundation

Why accessibility belongs in your architecture from day one instead of being retrofitted later.

Den Odell
By Den Odell Added
In this article
  1. TL;DR
  2. The Afterthought Problem
  3. Architecture, Not Decoration
  4. Component Hierarchy
  5. State Management
  6. Routing
  7. Data Fetching
  8. The Progressive Enhancement Connection
  9. When You Abandon Semantic HTML
  10. When You Build Accessible
  11. Semantic HTML as Bedrock
  12. Choose the Right Element
  13. Wrong Element + ARIA = Playing Catch-Up
  14. Component Design Starts With Accessibility
  15. State Management and Announcements
  16. Routing and Focus Management
  17. Required Steps
  18. Effect on Your Architecture
  19. Testing From the Start
  20. The Cost of Ignoring Accessibility
  21. The Upside of Building It Right
  22. Better Code
  23. No Retrofitting Project
  24. Easier Testing
  25. Reduced Legal Risk
  26. Expanded Market
  27. Good Engineering
  28. Building Accessibility Into Your Culture
  29. Developers
  30. Designers
  31. Product Managers
  32. The Organization
  33. The Path Forward
  34. Summary
TL;DR

TL;DR

Accessibility is an architectural decision that shapes component design, state management, routing, and testing from the first line of code, rather than a feature you add at the end.

Core principles:

Architecture, not decoration: accessibility shapes every layer of your application
Semantic HTML first: start with meaningful elements, not div soup
Test during development: keyboard and screen readers from day one
Better for everyone: accessible code tends to be easier to maintain and test

The power of the Web is in its universality. Access by everyone regardless of disability is an essential aspect.

— Tim Berners-Lee

The Afterthought Problem

I’ve watched many development teams treat accessibility the same way they treat security audits or performance optimization: something to worry about later. Ship the feature first, make it accessible second. Get to MVP, then circle back for WCAG compliance.

This approach fails because accessibility depends on decisions made at the very beginning, and adding it later means going back and changing those decisions.

The decisions you make on day one can decide whether making your app accessible later is possible at all.

Some of those decisions are very hard to reverse later, and others leave your options open.

When you build without considering accessibility, you build up technical debt, and it’s usually the structural kind that means rethinking entire systems rather than the kind you can refactor away in a sprint: components built without semantic meaning, state updates that never reach screen readers, navigation flows that only work with a mouse, forms that can’t be completed without sight.

The debt grows with every component you add that lacks accessible patterns, because each one becomes another thing to fix. The rest of the codebase grows around those components, and fixing them later means touching everything.

Architecture, Not Decoration

Accessibility is architectural because it affects every layer of your application. You can see this every time someone tries to retrofit accessibility into an application designed without it.

Accessibility in Every Layer

Component Hierarchy

Your component hierarchy determines whether focus management works correctly. A modal that renders outside the component tree breaks keyboard navigation unless you explicitly manage focus. A dropdown menu needs ARIA attributes coordinated with JavaScript state.

State Management

Your state management determines whether assistive technologies understand your application. When content updates dynamically, screen readers need to know. When an error occurs, keyboard users need to receive focus. When a form validates, the validation state needs to be announced.

Routing

Your routing determines whether navigation works for everyone. Client-side routing that never updates focus leaves keyboard users lost in the page. When a URL change isn’t announced, screen reader users don’t know the page changed.

Data Fetching

Your data fetching affects whether loading states are accessible. When you fetch data, someone using a screen reader needs to know loading started, when it finished, and whether it failed. Spinners that are purely visual exclude blind users.

Building an accessible application means deciding how it describes its own structure to assistive technology. You have to spell out what each part of the interface is, as well as how it looks.

That’s why retrofitting accessibility means going back over every one of those decisions.

The Progressive Enhancement Connection

Progressive enhancement and accessibility share the same foundation: semantic HTML.

When you build with progressive enhancement, you start with HTML that works everywhere: forms that submit, links that navigate, and buttons that trigger actions. This baseline is accessible from the start because browsers already know how to expose those elements to assistive technology.

Two Sides of the Same Coin

When You Abandon Semantic HTML

When you abandon HTML in favor of <div> soup with JavaScript event handlers, you lose both progressive enhancement and accessibility at the same time:

  • Click handlers don’t work without JavaScript
  • Keyboard navigation doesn’t exist
  • Screen reader users receive no semantic information
  • Only works in JavaScript-enabled, mouse-driven browsers

When You Build Accessible

When you build accessible applications, you get progressive enhancement benefits for free:

  • Semantic HTML works without JavaScript
  • Keyboard support means your site works with switch devices and with keyboards attached to tablets and phones
  • Focus management means your application works with various input methods
  • Live region announcements tell screen reader users about changes they can’t see

Progressive enhancement and accessibility are the same concern seen from two directions. Both treat HTML as your foundation rather than a rendering target for JavaScript.

See The Progressive Enhancement Mindset for more on how the two approaches support each other.

Semantic HTML as Bedrock

Every accessible application starts with semantic HTML: elements that have meaning of their own, rather than <div> elements with role attributes added.

That can look like a minor technical choice, but HTML was designed with meaning built into its elements.

Use the right element: <button> for actions, not <div onClick>. <a> for navigation, not <span> with routing. <main>, <nav>, <aside>, <article>, <section> to structure content. <form> to collect input. <label> to describe fields. <table> for tabular data, not layout.

Choosing the right element documents your intent in the markup itself, so the code describes what it does.

These elements also communicate structure and intent to assistive technologies. A screen reader announces “button” when encountering a <button>, but “clickable” or nothing at all when encountering a <div> with an onclick handler.

Semantic HTML vs ARIA

Choose the Right Element

When you choose the right element, you get accessibility for free:

  • Focus management works
  • Keyboard interaction works
  • Screen reader announcements work
  • ARIA attributes are rarely needed because the semantics are already present

Wrong Element + ARIA = Playing Catch-Up

When you choose the wrong element and try to fix it with ARIA:

  • You need to add role="button" and tabindex="0"
  • You need onKeyDown handlers for Space and Enter keys
  • You need to add aria-label because there’s no semantic content
  • You need to manage focus explicitly

The first rule of ARIA is to not use ARIA if semantic HTML can accomplish the same thing. Every ARIA attribute you add is a maintenance burden.

Component Design Starts With Accessibility

When you design components, accessibility shapes decisions about each component’s API and how components fit together.

Take a modal dialog. An accessible modal needs the coordinated pieces listed alongside, and they define what a modal is rather than being optional features you add later. Without focus management, a modal is just a div that appears on top of content.

Your component API needs to support these requirements. Where does focus return when the modal closes? What element should receive initial focus? Is the modal dismissible? These are questions about how the component behaves, not how it looks.

The same applies to every interactive component. A tabs component needs roving tabindex, arrow key navigation, Home and End key support, aria-selected, and proper label associations. A combobox needs aria-expanded, aria-autocomplete, aria-activedescendant, and specific keyboard interactions.

These patterns are very hard to add after the component is built, because they affect state management, keyboard event handling, DOM structure, and component lifecycle. They have to be part of the initial design, or you end up rebuilding the component from scratch.

Interactive widgets have well-defined behaviors, and the accessible behavior is part of each widget’s core behavior rather than something extra on top.

State Management and Announcements

Your state management architecture determines whether users know what’s happening in your application.

When state changes, visual users see the update: a message appears, a color changes, content loads. Screen reader users only find out about these changes if you communicate them explicitly.

This requires coordinating state changes with announcements. When a form submission succeeds, you need to announce success. When data loads, you need to announce completion. When validation fails, you need to announce errors and move focus to the first invalid field.

These announcements belong in your state management layer, rather than being added to each component one at a time.

When you dispatch an action that shows a notification, that action should trigger an announcement too. The same goes for moving into a loading state, and for updating data that affects content the user is focused on.

Frameworks like React, Vue, and Svelte give you lifecycle hooks and effect systems for coordinating DOM updates with side effects like announcements. But the framework can’t know what to announce or when. You decide that when you design your state layer.

The announcements themselves go through live regions: aria-live="polite" for non-urgent updates, aria-live="assertive" for critical information, aria-atomic to choose between announcing the entire region or just the changes, and aria-relevant to specify which types of changes get announced. These attributes only work if you send state changes through them consistently.

If this coordination is built into your architecture, every developer on your team gets announcements right without thinking about it. If it’s added later, every developer has to remember to add announcements by hand, and some of them will forget.

Routing and Focus Management

Client-side routing breaks accessibility by default.

When you navigate with traditional links, the browser handles everything: it requests the page, renders content, resets scroll position, and moves focus to the top of the page. Users know navigation occurred because the page reloaded.

When you intercept clicks and render new content with JavaScript, none of this happens automatically. The URL changes but focus stays on the link the user clicked. The page content updates but screen readers don’t announce it. Scroll position may or may not change, and users are left disoriented.

Accessible Routing Requirements

Required Steps

  • Move focus to the new content after navigation completes
  • Announce the navigation to screen readers
  • Update page titles so browser history makes sense
  • Handle loading states accessibly
  • Handle interrupted navigation when a new route loads before the previous one finishes

Effect on Your Architecture

These requirements shape your routing architecture. You need lifecycle hooks that run after navigation. You need a consistent target for focus management. You need a way to announce route changes.

If you build client-side routing without considering accessibility, you create an application that only works for sighted mouse users. Retrofitting focus management means revisiting every route, every route guard, every navigation trigger.

Building it in from day one avoids that rework.

Testing From the Start

Accessibility testing belongs in development, starting from the first component, rather than in a pass just before launch.

Accessibility belongs in your definition of 'working', rather than on a compliance checklist you run through before shipping.

When you write a component, the first check is keyboard navigation: can you reach every interactive element, trigger every action, and see the focus indicators? If not, the component is broken, not “working but needs accessibility.”

When you add dynamic content, a screen reader tells you whether the content is announced when it loads, whether errors are announced, and whether state changes come through. If they don’t, the feature is incomplete.

Forms need testing with several input methods: keyboard, screen reader, voice control, switch control. If you can’t complete the form, fix errors, and submit with each of them, the form doesn’t work.

Testing from the start catches problems while they’re cheap to fix. One component with incorrect ARIA attributes takes five minutes to correct, and fifty components with incorrect patterns take weeks.

Automated testing helps but can’t replace manual testing. Tools like axe-core, pa11y, and Lighthouse catch obvious issues: missing alt text, insufficient contrast, invalid ARIA. But they can’t catch incorrect keyboard behavior, confusing focus management, or unhelpful announcements. Those require human testing.

Manual accessibility testing works best as part of your normal development workflow: using a screen reader, using only the keyboard, trying voice control, and checking the page under simulated visual impairments with browser tools. It can be as routine as testing in different browsers.

The Cost of Ignoring Accessibility

Ignoring accessibility has legal and business costs, and the legal ones are rising. UsableNet counted 2,314 digital accessibility lawsuits filed in the U.S. in 2018, and more than 5,000 in 2025 across federal and state courts. Even a suit that settles quickly comes with legal fees on top of the cost of fixing the site.

Settlements can be large: Target paid $6 million in damages to settle a class action over its website in 2008. Domino’s Pizza, Netflix, Winn-Dixie, Beyoncé’s website, and hundreds of others faced lawsuits under the Americans with Disabilities Act. Several courts have ruled that websites can count as places of public accommodation subject to accessibility requirements, though not every court has agreed.

An inaccessible site also turns away customers who want to buy from you, which makes accessibility a business problem as well as an ethical one. You’re leaving money on the table.

Retrofitting a large application can tie up several engineers for months. That's time spent fixing existing features instead of building new ones.

The retrofitting checklist:

  • Auditing every component and page
  • Redesigning component APIs to support accessibility
  • Rewriting components that can’t be made accessible
  • Updating state management to handle announcements
  • Fixing routing and focus management globally
  • Testing everything again across devices and assistive technologies
  • Training your entire team on accessible patterns

There’s also an ethical cost. Banking, shopping, education, government services, and entertainment are increasingly online, so inaccessible software creates real barriers for disabled people in their daily lives, whatever the legal or business picture.

The Upside of Building It Right

Building accessibility in from the start also has benefits of its own.

The Benefits

Better Code

Accessible code is often better code. Semantic HTML is easier to maintain than <div> soup with role attributes. Keyboard navigation helps power users as well as the people who rely on it. Focus management makes your UI more predictable.

No Retrofitting Project

Every feature ships accessible from day one. There’s no separate cleanup phase before launch, and audits are less likely to find problems built into the architecture. You build it right the first time.

Easier Testing

When components follow consistent accessible patterns, you know what to test. When state management handles announcements architecturally, you don’t need to check the announcement logic in each component separately.

You’re not immune to lawsuits, but having demonstrable accessibility practices reduces your exposure and puts you in a much better position if a complaint does arrive.

Expanded Market

Accessible sites reach more users, including elderly users with declining vision or motor control, users with temporary disabilities like broken arms, and users in situational limitations like bright sunlight or noisy environments.

Good Engineering

Building sites that everyone can use, regardless of ability, is good ethics and good business, and it’s also good engineering.

Building Accessibility Into Your Culture

Making accessibility foundational takes cultural change as well as technical change.

Developers

Developers need ongoing training rather than a single workshop they forget, backed up by how the team works:

  • Accessible patterns should be in your component library
  • Code reviews should check accessibility
  • Pull request templates should include accessibility testing steps
  • Accessibility should be a normal part of definition of done

Designers

Designers need to design for accessibility as part of the primary design, rather than as a separate accessible version:

  • Color contrast should be verified in design tools
  • Focus states should be designed
  • Keyboard interaction should be specified
  • Screen reader announcements should be documented

Product Managers

Product managers need to prioritize accessibility as core functionality, rather than as technical debt or nice-to-have polish:

  • Accessibility work should be estimated in stories
  • Accessibility bugs should be treated like any other bug, because they stop the feature from working correctly
  • Shipping inaccessible features should be as unacceptable as shipping broken features

The Organization

This is organizational change, and it takes time. As long as accessibility is one person’s job, it tends to stay an afterthought, and it only becomes part of how you build software once everyone shares it.

The Path Forward

Accessibility has to be part of the architecture from day one. It shapes component design, state management, routing, and testing, and adding it at the end takes significant rework.

In practice, that means starting with semantic HTML and the right elements for the job, layering enhancements on top of that structure, testing with keyboards and screen readers as you develop, building announcements into state management and focus management into routing, and making accessibility part of what “working” means.

This approach takes less work than retrofitting, lowers your legal risk, reaches more people, and produces better software. Most of all, it keeps the web usable by everyone.

Summary

  1. Accessibility is architectural, not cosmetic. It shapes component design, state management, and routing from day one
  2. Start with semantic HTML and progressive enhancement to get accessibility built-in
  3. Test with keyboard and screen readers during development, not before launch
  4. Build announcements into state management and focus management into routing
  5. Ignoring accessibility creates legal, financial, and ethical costs that compound over time
  6. Making accessibility foundational creates better code, avoids a retrofitting project later, and reaches more users

Accessibility belongs in how you define “working” from the start, rather than being a feature you add later.

Related Content:

External Resources:

Newsletter

A Monthly Email
from Den Odell

Behind-the-scenes thinking on frontend patterns, site updates, and more

No spam. Unsubscribe anytime.