CSS :focus-within
The :focus-within
pseudo-class is a CSS Level 4 selector that has recently picked up a bit of steam with FireFox, Safari, and (at the time of writing this) soon-to-be Chrome supporting it. With that in mind, and since you’re probably using Chrome right now (you are, aren’t you?), you can go turn on support via the “Experimental Web Platform Features” flag, or download Chrome Canary.
So what does it do, and why is it awesome?
:focus-within
is a pseudo-class you add to an existing CSS selector, much like you would with :focus
or :hover
. The pseudo-class styling kicks in when either the element with :focus-within
becomes focused, or a focusable child of that element is focused.
Let’s look at the following demo with accompanying code. Try tabbing through the below example:
<div class="my-element">
<p>A paragraph</p>
<p>
<a href="http://scottohara.me">
My Website
</a>
</p>
<label for="wut_email">
Your email:
</label>
<input type="email" id="wut_email" />
</div>
/*
A normal (though ugly) focus
pseudo-class. Any element that
can receive focus within the
.my-element parent will receive
a yellow background.
*/
.my-element *:focus {
background: yellow !important;
color: #000;
}
/*
The :focus-within pseudo-class
will NOT style the elements within
the .my-element selector, like the
normal :focus above, but will
style the .my-element container
when its focusable children
receive focus.
*/
.my-element:focus-within {
outline: 3px solid #333;
}
The above .my-element
will receive a 3px outline if it programatically receives focus (because <div>
s are not meant to receive keyboard focus, without a set tabindex
), or if the link or input elements within the <div>
receive focus. You can play around with the above basic focus-within demo on codepen.
Focus-within examples
:focus-within
is pretty powerful and is not a pseudo-class you would want to add to elements all willy-nilly. Since the pseudo-class activates when any element within it receives focus, it definitely should be used sparingly and likely not on an element that contains many sub-components that also contain focusable child elements.
With that said, there are some common UX interactions that would benefit from :focus-within
’s behavior, that were previously reliant on JavaScript to reproduce for keyboard interactions.
Highlighting table rows
One common example: table rows that change background color on hover. Styling like this may be implemented to help sighted users parse complex/long tables, as the visual highlight can make it easier for these user to keep track of the table row they’re currently reading. Unfortunately, without JavaScript, there was no way to do this for sighted keyboard users, until now:
The above example showcases how one can use the :focus-within
pseudo-class to highlight an entire table row, if there is at least one focusable element within a table cell of a particular row.
The most important CSS from the table example is the following:
tbody tr:focus-within,
tbody tr:hover {
background: rgba(lightBlue, .4);
}
But you can view the rest of the table example on codepen.
Drop down navigation menus
The immediate use cases I thought of, when first learning about :focus-within
was how it could be used to basically eradicate the need for JavaScript for navigation drop down menus. Eradicate…once being a relative term, since I would obviously still want less than bleeding-edge browsers to be able to access the menus. But I digress…
Drop Down Navigation Menu with JavaScript
In the above example, JavaScript is used to keep track of when a user’s keyboard focus is within one of the navigation drop menus. If JavaScript detects focus is on one of the child links, then a class of .nav__list__drop.has-focus
will be added to the parent <li>
of the primary navigation link:
<ul>
<li class="nav__list__drop has-focus"> <!-- focused!! -->
<a href="#!">Primary Link</a>
<ul class="nav__list__drop">
<li>
<!--
if this <a> has focus, add .has-focus
to closest nav__list__drop parent
-->
<a href="#!">Drop Menu Link</a>
</li>
</ul>
</li>
</ul>
Please see the full navigation demo on codepen to see the rest of the CSS and JavaScript needed to reveal drop down menus for keyboard users.
Drop down navigation menu using :focus-within
Now here’s the same example, minus all of the JavaScript, and changing the previous .nav__list__drop.has-focus
to .nav__list__drop:focus-within
.
No JavaScript event listeners needed!
Note: I have not done any responsive or touch screen specific work for the drop menu examples. To reveal these drop menus on touch devices, JavaScript would need to be reintroduced, or the design of the navigation would need to be updated to reveal the drop menu links by default. Just remember, JavaScript isn't bad for accessibility, but bad JavaScript can be!
Support
Likely not a shocker to anyone, but :focus-within
still has a ways to go before it can readily be used on projects without any sort of polyfill. This is incredibly important to note as Keith J. Grant’s tweet explains:
Note: if a selector list contains an invalid selector, the entire list is considered invalid. Thus this doesn’t gracefully degrade: pic.twitter.com/HKJjZgp9GO
— keith•j•grant (@keithjgrant) May 9, 2017
At the time of writing, May 2017, here are the browsers that support :focus-within
, without needing to turn on experimental flags.
- FireFox 52+
- Chrome 60+
- Desktop Safari 10.1
- Mobile Safari 10.3
Fortunately, Rodney Rehm has a focus-within polyfill that you can checkout.
Link roundup:
You can check out the source code for all of the demos in this article via the listed CodePen and GitHub links below: