There’s a nice article by Enzo Manuel Mangano called Checkbox Interactions – The beauty of Layout Animations. In the end, you get some nicely animated checkboxes, essentially:

I like it.
It’s a modern-looking multiple-choice with very clear UX.
Enzo’s tutorial is all React Native-ified. I think Enzo is a React Native guy and that’s his thing. And that’s fine and makes sense. A lot of time UI like this is part of highly dynamic web apps that deserve that kind of treatment and embracing that style of code to help it live there is fine.
But I wouldn’t want anyone to think that it’s necessary you reach for a tool like React Native to create something like this. This is actually pretty darn simple HTML & CSS that can be quite performant, semantic, and accessible.
Each of those “buttons”, is really this:
<label>
Italian
<input type="checkbox" class="screenreader-only">
<svg>
<path d="..." />
<svg>
</label>
Each button is really a label
, because then you are appropriately connecting the text of the label to the checkbox in an accessible way. I will call out Enzo for this one: his final demo outputs <div>
s and <span>
s, which you can’t even tab to, so it’s a pretty inaccessible final result.
When it’s a label, the whole thing becomes “clickable” then, toggling the checkbox within.
We can hide the actual checkboxes (notice the UI doesn’t actually show them) by applying an accessible screenreader-only class. But each of them remains an interactive element and thus are able to be tabbed to. But because we can’t see them, we should do…
label {
cursor: pointer;
&:focus-within {
outline: 3px solid orange;
}
}
This will ensure there is a visual indicator when any of them are focused. And with that focus, the spacebar will work to activate them like any normal checkbox.
The fun part though is the neat interaction where activating one of the options animated the checkbox in and changes some colors. It’s easy! I promise! We just check if the label has a checked input and make those changes. All right in CSS.
label {
--highlightColor: oklch(0.764 0.1924 65.38);
cursor: pointer;
border: 1px solid white;
&:focus-within {
outline: 3px solid var(--highlightColor);
}
svg {
width: 0;
transition: width 0.66s;
fill: var(--highlightColor);
}
&:has(:checked) {
border-color: var(--highlightColor);
background: color-mix(in srgb, var(--highlightColor), transparent 80%);
svg {
width: 0.88lh;
}
}
}
The finished demo, I’d say, is near 100% the same experience as Enzo’s. Cheers!