How to properly mark up a checkbox and customizing its design taking in consideration that the result is accessible and easy to use.
As a user
- As a user, I should be able to navigate by keyboard.
- As a user, I expect that there will be no animation in case I disabled animations from system preferences.
- As a user, I should see a clear focus style.
- As a user, In multiple checkboxes, I should hear the question or title related to them.
- checkbox input
As per MDN, a checkbox:
<input>of type checkbox are rendered by default as square boxes that are checked (ticked) when activated. They allow you to select or deselect single values in a form.
Default Accessible Checkbox
Let’s mark up a checkbox in HTML.
Here is how the above code works:
The checkbox can be activated only by clicking on the box only. If the user clicked on the text, it won’t be activated. This is not good.
Associating the label with the input
To achieve that, an ID should be added to the checkbox
<input>. Then, this ID will be used as a
for attribute for the
Now, when the user clicks on the input’s label text, it will be activated. This is much better!
Preventing Text Selection
Sometimes, when the checkbox is activated/deactivated, the input’s label text got selected like below:
This can be solved by using
user-select in CSS.
To recap, I laid out the foundation to an accessible checkbox. Here is the final HTML:
It’s also possible to wrap the input in the label. In that case,
for attributes won’t be needed. The association will work out of the box.
Or a group of checkboxes. This can be useful for a question that might have multiple answers or selections from the user.
Let’s see how to mark up a question asking about your favorite type of meals. From the first glance, you might think about adding a heading and then to group the checkboxes in a wrapper or something.
The above might look good for a sighted user. However, for a visually impaired user, the above isn’t accessible. See the below screenshot for how macOS VoiceOver reads that.
The question “What’s your favorite meal?” hasn’t been read to the user. As a result, when going through the checkboxes, the user won’t know what the context they are in.
Fieldset for the win
By default, there is an HTML element that is suitable for that use case. It can be marked up as below:
Simple, isn’t it? I just swapped the group
<div> with a
<fieldset>, and replaced the heading with a
Now, let’s see how VoiceOver will read that!
Much better. Now the user can know the context. This is a group and there is a question related to it with multiple answers.
Custom Checkbox Design
Using CSS and Background Images
Most of the time, a custom design for the checkbox is required. Let’s explore how to achieve that.
Since the default checkbox design is not customizable, I will hide it visually and add a pseudo element. By visually, I mean to hide it from the UI and keep it in the DOM for screen reader and keyboard users.
Now, the input checkbox is hidden and it looks like the below figure.
I added some basic CSS to style a fake checkbox with
before pseudo element.
Next, I will add the
checked style for the checkbox. Since the input is there, it will be used to select the label
before pseudo element.
Using Inline SVG
It’s also possible to use an inline SVG element for the checkmark. That way, you can have a bigger control which can open a lot of possibilities.
In the below example, I placed an inline SVG and animated the checkmark path in CSS with
Things to Consider When Customizing a Checkbox
Using Pointer Events
In the custom checkbox I built, the checkbox won’t be active when clicking over the SVG tick element. To avoid that, it’s better to add disable pointer events on it.
In the below GIF, I’m trying to click on the square but it’s not working. It’s like being checked for a few milliseconds and then it’s returned to its initial state. That’s because I’m clicking on the SVG element.
It’s important to care about keyboard users. As a result, the focus style should be added to provide a better experience for those users.
The focus will now work with the keyboard. However, it will still there while using a mouse. The best solution I found to this issue is using CSS
:focus-visible which is supported only in Firefox at the time of writing this article.
If you’ve noticed, the outline on the first custom checkbox is not a 100% rectangle. See the below figure:
That’s due to that the label itself is an inline element. When adding
display: inline-block or
display: block to this, the issue is solved and the outline is clear.
Some users don’t feel comfortable or have issues dealing with animated elements. There is an option in every operating system that let the users disable animations for a better experience.
Here is an example of the accessibility settings in macOS.
For our case, it’s recommended to account for that in CSS and add the following:
Share the article:
Did you like the article? Share it on Twitter
Did you like the article? You can hire me.
I'm a UX, UI Designer and Front End Developer. I focus on building accessible and easy to use websites and apps. Get in touch by email: firstname.lastname@example.org