Jump to main content

CSS :focus-within

  • Published:
  • Category: CSS

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.

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…

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.

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:

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.

You can check out the source code for all of the demos in this article via the listed CodePen and GitHub links below: