🔬 Using :focus-within to style focus states in a multi-select dropdown (#72)
Happy Wednesday!
I've been working on building a multi-select dropdown this week at work. One of the requirements is that it supports keyboard handling.
Material UI has a similar multi-select dropdown to the one I'm building. The image below represents an example of a dropdown panel with a multiple unfocused dropdown checkbox items, and one focused dropdown checkbox item.
When a user navigates through the dropdown panel with arrow keys, the focused dropdown checkbox item should show the lighter gray style.
Building this is a bit trickier than one might expect though, because while the browser automatically focuses on the actual checkbox, it doesn't apply a focus state on the container that wraps it. Let's dig in!
Here's a basic example of how a DropdownCheckboxItem
can be structured:
const DropdownCheckboxItem = (props: { label: string }) => { const { label } = props; return ( <label className="dropdown-checkbox-item-label"> <input type="checkbox" /> {label} </label> ); };
As a user navigates within the dropdown panel using the arrow keys, the browser will handle focusing each <input type="checkbox" />
in the panel. But we need to apply a focus state to the whole <label />
, not the <input />
.
The :focus-within
pseudo-class allows us to capture that the nested <input />
has been focused by the browser, and then apply whatever focus styles we'd like to the parent, which in this case is the <label />
tag.
.dropdown-checkbox-item-label { // apply unfocused label styles here } .dropdown-checkbox-item-label:focus-within { // apply focused label styles here }
Huge shoutout to Jimmy Wilson for showing me the :focus-within
pseudoclass!
Talk soon,
Mae