CSS :Is() :Where() the Magic Happens ![rw-book-cover](https://matthiasott.com/assets/pictures/_twitterimage/blogtober-2025-day-07_2025-10-08-133407_dvwc.jpg) ## Metadata - Author: [[Matthias Ott]] - Full Title: CSS :Is() :Where() the Magic Happens - Category: #articles - Document Note: --- tags: [CSS, Selectors, Web Development] author: Matthias Ott source: matthiasott.com reference: [https://matthiasott.com/notes/css-is-where-the-magic-happens] date created: 2025-10-08 00:00 --- # 🏷️ Title: CSS :is() :where() the Magic Happens ## 🧭 Table of Contents - [📝 Summary](#-summary) - [📖 Main Content](#-main-content) - [🗒️ Notes](#️-notes) - [📚 References](#-references) - [🗣️ Paraphrase](#️-paraphrase) - [📰 Newsletter](#-newsletter) - [✅ Action Items](#-action-items) - [🧪 Hypotheses](#-hypotheses) - [📊 Methodology](#-methodology) - [❓ Questions](#-questions) - [🔮 Future Research](#-future-research) - [💡 Implications](#-implications) - [🧠 Learning Styles Assessment](#-learning-styles-assessment) - [📊 Accessibility Assessment](#-accessibility-assessment) - [🌍 DEIB Assessment](#-deib-assessment) - [✅ Logical Validity](#-logical-validity) - [🛡️ DARVO Analysis](#-darvo-analysis) - [🎯 Conclusion](#-conclusion) ### 🔗 References - CSS Selectors Level 4 Working Draft (W3C): https://www.w3.org/TR/selectors-4/ - MDN on :is(): https://developer.mozilla.org/docs/Web/CSS/:is - MDN on :where(): https://developer.mozilla.org/docs/Web/CSS/:where - MDN on :has(): https://developer.mozilla.org/docs/Web/CSS/:has - State of CSS survey: https://2023.stateofcss.com/en-US/features/selectors/ ## 📝 Summary - What happened: The article explains how :is() and :where() simplify selectors, enable forgiving selector lists, and manage specificity; it also shows powerful combos with :has(). - Why it matters: Cleaner CSS, safer progressive enhancement, fewer !important overrides, and easier design system layering. - Key stakeholders: Frontend developers, designers, and framework authors. - Risks and tradeoffs: Misusing :is() can unintentionally raise specificity; support for :has() is newer than :is()/:where(). - Bottom line: Use :is() for brevity when specificity can rise; use :where() for zero-specificity base styles; combine with :has() for expressive patterns. ## 📖 Main Content - Core arguments: - Both functions accept selector lists and match any item, reducing repetition. - They are “forgiving”: unsupported selectors inside them are ignored individually rather than invalidating the whole rule. - Specificity differs: :is() adopts the most specific argument; :where() always remains zero, ideal for easily overridden base styles and scoped resets. - Combining with :has() unlocks parent/context-aware styling. - Context and terms: - These are part of the CSS Selectors Level 4 draft and are well-supported (:has() support arrived later but is now broadly available in modern browsers). - Uncertainties: - Adoption remains lower than expected (e.g., “only 45% used :where()” in a recent survey), suggesting education and examples are still needed. ```css /* Fewer repeats with lists */ :is(main, section, article, aside) :is(h1, h2, h3, h4, h5, h6) { color: blue; } /* Forgiving selector lists */ h1:is(:-moz-nonexistent, .heading) { color: red; } /* Zero-specificity base styles with :where() */ .card :where(h1, h2, h3, p) { margin: 0; } /* Context-aware patterns with :has() */ section:has(:is(figure, video, iframe)) { padding: 2rem; background: var(--media-background-color); } :where(article, section, aside):has(> .flexible-card) { container-type: inline-size; } @container (width > 30em) { .flexible-card { display: grid; grid-template-columns: 1fr 2fr; } } label:has(+ :is(input, select, textarea):invalid) { color: red; font-weight: 700; } ``` ### 🗒️ Notes - “Both are now widely available baseline features … yet remain a bit underused.” - “If a selector fails [inside :is()/:where()], it will be ignored, but the rest … used by the browser.” - “When you use :is(), [the rule] takes on the specificity of the most specific selector.” - “If you use :where(), however, the specificity will always be 0.” - Scoped resets and base styles commonly use :where() (e.g., inheriting form font styles). - Combining with :has() enables container- and context-aware patterns without JS. ## 📚 References - CSS Selectors Level 4 (W3C): https://www.w3.org/TR/selectors-4/ - MDN reference for :is(): https://developer.mozilla.org/docs/Web/CSS/:is - MDN reference for :where(): https://developer.mozilla.org/docs/Web/CSS/:where - MDN reference for :has(): https://developer.mozilla.org/docs/Web/CSS/:has - State of CSS features overview: https://2023.stateofcss.com/en-US/features/selectors/ ## 🗣️ Paraphrase - Paragraph A: :is() and :where() landed in modern CSS, are widely supported, reduce repetition, yet seem underused in practice. - Paragraph B: :is() and :where() accept selector lists, are forgiving to unsupported selectors, but differ on specificity—:is() inherits the highest in its list, :where() stays zero. ## 📰 Newsletter Want cleaner, safer CSS without specificity wars? Matthias Ott shows how :is() and :where() compress verbose selector lists, enable forgiving matching, and keep resets override-friendly. The kicker: combine them with :has() for expressive, context-aware UI without resorting to JS. Brush up with MDN’s guides on :is(), :where(), and :has(), and revisit your utility/reset layers to remove stray !important rules. ## ✅ Action Items #### DevRel - Publish concise demos comparing :is() vs :where() specificity. - Add playgrounds showing forgiving lists and progressive enhancement. - Update docs and examples in SDKs/boilerplates. - Track adoption in tutorials and workshops. #### OSPO - Add CSS lint rules highlighting risky :is() specificity. - Include selector support checks in CI and training. #### Community - Run clinic on “escaping specificity traps.” - Create “first-issue” tasks to refactor verbose selectors. #### WordPress - Core: audit resets to use :where() for zero-specificity bases. - Themes: replace repetitive selectors with :is(). - Plugins: prefer :where() in components to ease overrides. - Documentation: add examples to DevHub with support notes. #### The WPCC - Fund microgrants to refactor theme CSS with modern selectors. - Publish governance note on CSS standards adoption cadence. ### FAIR - Policies: document feature baseline and sunset old patterns. - Productized artifacts: publish selector style guide and scorecards. ## 🧪 Hypotheses - Using :where() in base layers reduces !important usage by ≥50%. - Refactoring repeated selectors to :is() shrinks CSS size by ≥10%. - Teams using :has()+:is() reduce DOM hooks for styling by ≥30%. ### 📊 Methodology - Pre/post refactor diffs: file size, selector count, !important count. - Lint/CI metrics on specificity and override frequency. - Dev surveys on ease-of-overrides and bug rates. ### ❓ Questions - Where does :is()’s raised specificity most commonly bite? - How broadly reliable is :has() across target browsers for production use? ### 🔮 Future Research - Patterns combining :has() with container queries at scale. - Tooling that auto-suggests :is()/:where() refactors. ### 💡 Implications - Practice: simpler overrides, smaller CSS. - Policy: baseline-driven CSS standards in repos. - Education: specificity-first teaching with modern selectors. ### 🧠 Learning Styles Assessment - Reading level: Flesch-Kincaid Grade ≈ 9–10. #### 👀 Visual learners - Present: concise code examples. - Improve: before/after selector diagrams. #### 🔊 Aural learners - Improve: short audio summary of specificity rules. #### 🗣️ Verbal learners - Present: clear definitions and contrasts. - Improve: analogy for “forgiving lists.” #### 🧩 Kinesthetic learners - Present: try-it snippets. - Improve: step-by-step refactor exercises. #### 🧠 Logical learners - Present: rule-based specificity reasoning. - Improve: decision tree for choosing :is() vs :where(). #### 🧑‍🤝‍🧑 Social learners - Improve: discussion prompts and pair refactors. #### 🧘‍♀️ Solitary learners - Present: standalone reading. - Improve: self-check quiz on specificity. #### 🌿 Naturalistic learners - Improve: ecosystem view of feature baselines. ### 📊 Accessibility Assessment #### Screen reader semantics - Improve: ensure examples include accessible naming context when styling forms. #### Keyboard navigation - Improve: show focus-visible patterns with these selecto - Summary: Matthias Ott is an independent user experience designer and web design engineer from Stuttgart, Germany. He also teaches Interface Prototyping at the Muthesius Academy of Fine Arts and Design. - URL: https://matthiasott.com/notes/css-is-where-the-magic-happens For [Blogtober](https://matthiasott.com/notes/blogtober-2025-day-01), 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](https://2025.stateofcss.com/en-US/features/#all_features), 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](https://www.w3.org/TR/selectors-4/), 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](https://drafts.csswg.org/selectors-4/#typedef-forgiving-selector-list) makes your CSS more flexible, 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 the both selectors handle [specificity](https://developer.mozilla.org/en-US/docs/Web/CSS/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. 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](https://matuzo.at/), for example, recently revealed his “different type of reset style sheet”, [UA+ (user agent plus)](https://fokus.dev/tools/uaplus/) in his talk at [Smashing Conference](https://smashingconf.com/freiburg-2025/speakers/manuel-matuzovic/). 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()](https://developer.mozilla.org/de/docs/Web/CSS/: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](https://fokus.dev/tools/uaplus/forms/label/): ``` :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](https://mastodon.social/@matthiasott), [Bluesky](https://bsky.app/profile/matthiasott.com), or by [email](mailto:[email protected]) This is post 7 of [Blogtober 2025](https://matthiasott.com/notes/blogtober-2025-day-01). #### 0 Webmentions ¯\\_(ツ)\_/¯ ⓘ 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.](https://webmention.io/), CSS :Is() :Where() the Magic Happens, Matthias Ott, 01k724d1mzg1e87y7fs91zstkm.