Reputation: 2056
I want to target all descendant paragraphs of a certain class
while ignoring all descendant paragraphs of another class
inside the first (this should work no matter which class is inside which). To achieve this I had to use 4 selectors, like this:
* {
margin: 0.2em 0;
width: fit-content;
}
div {
margin-left: 1em
}
/* == 4 selectors to achieve desired effect = */
.orange p {
background: orange;
}
.cyan .orange p {
background: orange;
}
.cyan p {
background: cyan;
}
.orange .cyan p {
background: cyan;
}
<div class="orange">
<p>Orange</p>
<div>
<p>Orange</p>
<div>
<p>Orange</p>
<div class="cyan">
<p>Cyan</p>
<div>
<p>Cyan</p>
</div>
</div>
</div>
</div>
</div>
<div class="cyan">
<p>Cyan</p>
<div>
<p>Cyan</p>
<div>
<p>Cyan</p>
<div class="orange">
<p>Orange</p>
<div>
<p>Orange</p>
</div>
</div>
</div>
</div>
</div>
The question is: Can this be achieved using only two selectors? [The order of these two selectors should be able to change without altering the effect.]
I have tried selectors like:
.orange:not(.cyan) p {
background: orange;
}
.cyan:not(.orange) p {
background: cyan;
}
but it doesn't target the last one well, for it is inheriting the style of the first. I am looking for two selectors that match these cases without any particular order in the style sheet.
Upvotes: 5
Views: 223
Reputation: 272648
Here is a simple solution with CSS variables. Check the following question for more details: CSS scoped custom property ignored when used to calculate variable in outer scope
* {
margin: 0.2em 0;
width: fit-content;
}
div {
margin-left: 1em
}
p {
background: var(--c);
}
.cyan {
--c:cyan;
}
.orange {
--c:orange;
}
<div class="orange">
<p>Orange</p>
<div>
<p>Orange</p>
<div>
<p>Orange</p>
<div class="cyan">
<p>Cyan</p>
<div>
<p>Cyan</p>
</div>
</div>
</div>
</div>
</div>
<div class="cyan">
<p>Cyan</p>
<div>
<p>Cyan</p>
<div>
<p>Cyan</p>
<div class="orange">
<p>Orange</p>
<div>
<p>Orange</p>
</div>
</div>
</div>
</div>
</div>
You can scale it to any number of coloration as you only need one selector per color and the order doesn't matter:
* {
margin: 0.2em 0;
width: fit-content;
}
div {
margin-left: 1em
}
p {
background: var(--c);
}
.cyan {
--c:cyan;
}
.orange {
--c:orange;
}
.blue {
--c:lightblue;
}
<div class="orange">
<p>Orange</p>
<div>
<p>Orange</p>
<div>
<p>Orange</p>
<div class="cyan">
<p>Cyan</p>
<div class="blue">
<p>Blue</p>
</div>
</div>
</div>
</div>
</div>
<div class="cyan">
<p>Cyan</p>
<div class="blue">
<p>Blue</p>
<div>
<p>Blue</p>
<div class="orange">
<p>Orange</p>
<div>
<p>Orange</p>
</div>
</div>
</div>
</div>
</div>
Upvotes: 2
Reputation: 16069
You cannot achieve what you want, because this is not how CSS works. Both of your statement will have the same specificity, so CSS determines which rule will win according to the order in the CSS file. For the inner styles, you will need to have a statement which has a greater specificity. This can either be achieved with listing all combinations of classes or by e.g. using the child-selector (>
).
I thought, I'd share an improvement to your solution. It still uses 4 CSS statements (for 2 colors) but it does not require you to write down all possible combinations (in case of more than 2 classes, it is less effort; see the example below).
First, you colorize any child <p>
of a cyan element with the color cyan
. Then, you overwrite this behavior with the child-selector which targets only direct children of your element. .orange > p
then overwrites .cyan p
. The same goes with orange/cyan.
* { font-family: sans-serif; }
.cyan p {
background: cyan;
}
.orange p {
background: orange;
}
.red p {
background: red;
}
.cyan > p {
background: cyan;
}
.orange > p {
background: orange;
}
.red > p {
background: red;
}
<ul>
<li class="orange">
<ul>
<li class="cyan"><p>.orange >> .cyan</p></li>
<li class="red"><p>.orange >> red</p></li>
<li><p>.orange >> –</p></li>
</ul>
</li>
<li class="cyan">
<ul>
<li class="orange"><p>.cyan >> .orange</p></li>
<li class="red"><p>.cyan >> .red</p></li>
<li><p>.cyan >> –</p></li>
</ul>
</li>
<li class="red">
<ul>
<li class="orange"><p>red >> orange</p></li>
<li class="cyan"><p>red >> cyan</p></li>
<li><p>.red >> –</p></li>
</ul>
</li>
</ul>
Upvotes: 0