CSS :not() Pseudo-Class

Beginner
5 min read
Updated: Oct 2026
3 examples
Live preview

Definition and Usage

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.

CSS Syntax

syntax.css
:not( <complex-selector-list>)

Examples: :not(.active), :not(p, .note), input:not([type="submit"]).

Selector Arguments

:not() takes a selector list — not property values. Common argument patterns:

ArgumentMatchesTry it
:not(.class)Elements without that classTry it
:not(.a, .b)Elements that are neither .a nor .bTry it
:not(:last-child)Every sibling except the lastTry it
:not([type="submit"])Elements without that attribute valueTry it
Pseudo-elementsNot allowed inside :not() (::before, ::after, …)Invalid

How to Use :not()

Build a positive match, then exclude the exception.

  1. Choose the elements you mostly want (for example li or input).
  2. Add :not(…) with the class, type, or state to skip.
  3. Prefer a short list: :not(.a, .b) instead of many overrides.
  4. If an argument might be invalid, wrap it: :not(:is(…)).
PieceRequired?Role
Base selectorUsuallyLimits the pool (li, p, input)
:not(…)YesDrops matching exceptions
Selector listOptionalExclude several cases in one go
:is() wrapperOptionalForgiving list when an argument may be unsupported

Live Preview

Pick a class to exclude. Items that match li:not(.…) get the muted style; the excluded class stays highlighted.

Rule shown: li:not(.active)

List
  • Item — active
  • Item — plain
  • Item — done
  • Item — warn
  • Item — plain

Excluding .active · muted items match :not(.active)

CSS Examples

Short snippets that show one clear exclusion. Use Try It Yourself for a live editor.

Example 1 — Exclude a Class

Style every li except .active.

CSS
<style>
  li:not(.active) {
    color: #64748b;
  }
</style>

<ul>
  <li class="active">Active</li>
  <li>Other</li>
  <li>Other</li>
</ul>
Try It Yourself

How It Works

  1. Start with li. All list items are candidates.
  2. Add :not(.active). Items with class active are skipped.
  3. Only non-active items get the muted color.
  4. The active item keeps the default (or its own rule).

Example 2 — Selector List

Exclude two classes in one :not().

CSS
<style>
  li:not(.done, .warn) {
    background: #eff6ff;
  }
</style>

<ul>
  <li>Todo</li>
  <li class="done">Done</li>
  <li class="warn">Warn</li>
</ul>
Try It Yourself

How It Works

  1. Pass a list: :not(.done, .warn).
  2. Same as chaining: :not(.done):not(.warn).
  3. Only “Todo” matches and gets the blue background.
  4. Done and Warn are excluded from that rule.

Example 3 — Not the Last Child

Add dividers under every paragraph except the last.

CSS
<style>
  p:not(:last-child) {
    border-bottom: 1px solid #e2e8f0;
    padding-bottom: 0.75rem;
  }
</style>

<div>
  <p>First</p>
  <p>Second</p>
  <p>Last</p>
</div>
Try It Yourself

How It Works

  1. Combine with a structural pseudo-class: :not(:last-child).
  2. First and second paragraphs match and get a bottom border.
  3. The last paragraph is excluded — no divider under it.
  4. 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.

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 Chrome Supported
Full modern support
Mozilla Firefox Supported
Full modern support
Apple Safari Supported
Full modern support
Microsoft Edge Supported
Full modern support
Opera Supported
Full modern support
Samsung Internet Supported
Full modern support
Internet Explorer 9+
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.

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.

Next: :nth-child()

Learn how to style items by their position among siblings.

:nth-child() tutorial →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

5 people found this page helpful