2

I have the following HTML & CSS:

<div class="p-12 border both-have">
  <div class="p-12 border both-have my-inner">
@import "tailwindcss";

.both-have, .foo.bar {
  & .my-inner {
    background-color: red;
  }
}

.both-have {
  & .my-inner {
    background-color: aqua;
  }
}

I can't see how the background-color isn't set to aqua, but when inspecting in the browser, the specificity is (0,2,1) for the applied CSS. However, the .foo.bar class doesn't match anything here. Why would non-matching CSS still contribute to the specificity of a selector? Is this normal?

I know I can "fix" this issue in a number of different ways, more just curious about this seemingly odd behavior.


Note: only using Tailwind since they have a nice playground that doesn't require logging in & it was quick to apply some padding so i could showcase the background-color)

It might not last forever, but here is a tailwind playground link: https://play.tailwindcss.com/wZUsI94YPg?file=css, try removing the .bar from the first selector and see how it affects the background-color being applied.


Tried to investigate a bit, maybe it's part of the CSS spec, but it didn't make much sense, i read https://www.w3.org/TR/selectors-4/#specificity-rules, and it states the following:

If the selector is a selector list, this number is calculated for each selector in the list. For a given matching process against the list, the specificity in effect is that of the most specific selector in the list that matches.

This lead me to think that it's not intended behaviour since the selector .foo.bar doesn't match? (maybe it's different for when a selector list has nesting inside it?)

3
  • 2
    You overcomplicate programming issues. The browser does not check if every CSS rule is applied or if selectors exist in HTML. For a simple reason: Simplicity. It would take to much time and computing resources to do so. It simplifies the task and calculates the specificity weight while reading the CSS file. Nothing more. The next thing is, CSS selectors are read backwards by the browser. In your case it looks for every element that has the class my-inner, then it checks if this element also has the class both-have or foo and bar. It does not check Commented Jun 5 at 15:08
  • If it matches, then it applies the rule with the calculated specificity weight. It does not actively check which parts of the selectro rules apply and does not recalculate the specificity weight because of that. Why should it? Programming methods are supposed to be resource-efficient and fast. They are not for fixing the overcomplications of the programmers. Commented Jun 5 at 15:09
  • @tacoshy Your comments are generalizations that are not accurate according to the specifications. They really aren't helpful in this context. There are very detailed rules for specificity calculations and cascade order. Commented Jun 5 at 17:10

1 Answer 1

1

This is because of how specificity is calculated with nesting:

When creating complex selector lists with CSS nesting this behaves in exactly the same way as the :is() pseudo-class.

Specifically, the CSS Nesting module section on the nesting selector states that:

The specificity of the nesting selector is equal to the largest specificity among the complex selectors in the parent style rule’s selector list (identical to the behavior of :is()), or zero if no such selector list exists.

and there's a detailed explanation entitled "Why is the specificity different than non-nested rules?" under the same section heading.

You can read more about the :is() specificity calculation in the Selectors Level 4 specification in the Specificity section.

Examples:

With nesting:

div {
  height: 1rem;
}


.has-both, .fake.class {
  & .my-inner-1 {
    background: red;
  }
}

.has-both {
  & .my-inner-1 {
    background: aqua;
  }
}
<div class="has-both">
  <div class="has-both my-inner-1"></div>
</div>

Without nesting:

div {
  height: 1rem;
}


.has-both .my-inner-1, 
.fake.class .my-inner-1 {
    background: red;
  
}

.has-both .my-inner-1 {
    background: aqua;
}
<div class="has-both">
  <div class="has-both my-inner-1"></div>
</div>

Sign up to request clarification or add additional context in comments.

2 Comments

Huh. I don't quite follow the explanation here, but this seems like a clear bug in terms of how authors should expect their CSS to work. It makes absolutely zero sense to expect a selector to apply to an element that does not match it.
there are some performance issue that led to this decision. It's counter-intuitive but well documented

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.