All web developers know, at some level, that accessibility is important. But when push comes to shove, it can be hard to prioritize it above a bazillion other concerns when you’re trying to center a <div> and you’re on a tight deadline.
A lot of accessibility advocates lead with the moral argument: for example, that disabled people should have just as much access to the internet as any other person, and that it’s a blight on our whole industry that we continually fail to make it happen.
I personally find these arguments persuasive. But experience has also taught me that “eat your vegetables” is one of the least effective arguments in the world. Scolding people might get them to agree with you in public, or even in principle, but it’s unlikely to change their behavior once no one’s watching.
So in this post, I would like to list some of my personal, completely selfish reasons for building accessible UIs. No finger-wagging here: just good old hardheaded self-interest!
Debuggability
When I’m trying to debug a web app, it’s hard to orient myself in the DevTools if the entire UI is “div soup”:
<div class="css-1x2y3z4">
<div class="css-c6d7e8f">
<div class="css-a5b6c7d">
<div class="css-e8f9g0h"></div>
<div class="css-i1j2k3l">Library</div>
<div class="css-i1j2k3l">Version</div>
<div class="css-i1j2k3l">Size</div>
</div>
</div>
<div class="css-c6d7e8f">
<div class="css-m4n5o6p">
<div class="css-q7r8s9t">UI</div>
<div class="css-u0v1w2x">React</div>
<div class="css-u0v1w2x">19.1.0</div>
<div class="css-u0v1w2x">167kB</div>
</div>
<div class="css-m4n5o6p">
<div class="css-q7r8s9t">Style</div>
<div class="css-u0v1w2x">Tailwind</div>
<div class="css-u0v1w2x">4.0.0</div>
<div class="css-u0v1w2x">358kB</div>
</div>
<div class="css-m4n5o6p">
<div class="css-q7r8s9t">Build</div>
<div class="css-u0v1w2x">Vite</div>
<div class="css-u0v1w2x">6.3.5</div>
<div class="css-u0v1w2x">2.65MB</div>
</div>
</div>
</div>
This is actually a table, but you wouldn’t know it from looking at the HTML:

If I’m trying to debug this in the DevTools, I’m completely lost. Where are the rows? Where are the columns?
<table class="css-1x2y3z4">
<thead class="css-a5b6c7d">
<tr class="css-y3z4a5b">
<th scope="col" class="css-e8f9g0h"></th>
<th scope="col" class="css-i1j2k3l">Library</th>
<th scope="col" class="css-i1j2k3l">Version</th>
<th scope="col" class="css-i1j2k3l">Size</th>
</tr>
</thead>
<tbody class="css-a5b6c7d">
<tr class="css-y3z4a5b">
<th scope="row" class="css-q7r8s9t">UI</th>
<td class="css-u0v1w2x">React</td>
<td class="css-u0v1w2x">19.1.0</td>
<td class="css-u0v1w2x">167kB</td>
</tr>
<tr class="css-y3z4a5b">
<th scope="row" class="css-q7r8s9t">Style</th>
<td class="css-u0v1w2x">Tailwind</td>
<td class="css-u0v1w2x">4.0.0</td>
<td class="css-u0v1w2x">358kB</td>
</tr>
<tr class="css-y3z4a5b">
<th scope="row" class="css-q7r8s9t">Build</th>
<td class="css-u0v1w2x">Vite</td>
<td class="css-u0v1w2x">6.3.5</td>
<td class="css-u0v1w2x">2.65MB</td>
</tr>
</tbody>
</table>
Ah, that’s much better! Now I can easily zero in on a table cell, or a column header, because they’re all named. I’m not wading through a sea of <div>s anymore.
Even just adding ARIA roles to the <div>s would be an improvement here:
<div class="css-1x2y3z4" role="table">
<div class="css-a5b6c7d" role="rowgroup">
<div class="css-m4n5o6p" role="row">
<div class="css-e8f9g0h" role="columnheader"></div>
<div class="css-i1j2k3l" role="columnheader">Library</div>
<div class="css-i1j2k3l" role="columnheader">Version</div>
<div class="css-i1j2k3l" role="columnheader">Size</div>
</div>
</div>
<div class="css-c6d7e8f" role="rowgroup">
<div class="css-m4n5o6p" role="row">
<div class="css-q7r8s9t" role="rowheader">UI</div>
<div class="css-u0v1w2x" role="cell">React</div>
<div class="css-u0v1w2x" role="cell">19.1.0</div>
<div class="css-u0v1w2x" role="cell">167kB</div>
</div>
<div class="css-m4n5o6p" role="row">
<div class="css-q7r8s9t" role="rowheader">Style</div>
<div class="css-u0v1w2x" role="cell">Tailwind</div>
<div class="css-u0v1w2x" role="cell">4.0.0</div>
<div class="css-u0v1w2x" role="cell">358kB</div>
</div>
<div class="css-m4n5o6p" role="row">
<div class="css-q7r8s9t" role="rowheader">Build</div>
<div class="css-u0v1w2x" role="cell">Vite</div>
<div class="css-u0v1w2x" role="cell">6.3.5</div>
<div class="css-u0v1w2x" role="cell">2.65MB</div>
</div>
</div>
</div>
Especially if you’re using a CSS-in-JS framework (which I’ve simulated with robo-classes above), the HTML can get quite messy. Building accessibly makes it a lot easier to understand at a distance what each element is supposed to do.
Naming things
As all programmers know, naming things is hard. UIs are no exception: is this an “autocomplete”? Or a “dropdown”? Or a “picker”?

If you read the WAI ARIA guidelines, though, then it’s clear what it is: a “combobox”!
No need to grope for the right name: if you add the proper roles, then everything is already named for you:
comboboxlistboxoptions
As a bonus, you can use aria-* attributes or roles as a CSS selector. I often see awkward code like this:
<div
className={isActive ? 'active' : ''}
aria-selected={isActive}
role='option'
</div>
The active class is clearly redundant here. If you want to style based on the .active selector, you could just as easily style with [aria-selected="true"] instead.
Also, why call it isActive when the ARIA attribute is aria-selected? Just call it “selected” everywhere:
<div
aria-selected={isSelected}
role='option'
</div>
Much cleaner!
I also find that thinking in terms of roles and ARIA attributes sharpens my thinking, and gives structure to the interface I’m trying to create. Suddenly, I have a language for what I’m building, which can lead to more “obvious” variable names, CSS custom properties, grid area names, etc.
Testability
I’ve written about this before, but building accessibly also helps with writing tests. Rather than trying to select an element based on arbitrary classes or attributes, you can write more elegant code like this (e.g. with Playwright):
await page.getByLabel('Name').fill('Nolan')
await page.getByRole('button', { name: 'OK' }).click()
Imagine, though, if your entire UI is full of <div>s and robo-classes. How would you find the right inputs and buttons? You could select based on the robo-classes, or by searching for text inside or nearby the elements, but this makes your tests brittle.
As Kent C. Dodds has argued, writing UI tests based on semantics makes your tests more resilient to change. That’s because a UI’s semantic structure (i.e. the accessibility tree) tends to change less frequently than its classes, attributes, or even the composition of its HTML elements. (How many times have you added a wrapper <div> only to break your UI tests?)
Power users
When I’m on a desktop, I tend to be a keyboard power user. I like pressing Esc to close dialogs, Enter to submit a form, or even / in Firefox to quickly jump to links on the page. I do use a mouse, but I just prefer the keyboard since it’s faster.
So I find it jarring when a website breaks keyboard accessibility – Esc doesn’t dismiss a dialog, Enter doesn’t submit a form, ↑/↓ don’t change radio buttons. It disrupts my flow when I unexpectedly have to reach for my mouse. (Plus it’s a Van Halen brown M&M that signals to me that the website probably messed something else up, too!)
If you’re building a productivity tool with its own set of keyboard shortcuts (think Slack or GMail), then it’s even more important to get this right. You can’t add a lot of sophisticated keyboard controls if the basic Tab and focus logic doesn’t work correctly.
A lot of programmers are themselves power users, so I find this argument pretty persuasive. Build a UI that you yourself would like to use!
Conclusion
The reason that I, personally, care about accessibility is probably different from most people’s. I have a family member who is blind, and I’ve known many blind or low-vision people in my career. I’ve heard firsthand how frustrating it can be to use interfaces that aren’t built accessibly.
Honestly, if I were disabled, I would probably think to myself, “computer programmers must not care about me.” And judging from the miserable WebAIM results, I’d clearly be right:
Across the one million home pages, 50,960,288 distinct accessibility errors were detected—an average of 51 errors per page.
As a web developer who has dabbled in accessibility, though, I find this situation tragic. It’s not really that hard to build accessible interfaces. And I’m not talking about “ideal” or “optimized” – the bar is pretty low, so I’m just talking about something that works at all for people with a disability.
Maybe in the future, accessible interfaces won’t require so much manual intervention from developers. Maybe AI tooling (on either the production or consumption side) will make UIs that are usable out-of-the-box for people with disabilities. I’m actually sympathetic to the Jakob Nielsen argument that “accessibility has failed” – it’s hard to look at the WebAIM results and come to any other conclusion. Maybe the “eat your vegetables” era of accessibility has failed, and it’s time to try new tactics.
That’s why I wrote this post, though. You can build accessibly without having a bleeding heart. And for the time being, unless generative AI swoops in like a deus ex machina to save us, it’s our responsibility as interface designers to do so.
At the same time we’re helping others, though, we can also help ourselves. Like a good hot sauce on your Brussels sprouts, eating your vegetables doesn’t always have to be a chore.

Posted by James Morrin on June 16, 2025 at 11:48 AM
The testing use cases are huge! I remember long ago I worked at a company that used “data-test-*” in their markup. It always felt wrong to me, but I didnt have a good alternative to propose. Normally if I can’t propose a better alternative solution I dont think I have a right to complain so I never said anything. Well now I have a solution going forward! NICE!
Posted by Nolan Lawson on June 16, 2025 at 1:28 PM
Yep, to me
data-testidis a last resort. It is necessary sometimes if you’re testing something non-semantic (e.g. “ensure the animation on this bit of the UI looks right”), but most of the time it’s not needed.Posted by Jon on June 17, 2025 at 12:26 AM
The escape key exits fullscreen mode on my computer, so accessibility evangelists have to contend with that kind of thing too.
Posted by Dhruv Joshi on June 17, 2025 at 2:11 AM
I strongly agree with your point. In the early days, external CSS frameworks have influenced these <div> oriented structure, which can be avoided by properly following the HTML schematics.
Instead, use purpose built tags provided by the HTML first, then think about styling them by either using your preferred CSS framework or even going partially/fully class-less.
I have tried the fully class-less theme approach recently while migrating my old blog site and that experience pushed me to explore the accessibility attributes discussed here.
Posted by aaroselli on June 18, 2025 at 7:10 AM
I am also a fan of selfish accessibility (and not just because of my talks).
To avoid spamming your comment form with links, I’ll zero in on the notion of naming things. Pushing clients to identify what kind of thing their menus, drop-downs, pop-ups, and even grids are has saved a ton of time on design and development. Just by naming it well. Which, as you note, makes it easier for CSS to enforce the features.
So yes, please remind devs that accessibility helps them in their work. I’m here for it.
Posted by Weekly Reading List 23 June 2025 – OZeWAI on July 25, 2025 at 8:57 PM
[…] https://nolanlawson.com/2025/06/16/selfish-reasons-for-building-accessible-uis/ […]