Describing what should be targeted is often simpler than describing everything that should be excluded. The :not() pseudo-class flips that logic: instead of listing elements to style, it starts from a broad group and removes one precise exception.
It is a quiet but remarkably practical tool whenever a rule needs to apply almost everywhere, with one exception.
Definition
The :not() pseudo-class targets any element that does NOT match the selector passed inside the parentheses. It is used like any other pseudo-class, attached to a selector or used on its own.
A practical example
/* every button except the one with the .active class */
.button:not(.active) {
opacity: 0.7;
}
/* every paragraph except the last one in the list */
p:not(:last-child) {
margin-bottom: 16px;
}
/* every field except the disabled ones */
input:not([disabled]) {
cursor: text;
}The first example illustrates the most classic case: instead of styling each non active button one by one, :not(.active) starts from the whole group of buttons and removes the ones carrying the active class. This is particularly useful to visually mark every item in a list except the last one, without adding an extra class just for that one exception.
Accepting a list in recent browsers
Recent versions of CSS allow several selectors to be passed to :not(), separated by commas, which avoids chaining several :not() calls in a row to exclude multiple cases at once.
/* every link except the active OR disabled ones */
a:not(.active, .disabled) {
color: #2d6cdf;
}Before this addition, the equivalent had to be written as a:not(.active):not(.disabled), a chain of two :not() calls that produces the exact same result but stays heavier to read.
| Goal | Selector |
|---|---|
| Every button except the last | .button:not(:last-child) |
| Only external links | a:not([href^="/"]) |
| Every enabled field | input:not([disabled]) |
:not() matches that of its argument, exactly like :is(). A :not() that contains an id therefore inflates the whole rule, a point that is often overlooked when working with this selector.Using :not() to simplify a long list of exceptions
Without :not(), excluding several elements from a rule often forces a roundabout negative CSS selector, for example adding an extra class to every relevant element just to later target the ones that lack it. :not() avoids that detour by expressing the exception directly inside the selector, which keeps the HTML cleaner and the stylesheet shorter.
/* every card except the featured one */
.card:not(.featured) {
opacity: 0.85;
}
/* every nav link except the current one */
.nav-link:not([aria-current="page"]) {
color: #666;
}The second example relies on an accessibility attribute already present in a well built menu, instead of adding a redundant .active class. That is a good habit: before adding a class solely to exclude an element with :not(), check whether the HTML already carries that information as an attribute. Like :is() and :where(), :not() inherits the Specificity of its argument, a point worth keeping in mind to avoid surprises during a later override.
Frequently asked questions
:not() be nested inside another :not()?Yes, the specification allows it, even though that kind of nesting quickly makes the selector hard to read. In practice, it is often better to rephrase the logic differently when this happens.
:not() target a pseudo-element like ::before?No, :not() only accepts simple and compound selectors, not pseudo-elements, since pseudo-elements do not represent an element that could exist or not in the HTML.
:not() and an imaginary plain negative selector?CSS has never had a negative syntax outside of :not(). That is exactly what makes it unique: without it, every exception would always have to be targeted positively, one by one.