Does CSS ever care about DOM "closeness" relationships?

115 views Asked by At

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?

2

There are 2 answers

6
BoltClock On BEST ANSWER

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.

0
admcfajn On

If you re-order the declarations, you'll see the text-color change. Later definitions override sooner ones.

You can use the > operator to restrict your selection somewhat #main > span will match only spans immediately beneath #main, the + selector selects the next-sibling in the dom. & you've got nth-child in/around that type of selection. Is that what you meant by:

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?

div, span {
  padding: 10px;
  display: block;
}

.light-background {
  background-color: #cacaca;
}

.dark-background {
  background-color: #333;
}

.light-background span {
  color: black;
}

.dark-background span {
  color: white;
}
<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>