npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

postcss-zero-specificity

v0.0.1

Published

PostCSS plugin that wraps every selector in :where(), reducing its specificity to zero — the full power of selectors without the brittleness of BEM

Downloads

10

Readme

postcss-zero-specificity

PostCSS plugin that wraps every selector in :where(), reducing its specificity to zero — the full power of selectors without the specificity wars, and without needing naming conventions like BEM to keep everything flat.

npm install postcss-zero-specificity
import postcss from "postcss";
import zeroSpecificity from "postcss-zero-specificity";

postcss([zeroSpecificity()]);
#nav .item a:hover {}
/* becomes */
:where(#nav .item a:hover) {}

With every selector at zero specificity, the cascade reduces to source order: later rules win, and any override — a user's stylesheet, a component consumer's one-liner — actually works without specificity arms races.

Pseudo-elements

Pseudo-elements are invalid inside :where(), so they stay outside — along with any pseudo-classes after them, which address the pseudo-element itself:

a:hover::before {}          /* becomes */  :where(a:hover)::before {}
.tabs::part(tab):hover {}   /* becomes */  :where(.tabs)::part(tab):hover {}

What stays outside keeps its specificity — the pseudo-element's own, plus that of any pseudo-classes addressing it and of ::slotted()/::part() arguments. That's inherent to pseudo-elements, not a plugin limitation.

Details

  • Each selector of a list is wrapped separately: a, .b becomes :where(a), :where(.b).
  • Selectors that are already zero-specificity (*, a lone :where()) are left alone, and the output is idempotent.
  • & goes inside the wrapper (&:hover → :where(&:hover), bare & → :where(&)) — usually a no-op specificity-wise since parents get wrapped too, but load-bearing where & carries specificity of its own, like inside @scope.
  • Selectors are transformed everywhere they appear — inside @media, @supports, @layer, @container, @scope, and any at-rule the plugin has never heard of — except @keyframes step names, which aren't selectors.
  • @scope prelude selectors contribute no specificity to scoped rules, so they are left untouched.
  • Shadow DOM caveat: selectors built around :host/:host() are best excluded — browser support for :host inside :is()/:where() has historically been inconsistent, and :host() arguments keep their specificity regardless.

Composing with other transforms

This plugin is built on postcss-selector-transform, which parses each rule's selector once and runs any number of transforms over the shared AST. The underlying transform is exported as zeroTransform, so it can share a parse with other transforms:

import selectorTransform from "postcss-selector-transform";
import { zeroTransform } from "postcss-zero-specificity";

selectorTransform({
	transforms: [myOtherTransform, zeroTransform],
});