A few years back, Stephan Judis wrote about CSS’s :not pseduo-class as it started to gain widespread browser support. Fast forward to 2025, and it’s widely available, but so rarely used.
In this article, we’re going to dig into the :not pseudo class, create a simple pricing component with it with a nice hover effect, and see if it’s something we could see ourselves using more in future.
What is the :not() pseudo-class?
The :not()
pseudo-class in CSS allows you to apply styles to elements that don’t match a given selector. Let’s imagine you want to target elements in a group, but exclude certain ones too. This is where the :not()
selector comes in.
Hover effects are generally very simple, right? Add :hover
pseudo-class, and style away. But what about if you wanted to style other elements when you hover over one in particular?
The Syntax
The :not()
pseudo-class takes in a comma-separated selector list, and it will match any elements that do not contain the classes passed to it.
Let’s take a super simple example from the MDN docs, and imagine we had multiple paragraph elements in an HTML file:
<p>I am a paragraph.</p>
<p class="fancy">I am so very fancy!</p>
In our CSS, we can simply target any elements without the fancy
class using:
p:not(.fancy) {
color: green;
}
Is It Easy to Use?
The :not()
pseudo class is deceptively complex. It’s useful - yes - but in real world use cases it can be trickier to use than it might seem. The MDN docs page itself mentions the issues that exist with the selector, and the issues developers are likely to come across when using it.
However, as the example below shows - you can create some interest effects and gain more control over the elements of your UI if you can get to grips with the selector.
A Real World Example
In this example, we’re going to build a pricing table, and add some hover effects so that when we hover over one pricing block, the others dim and scale down, while the hovered element does the opposite.
Get the Code
We’ve made this pricing table with CSS and Tailwind, and you can get the code for whichever one you prefer.
How it Works
First of all, we’re going to focus on the CSS-only approach, but we’ll mention Tailwind later on so we can compare the two.
The general benefit of using :not()
is that instead of manually adding extra classes to each box, we can use :not()
to apply styles to every .box
except the one that is being hovered.
With pure CSS, the syntax is pretty straight-forward and it’s relatively readable, too.
We’ve essentially got a .box-wrapper
with three .box
elements inside.
When any box
is hovered over, it has a couple of simple hover styles, like normal:
.box:hover {
opacity: 1;
transform: scale(1.03);
background-color: #4f46e5;
}
The hovered item will have 100% opacity, be scaled-up, and have a purple background.
Then, we can use the :not()
pseudo-class along with the :has()
pseudo class (it essentially works the same in reverse), to say that when the box-wrapper has a child that is being hovered over, style any box not hovered like so…
.box-wrapper:has(.box:hover) .box:not(:hover) {
opacity: 0.3;
transform: scale(0.98);
}
It might look complex at first, but read it back and it really is quite powerful in that you can achieve complex styling with one line of CSS.
It really is that simple! In CSS, at least…
The Role of Tailwind
Tailwind has become hugely popular over the last few years, and up until the recent release of Tailwind 4.0, it was notoriously tricky to use when working with pseudo classes, and some of the finer intricacies of CSS.
While Tailwind 4.0 is great, and its far more supportive of pseudo classes and arbitrary values, it was tricky to get the same result as the CSS approach.
Not only is the Tailwind version longer, and less readable, but it feels… hacky:
[&:has(:hover)~.group:not(:hover)]:opacity-30 [&:has(~.group:hover):not(:hover)]:opacity-30 [&:has(:hover)~.group:not(:hover)]:scale-[0.98] [&:has(~.group:hover):not(:hover)]:scale-[0.98]
Perhaps this has, in some way, contributed to the lack of excitement around :not()
. At the same time as features like this were introduced to the web, frameworks like Tailwind - which generally didn’t support them very well - grew in popularity. In essence, it helped new developers learn core CSS concepts and features, but probably left them without knowing ones like this existed at all.
Conclusion
In conclusion, the :not()
pseudo-class is a powerful yet underused tool in CSS that can help streamline your styles by allowing you to target elements that don’t match specific criteria. Whether you’re designing a pricing table with hover effects or just trying to simplify your code, the :not()
pseudo-class provides a cleaner alternative to manually adding extra classes.
Although it can be a bit tricky to work with at first, especially when combined with other pseudo-classes like :has()
, it can lead to more flexible and efficient CSS. Additionally, when working with frameworks like Tailwind, while it may require a bit more effort, it’s still worth experimenting with to push the boundaries of what’s possible in CSS.
So, as you explore the capabilities of :not()
, don’t hesitate to get creative and see how it can make your styling workflow more efficient. Who knows, it might just become your new go-to tool for simplifying complex UI effects.