There are many ways to apply focus styles with CSS each with their own trade-offs. There are two main challenges with focus styles:

  1. Most of the time we only want to apply focus styles when using a keyboard so that the UI doesn’t feel clumsy with a mouse
  2. Sometimes we need to apply a style to a container based on whether a child has focus (a control inside a Card for example)

The first and oldest approach is to use the :focus pseudo-class. The problem is that it applies regardless of input method. So when you click a button with the mouse, a focus ring will appear. It also only applies directly to the selected element so you can’t apply a style to a container when a child has focus.

Next is the :focus-within pseudo-class which enables styling a container when a child has focus. This is great, but it still has the drawback that it always applies regardless of input method. What if you only want to apply a container focus style when a child is tabbed to with the keyboard?

FInally, there’s the :focus-visible pseudo-class which enables you to apply styles when focus was not triggered using a mouse. The browser uses heuristics to determine whether the style should apply based on the input method.

So the two problems have been solved individually, but what about if we want both – to style a container based on child focus but not when using a mouse? Unfortunately you can’t do something like :focus-visible:focus-within, that doesn’t work. But there is an approach using an even newer CSS feature – the :has() selector.

We can combine :has() with :focus-visible:

.card:has(:focus-visible) { ... }

Using this selector, we can apply a focus style to the .card when any interactive child is focused with the keyboard, and not with the mouse.