Made in Builder.io

Ship Your First Personalized Web Experience webinar on June 15 @ 10AM PT. Register Now

Talk to Us
Product
Developers
Talk to Us

Blog

Home

Resources

Blog

Forum

Github

Login

Signup

×

Visual CMS

Drag-and-drop visual editor and headless CMS for any tech stack

Theme Studio for Shopify

Build and optimize your Shopify-hosted storefront, no coding required

Resources

Blog

Get StartedLogin

‹ Back to blog

Web Development

Simpler CSS Selectors With :is()

January 19, 2023

Written By Steve Sewell

If you’ve got redundant CSS selectors like this:

/* 😕 */
.active a,
.active button,
.active label {
  color: steelblue;
}

Did you know that you can rewrite it like this?

/* 🤩 */
.active :is(a, button, label) {
  color: steelblue;
}

That’s right, the :is() pseudo-class is built into plain-ole CSS now.

You can use :is() to group any part of a selector, for instance, you could similarly transform this:

.section h2,
.aside h2,
.nav h2 {
  color: steelblue;
}

into just this:

:is(.section, .aside, .nav) h2 {
  color: steelblue;
}

But :is() is not just useful for parents and children - it can also select multiple adjoining selectors as well, like:

button:is(:focus, :hover, :active) {
  color: steelblue;
}

button:is(.active, .pressed) {
  color: lightsteelblue;
}

Which behave equivalent to:

button:focus, button:hover, button:active {
  color: steelblue;
}

button.active, button.pressed {
  color: lightsteelblue;
}

:where() is a very similar pseudo-class to :is() and is worth being aware of as well. They look very similar:

:where(.section, .aside, .nav) h2 {
  color: steelblue;
}

But the difference is that :where has a specificity of 0, whereas :is() always takes on the specificity of the most specific selector in the list.

So, do you know what color the button will be in this CSS?

:is(html) button {
  color: red;
}

:where(html) button {
  color: blue;
}

In the above example, although the block starting with :where() is below the block starting with :is(), the :is() block has a higher specificity (1 for the html tag, + 1 for the button), vs the block below it that has a lesser specificity (0 for html because it is within :where, and +1 for button).

A related, but very different, pseudo-class is :has(). :has() allows you to select a parent that contains a child matching a selector (or set of selectors).

An example use case of :has() is to not underline links that contain an image or video:

a { text-decoration: underline }

/* Links are underlined, unless they contain an image or video */
a:has(img, video) {
  text-decoration: none;
}

Now, if by default our a tags have underlined text, but we have an image or video inside one, the underlining will be removed for any matching anchor elements.

You can also combine this with :is()


:is(a, button):has(img, video) {
  text-decoration: none;
}**Note though that :has() is [*not* yet supported in all major browsers](https://developer.mozilla.org/en-US/docs/Web/CSS/:has#browser_compatibility), so use with caution.**

Now you may be reading this and saying “SCSS can do this!”, and you may even prefer its syntax:

.active {
  button, label, a {
    color: steelblue;
  }
}

And you would be right, this is quite elegant. But it seems like every day CSS natively gets features that we once needed SCSS (or other preprocessors) for.

CSS variables has also been another incredible addition to CSS itself, that it begs the question when or how often you really need preprocessors anymore:

/* Plain ole modern CSS baby */
.active :is(a, button, label) {
  --color: steelblue;
  color: var(--steelblue);
}

That’s not to say preprocessors don’t still have their use cases and merits.

But I think at one point in time they were truly mandatory for handling any non-trivial CSS (elegantly, at least), and now that’s not as much the case anymore.

Just to leave things on a high note, I want to point out that the future of CSS continues to look bright. The CSS Working Group is actively working on adding nested selectors directly to CSS. They are actively deciding between 3 possible syntaxes (known as options “3, “4”, and “5”):

/* Option 3 */
article {
  font-family: avenir;
  & aside {
    font-size: 1rem;
  }
}

/* Option 4 */
article {
  font-family: avenir;
} {
  aside {
    font-size: 1rem;
  }
}

/* Option 5 */
@nest article {
  & {
    font-family: avenir;
  }
  aside {
    font-size: 1rem;
  }
}

Which do you like best? And does your choice match the winner in the official poll?

Spoiler alert - #3 won the poll, so we may be getting a very SCSS-like nesting syntax as an icing on the cake to CSS soon, assuming the CSSWG’s chooses the poll winner.

And yes, I agree, Option 4 is an absolute abomination that needs to burn in fire.

The :is() and :where() pseudo-classes are supported in all major browsers:

Browser support table - screenshot from the link directly below - showing full browser support

Source: MDN

Note that the :has() pseudo-class that we touched on here does not have the same level of support, so use :has() with caution:

Browser support table - screenshot from the link directly below - showing only partial browser support

Source: MDN

Modern CSS is awesome, and only keeps getting better. Next time you’ve got redundant selectors in your code - don’t forget to reach for that handy :is() pseudo-class.

About me

Hi! I'm Steve, CEO of Builder.io.

We make a way to drag + drop with your components to create pages and other CMS content on your site or app, visually.

You can read more about how this can improve your workflow here.

You may find it interesting or useful:

Visually build with your components

Builder.io is a headless CMS that lets you drag and drop with your components right within your existing site.

// Dynamically render your components
export function MyPage({ json }) {
  return <BuilderComponent content={json} />
}

registerComponents([MyHero, MyProducts])

Share

Twitter
LinkedIn
Facebook
Hand written text that says "A drag and drop headless CMS?"

Builder.io is a headless CMS that lets you build visually with your components.

Learn more

Like our content?

Join Our Newsletter

Continue Reading
Web Development11 MIN
Next.js 13 - Routing Fundamentals and Beyond
WRITTEN BYVishwas Gopinath
June 9, 2023
Latest News7 MIN
Builder Drop: More insights, environments and localization permissions
WRITTEN BYBuilder Team
June 8, 2023
Latest News6 MIN
The Dev Drop: Multi-threading in JSX, Resumability, Qwik Case Study, React in 2023
WRITTEN BYYoav Ganbar
June 1, 2023