1

I have an issue where I'm working in a rather large CSS code base that makes frequent use of overriding previously defined classes/selectors. As such, it is very sensitive to the order in which they are defined.

Here is a sample of how I need it to work

.grid {
   padding:25px;
   background-color: red;
}

.grid {
  padding:50px;
  background-color: green;
}
<li>
   <div class="grid">
     Test
   </div>
</li>

Notice how the second .grid definition overrides the first.

This is what is happening now:

.grid:not(.ui) {
   padding:25px;
   background-color: red; 
}

.grid {
  padding:50px;
  background-color: green;
}
<li>
   <div class="grid">
     Test
   </div>
</li>

Using the :not pseudo-class hover moves the evaluated priority to after normal class definitions. I need it to be evaluated in the same order as before, but I need the :not selector. Are there any solutions besides refactoring?

TylerH
  • 20,799
  • 66
  • 75
  • 101
Chase
  • 2,206
  • 1
  • 13
  • 17

3 Answers3

4

The :not rule is more specific, so it takes higher precedence.

If you can't refactor, you could put a bogus :not condition on the other rule as well, so they'll have the same precedence and thus revert to document order:

.grid:not(.ui) {
   padding:25px;
   background-color: red; 
}

.grid:not(.nonexistentclassname) {
  padding:50px;
  background-color: green;
}
<li>
   <div class="grid">
     Test
   </div>
</li>
Daniel Beck
  • 20,653
  • 5
  • 38
  • 53
1

In your first example the .grid selectors each have a specificity value of 10 (classes = 10). Therefore, since both rules have the same specificity, their source order decides.

In your second rule, .grid:not(.ui) has a specificity value of 20 (2 classses; the :not() pseudo-class has no specificity value). The source order is subordinate because the rules have different specificity values.

So, to achieve your goal (the same behavior as before but with :not() applied to the first rule), you need to boost the specificity of the second rule by at least 10.

One method would be to add a useless :not() to the second rule. This method is described in another answer and is allowed by the spec:

6.6.7. The negation pseudo-class

Note: the :not() pseudo allows useless selectors to be written. For instance :not(*|*), which represents no element at all, or foo:not(bar), which is equivalent to foo but with a higher specificity.

.grid:not(.ui) {
   padding:25px;
   background-color: red; 
}

.grid:not(.bar) {
  padding:50px;
  background-color: green;
}
<div class="grid">Test</div>

specificity calculator

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • The specificity of :not(.ui) is defined not by the :not() part but the .ui part. The :not() itself does not contribute to specificity. So the specificity of .grid:not(.ui) is that of 2 class selectors, versus the 1 in .grid. – BoltClock Mar 08 '18 at 03:47
  • Thanks for this clarification. Answer revised. I was under the impression that `:not()`, being a pseudo-class, had a specificity value of "10", like classes and other attributes (except IDs). – Michael Benjamin Mar 20 '18 at 15:27
  • I was also under this impression because I could not find any official reference to this `:not()` exception. I certainly see the exception mentioned across the web on various developer sites, but nothing from the W3C. I looked [**here**](https://www.w3.org/TR/CSS22/cascade.html#specificity) and [**here**](https://www.w3.org/TR/selectors-3/#negation). I must be overlooking something. Maybe you can point me in the right direction. Thanks. @BoltClock – Michael Benjamin Mar 20 '18 at 15:29
  • :not() is new to Selectors 3. As with CSS2, Selectors has its own section on calculating specificity - the specificity of :not() is [defined there](https://www.w3.org/TR/selectors-3/#specificity), as is that of :matches() in Selectors 4. – BoltClock Mar 20 '18 at 15:31
  • Ah. I was looking at Selectors 2.2. There it is clearly in Selectors 3. Thank you! (The Selectors 2.2 spec is referenced in a big red box across all CSS 2.1 spec pages, which have the highest ranking in the SERPs, and it looks so clean and new, that it threw me off.) – Michael Benjamin Mar 20 '18 at 15:42
  • This! It's everywhere on the web. I need to remember to go to Selectors 3 and stop falling for this trap :-) https://www.w3.org/TR/CSS21/selector.html%23id-selectors @BoltClock – Michael Benjamin Mar 20 '18 at 15:44
1

You just need to make the selector you want to take precedence be more specific than the other one. If you add a "dummy" class to the element, you can add that class to your second selector to make it more specific (or at least make a tie where then, the last selector wins).

CSS Specificity is calculated as follows:

1000 points for an inline style 100 points for an id in the selector 10 points for a class or pseudo-class in the selector 1 point for an element or pseudo-element in the selector

In your case:

.grid:not(.ui)

Is worth 20 points because the selector has 1 class and one pseudo-classes in it.

But:

.grid

is only worth 10 points because of the one class.

/* This selector is worth 20 points */
.grid:not(.ui) {
   padding:25px;
   background-color: red; 
}

/* This selector is also worth 20 points, but becomes it comes 
   after the other one, the location breaks the tie. */
.grid.special {
  padding:50px;
  background-color: green;
}
<li>
   <!-- Adding another "dummy" class to the element allows you
        to correctly find it with your CSS, and do it with a more
        specific selector, if needed. -->
   <div class="grid special">
     Test
   </div>
</li>

And, what if you need (for some reason) have the order of the selectors reversed? Just make the one that is suppose to "win" a little more specific:

/* This selector is worth 21 points */
div.grid.special {
  padding:50px;
  background-color: green;
}

/* This selector is worth 20 points */
.grid:not(.ui) {
   padding:25px;
   background-color: red; 
}
<li>
   <!-- Adding another "dummy" class to the element allows you
        to correctly find it with your CSS, and do it with a more
        specific selector, if needed. -->
   <div class="grid special">
     Test
   </div>
</li>

Here's a great site for understanding how specificity is calculated that let's you "play" with selectors.

Scott Marcus
  • 64,069
  • 6
  • 49
  • 71