Semantic Markup Is Less Work

Why are we still building user interfaces that are inaccessible? Why do we accept creating significant technical debt to build experiences that simply don’t work for all users?

I’ve been a designer / developer for many years and I have yet to meet anyone in this industry that doesn’t care about how their design or implementation impacts users. The issue isn’t that we don’t care, it’s that many believe it’s more difficult or time intensive to design and develop an inclusive interface. It is also believed that accessibility can be addressed in a future development phase. Of course it can, but the effort to do so is underestimated because of the amount of technical debt that can be accrued. Planning for and building inclusive interfaces from the beginning is quicker, simpler, and results in higher quality code that is accessible to more users.

Planning for and building inclusive interfaces from the beginning is quicker, simpler, and results in better quality code that is accessible to more users.

There are many articles around this topic, so instead of discussing how a list of items should be marked up in an ordered <ol> or unordered <ul> list rather than being placed in a series of <div> tags, I want to provide an example of code that I’ve seen in the wild. This is code that renders an interface inaccessible by anyone that is not a mouse user, simply because semantic mark up wasn’t used.


The non-semantic markup was the result of a custom styled checkbox generated by a prototyping tool or WYSIWYG editor. The checkbox state is created with javascript and relies on background images to visually indicate that state to the user.

<!-- Accessible / Semantic -->
<label class="input-checkbox">
  <input type="checkbox" name="checkbox_1" value="some value" />
  <span class="input-checkbox__check"></span>
  <span>I'm a custom styled checkbox</span>

<!-- Inaccessible / Non-semantic -->
<div class="input-checkbox">
  <span>I'm an imposter</span>
  <input type="hidden" name="checkbox_1" value="set on click" />

See the Pen Custom Checkbox by Derick Montague (@BeyondHyper) on CodePen.

The non-semantic block uses a span as a label and a hidden input for storing the checkbox value. This component can only be accessed by mouse users. The only way to change the state of the element is by clicking on it. Another critical point is that the state of the checkbox is 100% dependent on javascript to function. If the user doesn’t have javascript enabled or if the function that establishes the checkbox behavior throws an error, no users will be able to interact with the element.

In this example, one potential reason for the non-semantic markup was that it came from generated code and it was a simple copy and paste implementation. In another, an engineer that wasn’t experienced with CSS layouts made a guess on how to build a custom checkbox. In both cases, we have isolated any user that can’t or prefers not to use a mouse and once this code has been implemented throughout the application, updating the solution to use the semantic approach is not a trivial process due to having to remove the javascript dependency and complete regression testing.

Although we could use ARIA roles and tabindex attributes to make the non-semantic code accessible, the amount of work and effort (development and cross-browser/device testing) negates the efficiency gained by using generated code. The developer would need to develop the functionality that a native checkbox provides out of the box, such as managing the state of the checkbox, the ability to focus on and tab to the element, and emitting events, such as the change event. It’s always better to use semantic elements rather than relying on ARIA when possible.

Looking at the semantic example on CodePen, it easy to see how simple the implementation could have been. A native input, label, span, and pseudo-element and we have created an inclusive component that doesn’t have any dependency on javascript to function.