CSS Specificity for the :not Pseudo-Class

Understanding how CSS specificity works within the :not() negation pseudo-class is essential for writing predictable and maintainable stylesheets. While the :not() pseudo-class itself contributes no specificity to a selector, it adopts the specificity of the most specific selector present within its argument list. This article explains how CSS calculates specificity for negation lists like :not(.class1, .class2) and details the implications this behavior has on styling rules.

The Most Specific Argument Rule

In CSS Selectors Level 4, functional pseudo-classes such as :not(), :is(), and :has() accept comma-separated selector lists. When determining the specificity of the pseudo-class, the browser does not count the pseudo-class name itself (its base specificity is 0-0-0). Instead, the browser analyzes every selector inside the parentheses and assigns the entire pseudo-class the specificity of the single argument with the highest specificity weight.

Specificity Breakdown of :not(.class1, .class2)

When evaluating :not(.class1, .class2), the calculation proceeds as follows:

  1. Evaluate the first argument: .class1 is a class selector with a specificity of 0-1-0 (0 ID selectors, 1 class/attribute/pseudo-class selector, 0 type/element selectors).
  2. Evaluate the second argument: .class2 is also a class selector with a specificity of 0-1-0.
  3. Determine the highest specificity: Between 0-1-0 and 0-1-0, the highest value is 0-1-0.
  4. Apply to the selector: The entire :not(.class1, .class2) expression receives a specificity of 0-1-0.

If you attach this pseudo-class to an element, such as button:not(.class1, .class2), the element selector (button, which has a weight of 0-0-1) is added to the pseudo-class specificity (0-1-0), resulting in a total specificity of 0-1-1.

Mixed Specificity Within :not()

The calculation becomes particularly important when arguments inside the list carry different weights. Consider the selector :not(.class1, #unique-id):

  • .class1 carries a weight of 0-1-0.
  • #unique-id carries a weight of 1-0-0.

Because #unique-id has the highest specificity in the list, the entire :not(.class1, #unique-id) selector takes on a specificity of 1-0-0. Even if a target element fails to match .class1 and has nothing to do with #unique-id, any rule using this selector will still carry the heavy specificity of an ID selector.

Practical Takeaway

For :not(.class1, .class2), the specificity is always equal to a single class (0-1-0) because both arguments share the same weight. When writing selector lists inside :not(), keep arguments at similar specificity levels to avoid accidentally inflating the selector's weight and creating difficult-to-override rules elsewhere in your CSS.