Create Accessible Web Forms with Custom Styling
HTML is accessible by default. At least, if you use the right native elements. Very often, developers build custom form controls to meet design needs – neglecting accessibility in the process.
As I'll show in this post, modern browsers enable us to apply custom styling to most native form controls. This way, we can create accessible web forms with a consistent look across all platforms.
Photo: © Steve Johnson / pexels.com
When Design Needs wreck Accessibility
Native form controls like input
or select
are accessible
by default. They receive focus when keyboard users press the tab key. The enter and space key can be used
to interact with them, e.g., to mark a checkbox as checked. They communicate their role and current state
to screen readers.
Sounds great! You would be mad not to use native form controls and, instead, build custom UI components with non-semantic spaghetti code. Right? Well, the sad truth is: Using custom form controls is a common practice. And most of the time they're not accessible. Let's look at an example for a custom select:
<div class="custom-select">
<div class="select-trigger">
<span>PlayStation 5</span>
<div class="arrow"></div>
</div>
<div class="custom-options">
<span class="option selected">
PlayStation 5
</span>
<span class="option">Nintendo Switch</span>
<span class="option">Xbox Series X</span>
</div>
</div>
By itself, this mess of div
and span
tags is not accessible
to keyboard and screen reader users. Yes, you can fix this with ARIA attributes and keydown event listeners.
But many developers are either not aware of this or simply don't care. What they care about is styling
the custom elements to meet the design needs of their clients.
Why Styling Native Form Controls can be challenging
Using CSS to style form controls has historically been difficult. Browsers apply their own default styling and form controls vary greatly in how easy they are to customize with CSS. Fortunately, this is getting easier as old browsers are retired (death to IE11) and modern browsers offer more features to use.
Mozilla has a great article on Styling web forms, which I highly recommend. They categorize native form controls into three groups: “The good”, “The bad”, and “The ugly”. I mostly agree with them, except for considering checkboxes and radio buttons difficult to style. This may have been true in the past, but modern CSS allows you to do whatever you want as I'll show you.
Styling Example 1: Radio Buttons
In order to style your radio buttons with CSS, you need to set the value of the appearance
property to none
. This removes the default appearance applied by the browser and operating
system. Now you can get creative and even apply smooth CSS transitions if you like:
input[type="radio"] {
-webkit-appearance: none;
appearance: none;
box-sizing: border-box;
position: relative;
width: 1.4em;
height: 1.4em;
border: 0.15rem solid darkred;
border-radius: 50%;
}
input[type="radio"]::before {
position: absolute;
top: 50%;
left: 50%;
content: "";
width: 0.7em;
height: 0.7em;
border-radius: 50%;
background-color: darkred;
/* scale(0) hides the dot in unchecked state */
transform: translate(-50%, -50%) scale(0);
transform-origin: center;
transition: all 0.3s ease-in;
}
input[type="radio"]:checked::before {
transform: translate(-50%, -50%) scale(1);
transition: all 0.3s cubic-bezier(0.25, 0.25, 0.56, 2);
}
In my example, I defined the unchecked state of the radio button to appear as a darkred circle.
With the help of the ::before
pseudo-element and the :checked
pseudo-class, a darkred dot appears inside the circle when the radio button is selected. Check out the
demo on Codepen.
Styling Example 2: Select
The select
element belongs to the category “The ugly”. The problem with the
form controls in this group
is that they have very different default looks across browsers. You can style them to some degree with CSS,
but some parts of their internals are literally impossible to style.
You can use the appearance
property to override the default look of
the select
element. But this will not affect the list of options that appears
when you click on the element. Styling the option
element with CSS is highly
limited and only supported by some browsers.
In my opinion, the styling options are sufficient to meet basic design needs (e.g. color and font). Take a look at my demo on Codepen where I used the following styling:
select {
-webkit-appearance: none;
appearance: none;
box-sizing: border-box;
background-color: white;
background-image:
url('/assets/custom_arrow_for_select.svg');
background-position: right;
background-repeat: no-repeat;
background-size: contain;
border: 0.25rem solid darkred;
padding: 0.25em;
/* Leave enough space for the arrow on the right */
padding-right: 2em;
}
Native HTML is always better
What I wanted to show with this post is: Using native HTML is always better than messing around with custom UI components. Because keyboard and screen reader accessibility is already built in. Therefore, I would always pick the native form control if it can be sufficiently styled with CSS.
Posted on