This content originally appeared on Scott O’Hara UX developer, designer & accessibility engineer and was authored by Scott O'Hara
About three years ago now (2017/2018), I published a collection of accessible styled form controls which included specific markup patterns to create custom styled radio buttons and checkboxes. These patterns were the culmination of years of my own tinkering, studying other people’s implementations, and then stress testing them with the assistive technologies I had at my disposal.
At the time, the most robust way to style these form contrls, without re-creating them from scratch with ARIA, was to visually hide the radio button or checkbox, and then recreate the controls using a <label>
or <span>
and their pseudo-elements (::before
and ::after
). The need for this approach was largely, but not entirely, due to Internet Explorer and Legacy Edge not providing the best support to directly style native HTML <input>
elements themselves. And if you go even further back in time, all browsers had barries in directly styling these controls.
That’s not to say, in 2018, that directly styling native radio buttons and checkboxes couldn’t be done (see restyled radio buttons) and restyled checkboxes). But there were workarounds needed, and styling limitations that still existed due to inconsistencies with Firefox, Internet Explorer, and pre-Chromium Edge, at the time.
Now (2021), with Internet Explorer support being dropped left and right, and Edge now being Chromium-based, and Firefox quirks having been ironed out, these limitations have largely lifted.
I hope this is the last time I write about this subject.
What’s necessary to directly restyle radios and checkboxes?
As we are going to style the elements directly, the expected markup patterns would be the same as if you were not applying custom styling. For instance:
<label for="r1">
<input type="radio" id="r1" name="r">
Restyle Option
</label>
<!-- or -->
<input type="radio" id="r2" name="r">
<label for="r2">Resyle option</label>
<label for="c1">
<input type="checkbox" id="c1">
Choice 2
</label>
<!-- or -->
<input type="checkbox" id="c2">
<label for="c2">
Choice 2
</label>
The necessary CSS
There are a varieity of CSS properties that radio buttons and checkboxes will respect by default, but to start absolutely fresh (which, if you’re restyling these controls that’s probably what you want anyway), the first thing we need to do is clear them of all default styling. For this, we use the appearance: none
property.
appearance
property. But, according to caniuse.com, Webkit still requires a -webkit-
prefix. Generally best to be safe than sorry, so while annoying to still need both the prefixed -webkit-appearance
and apperance
, might as well keep it up for now.
Once apperance: none
is specified for the radio buttons and checkboxes you’re going to revise, you’ll have full styling control over the element itself, and its ::before
an ::after
pseudo elements. But, now that you’ve taken on this task, you have the following you need to account for in your CSS:
- Default (unchecked state).
- New checked state.
- Custom focus state.
- Checked state focus style (if the previous focus style is not noticable against the new checked style - e.g., dark outline against a new dark fill when the control is checked).
- Disabled state (make sure the label text continues to meet necessary contrast minimums!).
- Make sure left-to-right and right-to-left (
dir=ltr
,dir=rtl
) was not impacted by any of your styling. - Verify your styling works with Media Queries such as
forced-colors: active
(previously-ms-high-contrast: active
)prefers-color-scheme: dark
prefers-reduced-motion
(if you make some zany animations or something)print
- really any other media query where your custom styling might fall down flat. We should all be testing this stuff anyway, right?
You can play around with the following radio button and checkbox CodePen. Try adding dir=rtl
to the containing <div>
elements, or set one of the controls to disabled
.
See the Pen Custom Styled Native Radio Button by Scott (@scottohara) on CodePen.
Beyond the necessary CSS and markup
Maybe you’re thinking you need more than what’s been called out here. Three selectors to style a radio button or checkbox? “Psh”, you exclaim. “I have some serious user delight that needs injecting into these mundane controls for performing basic tasks on the Internet.” Cool! Feel free to add in other generic/presentational elements as you see fit.
For instance:
<div class="custom-check-container">
<input type="checkbox" id="c">
<span class="blip-bloop" aria-hidden="true"></span>
<span class="bleep-blop" aria-hidden="true"></span>
<label for="c">
Choice
</label>
</div>
<!--
Regarding the aria-hidden=true on the span elements.
You may well not "need" these to be explicitly set to be
hidden from the browser's accessibility tree... but as
I have no idea what one might do with these decorative
elements, seems best to put up some guard rails.
-->
The markup above has wrapped the checkbox within a containng <div>
element, and two extra <span>
elements with silly class names have been added for additional styling flourishes. E.g., want to make an animation effect when hovering, focusing or activating your checkbox? Now you can use CSS sibling selectors (e.g, ~
or +
) and/or some JavaScript to get your Material Design on (just remember to be careful with your z-indexing so these extra <span>
elements don’t accidently block a click or touch event from reaching your form control).
Just, whatever you do, you don’t need to do something silly like this:
<div role=checkbox aria-checked=false tabindex=0>
<input type=checkbox tabindex=-1 aria-hidden=true>
<div>
<!-- oodles of divs -->
Name of control
</div>
</div>
Nesting interactive elements like the above is absolutely unnecessary, and depending on the assistive technology used, the “hidden” checkbox may still be discoverable (for instance, navigating line-by-line with VoiceOver on macOS, or using Dragon Naturally Speaking - as recent testing has uncovered).
If you find yourself stuck in a situation where you are dealing with the above invalid nesting of a checkbox within a ‘checkbox’, you likely use CSS visibility: hidden
to competely hide the nested native checkbox from assistive technologies, while still maintaining your present functionality. A refactor would be ideal here, as the CSS we ship is a strong suggestion, but not a requirement for users. But hey, sometimes you just gotta hide your mistake and hope that no one peaks under your rug and notices all piles of dirt and dust. Right?
Wrap up and acknowledgements
At this point, I’m seeing very few reasons to continue to recommend the visually-hidden technique to style radio buttons and checkboxes. With all modern browsers being able to handle the styling of these controls, the visually hidden technique has far too many extra gotchas, though managable, to be aware of and account for.
Additionally, I’m also quite skeptical of the “need” to create custom ARIA radio buttons and checkboxes. The level of effort to make these custom controls, and ensure all necessary functionality is present, goes far beyond just needing to update one’s CSS. Even if you needed to create an ARIA role=switch
, CSS styling and relying on an <input type=checkbox>
’s implicit checked
functionality would give you all that you need to style a native checkbox into a custom switch (the third example on this linked page).
For additional information on styling native radio buttons and checkboxes, particularlly using the visually-hidden technique, I recommend reviewing the following:
- Under-Engineered Custom Radio Buttons and Checkboxen - Adrian Roselli (2017)
- Inclusively Hiding & Styling Checkboxes and Radio Buttons - Sara Soueidan (2020)
- a11y Styled Form Controls: Checkboxes (2018)
- a11y Styled Form Controls: Radio Buttons (2018)
- a11y Styled Form Controls: Star Rating Radio Buttons (2018)
- WTF, Forms (2016)
This content originally appeared on Scott O’Hara UX developer, designer & accessibility engineer and was authored by Scott O'Hara
Scott O'Hara | Sciencx (2021-09-24T00:00:00+00:00) One last time: custom styling radio buttons and checkboxes. Retrieved from https://www.scien.cx/2021/09/24/one-last-time-custom-styling-radio-buttons-and-checkboxes/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.