The :not() pseudo-class matches elements that do not match the selector list inside the parentheses. It is the negation filter for CSS selectors.
Example: li:not(.active) styles every list item except those with class active. Use it to write one rule with a clear exception instead of duplicating overrides.
Baseline — widely available.
Supported across modern browsers since 2015. Selector lists inside :not() (Level 4) are widely supported in current browsers; very old engines only allowed a single simple selector.
Combine with a structural pseudo-class::not(:last-child).
First and second paragraphs match and get a bottom border.
The last paragraph is excluded — no divider under it.
Cleaner than styling all p then undoing the last.
Edge Cases & Pitfalls
Most surprises come from specificity, descendant paths, or invalid arguments.
specificity
Specificity comes from the argument
:not() uses the specificity of its most specific selector (like :is()). #foo:not(#bar) is as heavy as two IDs.
descendants
“Not X” still matches paths through X
body :not(table) a can still style links inside a table, because a parent like td is not a table. Prefer a:not(table a) when you mean “links that are not under a table.”
invalid
One bad argument can drop the rule
An unsupported selector inside :not() may invalidate the whole declaration. Use :not(:is(…)) for a forgiving list.
pseudo-elements
No ::before / ::after inside
Pseudo-elements are not valid arguments. Negate elements, classes, attributes, or other pseudo-classes only.
Compatibility
Browser Support
:not() is a browser CSS pseudo-class. Node.js, Deno, and Bun do not apply it.
✓ Baseline · Widely available
:not()
Core :not() has been available across browsers since mid-2015. Complex selector lists inside :not() are supported in current Chromium, Firefox, and Safari. Internet Explorer 9+ supports a simpler form (one simple selector).
98%Widely available
Google ChromeSupported
Full modern support
Mozilla FirefoxSupported
Full modern support
Apple SafariSupported
Full modern support
Microsoft EdgeSupported
Full modern support
OperaSupported
Full modern support
Samsung InternetSupported
Full modern support
Internet Explorer9+
Simple selector only
:not()Baseline · Widely available
Bottom line: Safe for everyday exclusions. Prefer selector lists in modern browsers; chain :not() if you must support very old engines. This is not a JavaScript runtime API.
Remember
Key Takeaways
Role: match elements that fail the selector inside :not().
Lists::not(.a, .b) equals :not(.a):not(.b).
Start simple:li:not(.active) { … }.
Watch specificity and descendant traps; no pseudo-elements inside.
One line::not() styles “everything except …” — keep the exception short and intentional.
Frequently Asked Questions
It matches elements that do not match the selector list inside the parentheses. Use it to style everything except a class, type, attribute, or state.
Yes. Modern CSS allows a comma-separated list: :not(.a, .b), which is the same as :not(.a):not(.b). Older code often chains :not() instead.
No. Pseudo-elements such as ::before or ::after are not allowed inside :not(). Simple, compound, and complex selectors (without pseudo-elements) are allowed.
The specificity of :not() is the specificity of its most specific argument — the same idea as :is(). For example, #foo:not(#bar) is as specific as two IDs.
An invalid selector inside :not() can invalidate the whole rule. Wrap the list in :is() for a forgiving list: :not(:is(.foo, :bad)).
No. It is a browser CSS selector. Node.js, Deno, and Bun do not apply it.