CSS :is() :where() the Magic Happens Posted October 7, 2025 by Matthias Ott 53 Webmentions #blogtober #css For Blogtober, I dug up a draft about the two CSS pseudo-class functions :is() and :where() that I’d had lying around in my drafts folder for quite some time. Actually, when I originally started writing this post, :is() and :where() had just landed in CSS, and — just like with so many other new CSS features — I was expecting them to “change the way we write CSS.” Both are now widely available baseline features supported by all modern browsers. We often write about CSS features when they are brand new, but it is equally interesting to see how those shiny new features are actually being used in the wild after they’ve been around for a while. In the case of :is() and :where(), it seems like although browsers support is great and they are two of the most useful modern CSS features, they remain a bit underused. In the latest State of CSS Survey, only 45 % of respondents answered that they have used :where(), for example. So it might make sense to take another look at how these two functions are best used in practice. But first, let’s quickly recap what :is() and :where() actually do. Both pseudo-class functions, which are included in the CSS Selectors Level 4 Working Draft, take a selector list as their argument. They will then select any element that matches any of the selectors in the list. This allows you to simplify your code significantly. For example, you can stop to repeat to repeat to repeat yourself: /* A selector list like this… */ main h1, main h2, main h3, main h4, main h5, main h6, section h1, section h2, section h3, section h4, section h5, section h6, article h1, article h2, article h3, article h4, article h5, article h6, aside h1, aside h2, aside h3, aside h4, aside h5, aside h6 { color: blue; } /* …becomes this. */ :is(main, section, article, aside) :is(h1, h2, h3, h4, h5, h6) { color: blue; } In the example above, all headlines within any of the HTML elements main, section, article, or aside will be styled. The improvements in terms of readability and selector length are obvious. This alone is reason enough to use :is() and :where() more often. The Forgiven # But using :is() and :where() has another huge advantage: Usually, when you write a list of selectors and the browser doesn’t understand just one of the selectors in the list, the whole list is deemed invalid and will be ignored by the browser. Like, completely. I wasn’t aware of this for a very long time. Not so with :is() and :where()! If a selector fails, it will be ignored, but the rest of the selector list will still be used by the browser. This forgiving selector list makes your CSS more flexible and also makes it much easier to use new selectors in your CSS. /* The whole selector list fails – the heading won’t be red ☹️ */ h1:-moz-shimmy-shimmy-ya, h1.heading { color: red; } /* This still styles all h1s with a .heading class 🥳 */ h1:is(:-moz-shimmy-shimmy-ya, .heading) { color: red; } See how this little detail allows you to use new CSS selectors more easily without breaking your code? Huge for using progressive enhancement in your styles. A Tale of Two Specificities # But what is the difference between :is() and :where()? The difference lies in how both selectors handle specificity. When you use :is(), the whole rule will always take on the specificity of the most specific selector in the list you provide. If you are using an ID selector, for example, all the other selectors will also share the specificity of this selector, even if they don’t include this or any other ID. If you use :where(), however, the specificity will always be 0. Both things are important to consider. Let’s say you are writing a rule like this to style the links within a header element and, more specifically, your .site-header: :is(header, .site-header) a { color: black; } If we later want to override the color of our link with a element selector, we run into a problem. header a { color: blue; } Because the :is() selector includes a class, its specificity is higher than a selector that only uses element names. As a result, the second rule can’t override it because it has lower specificity. This is where :where() comes in handy. Because the specificity of :where() is always 0, we can use it to write global styles that can easily be overwritten with rules that even have a low specificity. No need to use !important anymore or to artificially increase the specificity of a rule when you want to overwrite the code of a CSS framework or the components of your design system. If you set the margin for the text elements inside a card component like this, for example: .card :where(h1, h2, h3, p) { margin: 0; } The specificity of the whole sector will be the same as if we were just using .card, which makes it much easier to override the styles with more specific rules later. More Use-Cases for :is() and :where() # When it comes to how people have started applying :is() and :where() in the wild, using it for scoped resets that shouldn’t override component styles might actually be the most common use-case. You will find a lot of CSS resets, layout and typography base styles, and also utility frameworks using :is() and :where(). Manuel, for example, recently revealed his “different type of reset style sheet”, UA+ (user agent plus) in his talk at Smashing Conference. In the reset, he is using :where() almost every:where(), for instance to inherit font styling in form elements: :where(button, input, select, textarea) { font-family: inherit; font-size: inherit; } But it doesn’t stop there. If we think about how we can combine the two pseudo-class functions with other selectors, it gets really interesting. Combining :is() and :where() with :has() # If we throw :has() into the mix, for example, we can create really flexible selectors. Like in this example: section:has(:is(figure, video, iframe)) { padding: 2rem; background: var(--media-background-color); } We are styling the section, but only if it is the parent container of a figure, video, or iframe element. We could also use :where() to style layout containers with zero specificity. In this case only if they contain a direct child that we later want to style with a container query: :where(article, section, aside):has(> .flexible-card) { container-type: inline-size; } @container (width > 30em) { .flexible-card { display: grid; grid-template-columns: 1fr 2fr; } } Or, we could style form labels differently when they are followed up by any :invalid form element, using :is() to keep the selector concise: label:has(+ :is(input, select, textarea):invalid) { color: red; font-weight: bold; } And here’s Manuel again, turning labels into block elements: :where(label):has(+:where(textarea, input, select)) { display: block; } As you can see, both :is() and :where() come with many advantages beyond just reducing the complexity of your code and provide a lot of ways to write more flexible, useful, and maybe even a bit magical CSS. There are a lot of reasons to include them in your arsenal of modern CSS tools today. And now, I’m curious again: How do use :is() and :where()? Are there any smart techniques that you are using often? Let me know, for example on Mastodon, Bluesky, or by email ❦ This is post 7 of Blogtober 2025. ~ 53 Webmentions dch :flantifa: :flan_hacker: 8 October 2025 | 16:44 @matthiasott love these posts thankyou Matthias Ott 8 October 2025 | 16:46 @dch Thank you, too – happy you enjoy them! :) Frontend Dogma 27 October 2025 | 20:40 CSS :is() :where() the Magic Happens, by @matthiasott: https://matthiasott.com/notes/css-is-where-the-magic-happens #css #selectors #cascade cascade css selectors CSS :is() :where() the Magic Happens · Matthias Ott 13 Reposts KB 9 October 2025 | 07:35 Ryan Mulligan 9 October 2025 | 07:35 björn 9 October 2025 | 07:35 Andy Bell 9 October 2025 | 07:35 dch :flantifa: :flan_hacker: 9 October 2025 | 07:35 Stephen Bannasch (316 ppm) 9 October 2025 | 07:35 Valentino Gagliardi 9 October 2025 | 07:35 Sara Joy ☠️ 9 October 2025 | 07:35 Jean Pierre Kolb 9 October 2025 | 07:35 Charles Bauer 9 October 2025 | 07:35 Joshua Iz 9 October 2025 | 07:35 Nicod, The Fediverse Dude 🐒 9 October 2025 | 07:35 Ghazal 13 October 2025 | 12:11 37 Likes Heather Buchel 9 October 2025 | 07:35 JauntyWunderKind 9 October 2025 | 07:35 Thomas Reimer 9 October 2025 | 07:35 David Bushell 👻 9 October 2025 | 07:35 Bill Criswell 9 October 2025 | 07:35 🄰🄻🄸 🇵🇸 9 October 2025 | 07:35 Andy Bell 9 October 2025 | 07:35 Nilesh Prajapati 9 October 2025 | 07:35 luci 9 October 2025 | 07:35 dch :flantifa: :flan_hacker: 9 October 2025 | 07:35 Aaron In Minnesota 9 October 2025 | 07:35 Nexii 9 October 2025 | 07:35 Matthias Zöchling 9 October 2025 | 07:35 Johnny Taylor 9 October 2025 | 07:35 Ian Sutherland 🇨🇦 9 October 2025 | 07:35 Brandon Kelly 9 October 2025 | 07:35 uralrex 9 October 2025 | 07:35 Sara Joy ☠️ 9 October 2025 | 07:35 Stephanie Tuerk 9 October 2025 | 07:35 Charles Bauer 9 October 2025 | 07:35 Jordi Sánchez 9 October 2025 | 07:35 Joshua Iz 9 October 2025 | 07:35 Mastro 9 October 2025 | 07:35 Konstantin Dankov 9 October 2025 | 07:35 André Ruffert 9 October 2025 | 07:35 Поміркований бандера 🇺🇦 9 October 2025 | 07:35 Øystein Håberg 9 October 2025 | 07:35 Andrew Aquino 9 October 2025 | 07:35 RogerPence 9 October 2025 | 07:36 geeky Chakri 🚀 9 October 2025 | 10:34 finxol 9 October 2025 | 11:57 Mudasser Ali 9 October 2025 | 12:11 Isabella Joshua 9 October 2025 | 14:12 Sherin Joseph Roy 9 October 2025 | 14:24 Adam Argyle 10 October 2025 | 23:59 Ahmad Shadeed 11 October 2025 | 00:00 Rickard Natt och Dag 11 October 2025 | 00:00 ⓘ Webmentions are a way to notify other websites when you link to them, and to receive notifications when others link to you. Learn more about Webmentions. Have you published a response to this? Send me a webmention by letting me know the URL. Ping! More Notes Ad Infinitum Lazy and Prompt Buckle Up At Machine Speed
Frontend Dogma 27 October 2025 | 20:40 CSS :is() :where() the Magic Happens, by @matthiasott: https://matthiasott.com/notes/css-is-where-the-magic-happens #css #selectors #cascade cascade css selectors CSS :is() :where() the Magic Happens · Matthias Ott