Pseudo- classes in CSS: Negating Selectors with :not()

The :not() pseudo-class is the opposite of :is() . It returns all elements except for those that match the selector argument. For example, p:not(.message) matches every <p> element that doesn’t have a class of message .

Here’s an example of a form that uses textual input types and radio buttons:

form method=”post” action=”#”>

<h1>Join the Cool Kids Club</h1>


<label for=”name”>Name:</label>

<input type=”text” id=”name” name=”name” required>



<label for=”email”>Email:</label>

<input type=”email” id=”email” name=”email” required>



<legend>Receive a digest?</legend>


<input type=”radio” id=”daily” name=”digest”>

<label for=”daily” class=”label-radio”>Daily</label>

<input type=”radio” id=”weekly” name=”digest”>

<label for=”weekly” class=”label-radio”>Weekly</label>



<button type=”submit”>Buy Tickets!</button>


In the HTML, labels associated with a radio type have a .Label-radio class. We can use :not() to target those elements without a .label-radio class:

label:not( .label-radio ) {

font-weight: bold;

display: block;


The end result is shown below.

Here’s a slightly trickier example. Let’s create styles for textual inputs. These include input types such as number, email, and text along with password and URL. Let’s do this by excluding radio button, checkbox, and range inputs:

input:not( [type=radio], [type=checkbox], [type=range] ) {


As with :is() , the :not() pseudo-class accepts either a single selector or a selector list as an argument. It will match any and all of the supported selectors in the list.

Chrome and Edge versions 87 and below, and Firefox versions 83 and below, implement an earlier definition of :not() that doesn’t accept selector lists. Instead, those browsers accept a single selector argument. For those browsers, we’ll need a different approach.

Your instinct might be to rewrite the preceding example like so:

input:not( [type=radio] ),

input:not( [type=checkbox] ),

input:not( [type=range] ) {


Unfortunately, this won’t work. Each selector overrides the previous one. It’s the equivalent of typing:

input:not( [type=radio] ){ … }
input:not( [type=checkbox] ) { … }
input:not( [type=range] ) { … }

Instead, you’ll need to use the following selector:

input:not( [type=radio] ):not( [type=checkbox] ):not( [type=range] ) {


Each instance of :not() in this selector further filters the list, achieving our desired result.

Pseudo-elements aren’t valid arguments for :is() and :not() . A selector such as :is(::first-Letter) or :is(::marker, ::-webkit-detaiLs-marker) won’t match any elements, and browsers will ignore the rules associated with that selector.

Source: Brown Tiffany B (2021), CSS , SitePoint; 3rd edition.

Leave a Reply

Your email address will not be published. Required fields are marked *