New and Extended Selectors

Learn about new CSS selectors, including, is(), :where(), and :has(), along with others for focus selection and dynamic list styling.

Three of the most influential changes to CSS recently have been the :is, :where, and :has pseudo-class selectors. Here’s a summary of them:

  • :is() accepts a selector list and equals the specificity of the highest selector. For example, :is(#id, a, .class) will have the specificity of an id .

  • :where() accepts a selector list and always has zero specificity, making it a popular choice for defining baseline styles in resets.

  • :has() is the long-awaited “parent selector,” which allows checking a parent for a particular child and then styling either the parent or extending into a compound selector to style the children.

Note: Uniquely, both :is() and :where() are also forgiving selectors. This means that, if an invalid selector or a prefixed selector that doesn’t apply is in the list, the rule will continue to work for the valid selectors. This is contrary to standard compound rule definitions like the -webkit-property property, where browsers that don’t understand -webkit-property will throw out the whole rule.

The following code utilizes :where() , :is() , and :has() to create an author bio component that changes grid display properties depending on whether or not an avatar is available.

Note: The :has() only has partial browser support at the time of writing, so the demo above will currently work in Safari 15.4+ and Chrome/Edge 105+, as well as Firefox 103 with the layout.css.has-selector.enabled flag enabled.

Enhanced :not() selector

The :not() selector has recently been enhanced to accept a selector list, making :not(nav a, footer a) valid. However, unlike :is() and :where() , the update hasn’t made :not() forgiving of invalid selectors due to backward compatibility support.

Focus selectors

The next two new pseudo-classes both affect focus behavior. The :focus-within selector can be used to style a parent when a child is in focus—such as a container around a form field. For element focus styles, we can now use :focus-visible , which has recently replaced :focus as the cross-browser default for styling elements that receive focus.

The :focus-visible pseudo-class makes defined styles visible based on heuristics. Practically speaking, this involves setting focus styles when elements gain focus via keyboard tabbing, but not via mouse clicks.

The ::marker pseudo-element

Last but not least, the ::marker pseudo-element allows us to directly select and style list item bullets and numbers on <ul> and <ol> elements, as well as the “caret” for the <summary> element. This means we can use ::marker to change just a list’s bullet color! There’s a reduced set of allowed properties for styling with ::marker , and it may not be the best approach, depending on the complexity of a design.