You need to reach as many users for your digital product as possible. Accessible design is how you do that. Book a free consult to learn how we bake accessiblity in from the start to maximize your user base.
In the early versions of Tailwind, there would often need to be additional logic written into components for
conditionally toggling long strings of utility classes. Things would get even messier when that logic involved styling
descendent elements based on the parent element’s state. With the addition of Tailwind’s aria-*
modifiers and group
helper classes, conditionally styling components has gotten significantly cleaner and more legible. Let’s take a look at
this in practice by creating an accessible toggle switch component in Tailwind.
We’ll start out by styling a simple toggle button in its default “off” state
<button type="button" id="toggle-switch" class="flex w-14 h-8 p-1 rounded-full bg-slate-400 overflow-hidden outline-none focus-visible:ring focus-visible:ring-purple-400">
<span class="w-6 h-6 rounded-full bg-white"></span>
</button>
<label for="toggle-switch">
Switch off
</label>
This will create a left-aligned circle within a gray container, indicating its “off” state. It’s also good practice to include accompanying text to clear up any ambiguity on the state of this toggle switch, so we aren’t relying on visual cues only.
The aria-checked
Attribute
Next, we’ll need to add styling for the “on” state. One approach is to toggle an .active
class on the button to
visually distinguish between “on” and “off” states. But this adds more complexity and classes. Instead, we can leverage
the state of ARIA attributes to conditionally style our button component. Tailwind provides a few attribute selector
utility classes out of the box.
For this component, we will be using the ARIA switch
role. This role can only have two possible values: “on” and
“off.” When using role="switch"
, the aria-checked
attribute is required. The aria-checked
attribute indicates the
current “checked” state of checkboxes, radio buttons, and other widgets.
We can add the aria-checked:
utility class to change the button background color when aria-checked
’s value is true
<button type="button" id="toggle-switch" role="switch" aria-checked="true" class="flex w-14 h-8 p-1 rounded-full bg-slate-400 transition-colors duration-200 ease-in-out overflow-hidden outline-none focus-visible:ring focus-visible:ring-purple-400 aria-checked:bg-blue-400">
<span class="w-6 h-6 rounded-full bg-white"></span>
</button>
<label for="toggle-switch">
Switch on
</label>
The group
Helper Class
This changes the background color, but we’ll also want to change the positioning of the inner circle to indicate that
the toggle is now switched on. In CSS, you can do this by targeting the nested span
within the button. You can also
achieve this in Tailwind by using an arbitrary variant. This approach would look like:
class="[&>span]:aria-checked:translate-x-full"
While this works and does keep all styling inline, there can be gotchas down the road as the project grows: What if the inner element changes and the selector no longer works? What if more markup is added later? We’d need to create more arbitrary variants for each affected element, and that can become cumbersome and hard to keep readable.
Enter Tailwind’s group
helper class. These classes are helpful when you need to style elements based on the state of
the parent element. Add a group
class to the parent, and use group-*
modifiers to style the target element.
In our case, we want to conditionally set the circle’s (child) position based on the button’s (parent) aria-checked
value. Adding the appropriate group
classes in our markup achieves this.
<button type="button" id="toggle-switch" role="switch" aria-checked="false" class="group flex w-14 h-8 p-1 rounded-full bg-slate-400 transition-colors duration-200 ease-in-out overflow-hidden outline-none focus-visible:ring focus-visible:ring-purple-400 aria-checked:bg-blue-400">
<span class="w-6 h-6 rounded-full bg-white transition-transform duration-200 ease-in-out group-aria-checked:translate-x-full"></span>
</button>
<label for="toggle-switch">
Switch off
</label>
Final Result
The resulting component will only rely on keeping track of the button’s aria-checked
value as it is clicked. All
conditional styling will be handled by Tailwind’s aria-checked
and group
classes.
Conclusion
Using Tailwind’s built-in aria-*
modifiers and group
helper classes cleans up our inline markup and allows us to
conditionally style both parent and child elements seamlessly. This aids in trimming down the amount of additional logic
that has to be written into our components, as well as keeping our code concise and legible as the project grows.