I am the kind of driver some people despise. I have the audacity to not confuse confidence with just plain stupidity. Every day I witness folks on the road doing things they shouldn’t just because they can with seemingly no consideration for others. They seem to be under the impression that their ability to accomplish something means they must, even if it make other folks on the road uncomfortable, inconvenienced, or even puts them in danger. And I see similar behaviors in web developers.
Just because you can doesn’t mean you should
I will never understand drivers who have something to prove in the most meaningless ways. Say a highway has a 55mph (miles per hour)/88kph (kilometers per hour) speed limit and there is a blind curve with a 45mph/72kph speed limit along it. Now, chances are, folks are already going over the highway’s speed limit, minimum 65mph/105kph. And at least where I live, those folks are going to take that blind curve still going that 65mph. Why is that? Is it to prove to the world they can? “Look how confident I am that my vehicle can take a 45mph curve going 65mph.” Are they trying to prove what rebels they are by not obeying the sign? Are those precious 5 seconds they saved by not slowing down really worth that much? They are only thinking of themselves, their time, and what they think they’re capable of and not why that curve has a 45mph speed limit.
Now I use this example because I witness it constantly where I live. And I know exactly why the curve in question has such a limit: it’s a T-intersection that is obscured by the curve. That speed limit isn’t there because it’s too sharp for vehicles to make going 65mph, daring only the most brave among us to try. It’s there so vehicles have enough time to stop if another vehicle is using the turn. The accidents that occur here are numerous because people are constantly refusing to slow down for that curve. That speed limit isn’t there to ruin anyone’s fun or challenge anyone’s pride – it’s there to keep people safe.
HTML rules of the road
When learning to operate a vehicle, the first thing you do is figure out what everything does and learn how and when to use it. Blinkers, brakes, clutch, gear shift, rearview mirror, side mirrors, windshield wipers, adjusting the seat, and so on and so forth. For the most part, everything that goes into safely operating a vehicle has a distinct purpose and there is a time and place to use it.
HTML is no different. Every element has a distinct purpose: something it was created for. Forms have specific elements that tell the browser it’s a form field, links have tags that tell the browser it will take a user somewhere as a button has a tag to tell the browser to activate something, and attributes exist to provide additional information. It’s a beautiful system. So why do some developers insist on using the wrong elements and forcing them into a role they weren’t meant for when an element to accomplish the task already exists? Are they just like our drivers trying to prove to the world they can take a curve faster than the posted speed?
Let’s look at one of the most common infractions I see: <div>
vs. radio buttons. This is particularly common on eCommerce sites using product variations. If the purpose is to select a single variation, say a color, for a product then a radio button would suit this perfectly as radio buttons natively only allow a single selection. However, what I see is a lot of developers using <div>
tags and forcing them to act like a radio button with JavaScript. This can go largely unnoticed by mouse-users but since <div>
tags are not user-interface objects, they’re inaccessible by keyboard users. This is bad practice. If your car has an open cup holder, why would you set your drink on the dashboard? A feature in your vehicle literally exists for the purpose of holding a cup – why put the cup somewhere that could result in a huge mess with the slightest tap of the brakes? And boy do fake user-interface objects make a mess.
(By the way, for those of you who may be reading this who aren’t developers, a <div>
, short for “division”, is a basic HTML container element used for grouping other elements together.)
I’m taken aback every time I see this; but, unfortunately, I see it a lot. Developers who would rather force and element to do something it shouldn’t instead of just using the element that was made to do that job. I’d call them lazy, but it actually takes more effort to force a <div>
to act like a radio button than actually just using a radio button.
Let’s look at two sets of options:
Looking at them, they’re the same. And a mouse-user would probably never know the difference. But a keyboard-user would because Sample #2 is made up of <div>
elements using JavaScript (JS) to make them act like radio buttons. A screen reader would hear “clickable [option]” which doesn’t have an audio cue that it’s a single-selection item. It sounds more like a <button>
meant to activate something, not a single-selectable from an array of options. Meanwhile, Sample #1 is made up of native radio buttons and a little CSS. A dev can implement more JavaScript to make the fake buttons more keyboard-friendly, but now we’re just bloating a site with more unnecessary scripts despite the fact elements for this purpose already exist. And a screen reader user still wouldn’t hear the expected audio cues for a single-selection field. Additionally, without even more extra JS, there will be no screen reader audio cue for when an option has been selected (unlike a native radio button that will provide these alerts by default).
We also have another issue: Not everyone browses with JavaScript or CSS enabled. Browsing with JavaScript disabled increases performance and improves load speeds, removes animations and many ads, and has the benefit of increased privacy by reducing tracking cookie collection. Whether a person has a disability or is able-bodied, browsing with JavaScript disabled is not an uncommon practice. So let’s remove JavaScript from our samples:
The options in Sample #2 are no longer selectable because the JavaScript it once depended on no longer exists (even if they appear interactable due to CSS; for example, the CSS’s :focus
indication would still make it appear selected on a touch device, like a tablet or phone, but it means nothing on a programmatic level). This makes this set of options completely inaccessible for a screen reader user because <div>
elements ARE NOT native user-interface objects.
It’s also not uncommon for people to browse with CSS disabled. This also increases load speeds and removes common inaccessible design hurdles like poor color contrast or difficult to read fonts. So let’s also remove the CSS and see what we have left:
Of the two options, only one remains functional because it was built with HTML elements that were made to do this job instead of adding extra unnecessary code to force an unqualified element to masquerade like another.
Unfortunately, you can’t throw a rock in the WordPress plugin repo for WooCommerce swatch variations without hitting at least a dozen plugins using this bad practice code. And folks using Shopify should be terrified of most of the apps they’re using. But eCommerce websites aren’t the only ones who should be worrying about this practice. So many plugins for dynamic features are doing this. There are even basic Table plugins out there outputting <div>
elements instead of native <table>
elements, making them nearly impossible to effectively navigate for screen reader users. “Tab” plugins are also HUGE violators of this practice.
Here is a “Tab” sample using Kadence Block’s Tab block
This is fake content #1
This is fake content #2
This is fake content #3
This block DOES use JavaScript. But because of how it’s built, it has a fallback. Here’s a screenshot of what happens to this block when JavaScript is disabled:

And without CSS:

I’m a huge fan of Kadence for WordPress. Clearly, they’re not perfect (who among us is?), but their staff is taking accessibility concerns seriously and I am seeing improvements and optimizations for accessibility every time they update this plugin. So don’t lose faith in them just because of this.
Let’s look at the pros and cons of what they’ve done. They’re using list elements to manage the tab names and then <div>
elements to manage the content. So PRO: When we disabled JavaScript, we didn’t lose the content altogether. Most tabs like this will physically still look like tabs but the other tabs won’t be activatable so a user with JS disabled wouldn’t have access to the other content at all. So that’s a step above what I see a lot of other Tab Block developers doing in the WordPress community. But, unfortunately, CON, we lose the content association with the Tab’s title which could provide vital context. Especially without an aria-describedby
attribute, a screen reader user could find this to be a poor user experience.
Don’t get me wrong… This isn’t a <div>
witch hunt and I’m not anti-JavaScript. It can be helpful, is necessary for some functionalities to work, and can create some nice aesthetic features. But just like operating a vehicle, developers must use it responsibly out of considerations for others on the road of life. Right now, there are just a whole lot of metaphorical drunk-driving developers out there swerving out of their lane and creating hazards for others.
Keeping road conditions in mind
A lot of the drivers out there wreaking havoc seem to assume only the best road conditions are ahead of them. Only the best-case scenario is possible so there’s no chance there’s a hazard around a curve, an animal will jump out in front of them, or some other dumbass in on-coming traffic is texting and driving and crossing over the line. These things don’t go through the minds of these drivers who make reckless decisions while driving. And the same thing goes with bad practice developers.
A brand new 20-inch monitor. A nice keyboard with a keypad on it. An ergonomic mouse. The best of fiber Internet. Only the best for a perfectly able-bodied person. These are the conditions those developers build for. The alternative conditions someone may be looking at their website in don’t cross their minds. They don’t think of the road conditions before the development journey. Just like crappy drivers, they don’t think “What if.” What if some of our users can’t use a mouse? What if not all of our users have the best Internet? What if some of our users are blind or low vision? No, they create their product assuming only the best conditions are ahead of them.
The fact is, life is full of unpredictable conditions, even for an able-bodied person. The parent with a squirming infant in their arms who struggles to focus on a website with too many moving pieces, the considerate person in a waiting room who forgot earbuds and is disappointed their video doesn’t have captions because they don’t want to disrupt people by playing the audio, or the fact that no matter how advanced auto-adjust brightness features on phones get some color combinations are impossible to see in certain lighting even for a person with perfect vision.
Folks get frustrated, impatient, and have bad days at work. These are all conditions developers need to consider when creating websites. “Is the feature I’m building going to make someone who might already be annoyed even more annoyed, or will they be relieved by a user-friendly experience?” They can’t assume every person who visits their website is patient and understanding because their super neat, poorly coded feature causes a site to take longer to load. This is an unrealistic expectation and puts the onus on the user to deal with whatever issues a developer using bad coding practices has created (just like reckless driving puts the onus on other drivers to be responsible and alert).
Start practicing defensive development
When I was learning to drive, my folks told me I needed to “drive with the assumption everyone else on the road is a dumbass.” And that’s exactly how I drive; it goes through my mind every time I get behind the wheel. But no one, with or without a disability, should have to go into every website experience under the assumption the developer was a dumbass.
If we were the only people on the road, any of us could take that 45mph curve going 65mph. But the fact is, we’re not. There are other people and conditions on the road to consider while we’re driving, just like there are other people and conditions to consider while developing. Developers can’t keep ignoring the signs and misusing their vehicle just because they feel like they can. Those signs, those HTML elements, exist for a reason.
Lest we forget to mention the other penalty for some of these infractions: tickets. I hear they’re a pain in the ass. However, a $200 speeding ticket is nothing compared to a potential $20,000 or more accessibility lawsuit that can result from bad practice development. Non-developers just trying to run a small eCommerce business trust that developers know what they’re doing when they use a product – and they should be able to trust us! But most of them are being unwittingly screwed over and hit with lawsuits because they trusted a plugin or add-on or theme they shouldn’t have. And that’s on the developer. The developer who crapped out shit code and got lucky enough to get positive reviews by able-bodied, non-techy folks who can’t tell the difference to encourage continuous downloads, giving this dev the false impression they did a good job when any developer worth their salt should know what basic HTML elements are meant to be used for.
Someone reading this might feel personally attacked right now. Either because they’re the kind of driver that consistently drives recklessly or because they’re the kind of developer who likes misusing HTML elements. And from the bottom of my heart, let me say: Good. I am personally attacking you because you’re only thinking of yourselves and the perfect conditions you imagine are ahead. Get your head out of your ass and start thinking of other people before someone gets hurt.