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?)
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.
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>