Given the following code:
div, span {
padding: 10px;
display: block;
}
.light-background {
background-color: #cacaca;
}
.dark-background {
background-color: #333;
}
.dark-background span {
color: white;
}
.light-background span {
color: black;
}
<div class="light-background">
<div class="dark-background">
<span>Here is some light text on a dark background.</span>
<div class="light-background">
<span>Here is some dark text on a light background.</span>
</div>
</div>
<span>Here is some dark text on a light background.</span>
</div>
The inner-most span matches both .dark-background span
as well as .light-background span
, so there seems to only ever be a relationship to the CSS cascade weight and the last-rule-defined cascade, and never how close the two selectors in a rule are to each other in the HTML.
Is it possible to apply a rule if the elements matched by the selector are closer to each other than other rules which may match?
Before addressing your question, which is a high-level question about selector matching in general, let's get your actual problem out of the way. All you're really trying to do is style each span
based on whether its parent is a .light-background
or a .dark-background
, and the solution for the problem in your CSS is simply to replace the descendant combinator with the child combinator:
.dark-background > span {
color: white;
}
.light-background > span {
color: black;
}
div, span {
padding: 10px;
display: block;
}
.light-background {
background-color: #cacaca;
}
.dark-background {
background-color: #333;
}
.dark-background > span {
color: white;
}
.light-background > span {
color: black;
}
<div class="light-background">
<div class="dark-background">
<span>Here is some light text on a dark background.</span>
<div class="light-background">
<span>Here is some dark text on a light background.</span>
</div>
</div>
<span>Here is some dark text on a light background.</span>
</div>
With that out of the way, why does your approach with descendant selectors not work as expected to begin with? That's where we turn to your question:
Does CSS ever care about DOM “closeness” relationships?
No, complex selectors matching the same element are compared only by specificity. And specificity does not take into account the proximity of the elements matched by each compound selector, because this requires information about the DOM, and specificity is never calculated based on any information about the DOM. Likewise, combinators themselves do not contribute to specificity.
Given the following example:
<div class="A">
<div class="B">
<div class="C"></div>
<div class="D"></div>
<div class="E"></div>
</div>
</div>
In each of these pairs, both selectors match the same element and are equally specific; therefore the second rule will always take precedence over the first:
.B .C {}
.B > .C {}
.B > .C {}
.B .C {}
.A .C {}
.B .C {}
.B .C {}
.A .C {}
.D ~ .E {}
.D + .E {}
.D + .E {}
.D ~ .E {}
.C ~ .E {}
.D ~ .E {}
.D ~ .E {}
.C ~ .E {}
Is it possible to apply a rule if the elements matched by the selector are closer to each other than other rules which may match?
No, this is currently not possible. css-values-3 has a proposed feature called toggle()
that will aid in solving problems that are somewhat similar to yours but not quite the same. But there hasn't been any interest in implementing it for the last several years, so it's been punted to level 4, and I don't expect implementations to surface for at least the next 5 years.