I'm running tests with CSS and SASS and have a series of selectors that are the same one repeated and nested several times, I want to simplify my code but don't know how to do it (it should be possible, but I can't figure it out).
The main idea is that I have a good number of radio buttons (84, in 42 sets of 2 with the same name) and want to add styles to an element depending if the number of radio buttons checked is odd or even.
To do that, right now I'm using &
in SASS to generate the current selector, and nest it with the sibling selector ~
(order is not important, just the number of checked radios). This is a reduced version of the SASS code (with only 10 groups):
.rd:checked {
& ~ &,
& ~ & ~ & ~ &,
& ~ & ~ & ~ & ~ & ~ &,
& ~ & ~ & ~ & ~ & ~ & ~ & ~ &,
& ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ &,
& ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ &,
& ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ &,
& ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ &,
& ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ &,
& ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & {
~ #oddeven {
background: red; // red if even
}
}
&,
& ~ & ~ &,
& ~ & ~ & ~ & ~ &,
& ~ & ~ & ~ & ~ & ~ & ~ &,
& ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ &,
& ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ &,
& ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ &,
& ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ &,
& ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ &,
& ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & ~ & {
~ #oddeven {
background: green; // green if odd
}
}
}
That generates the following CSS code (I add the HTML so the effect I want is visible):
.rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven {
background: red;
}
.rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven,
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven {
background: green;
}
#oddeven {
color: white;
background: red;
padding: 1em;
position: absolute;
top: 50px;
right: 0;
left: 100px;
}
<input type="radio" class="rd" name="rd0" />
<input type="radio" class="rd" name="rd0" /><br/>
<input type="radio" class="rd" name="rd1" />
<input type="radio" class="rd" name="rd1" /><br/>
<input type="radio" class="rd" name="rd2" />
<input type="radio" class="rd" name="rd2" /><br/>
<input type="radio" class="rd" name="rd3" />
<input type="radio" class="rd" name="rd3" /><br/>
<input type="radio" class="rd" name="rd4" />
<input type="radio" class="rd" name="rd4" /><br/>
<input type="radio" class="rd" name="rd5" />
<input type="radio" class="rd" name="rd5" /><br/>
<input type="radio" class="rd" name="rd6" />
<input type="radio" class="rd" name="rd6" /><br/>
<input type="radio" class="rd" name="rd7" />
<input type="radio" class="rd" name="rd7" /><br/>
<input type="radio" class="rd" name="rd8" />
<input type="radio" class="rd" name="rd8" /><br/>
<input type="radio" class="rd" name="rd9" />
<input type="radio" class="rd" name="rd9" />
<div id="oddeven">
RED background if even number of checked radios (or nothing).<br/>
GREEN background if odd number of checked radios.
</div>
Is there a way to reduce/simplify the SASS code so it is cleaner? The way it is right now, it's easy to make a mistake and it is complicated to add new rules (for new sets of radio buttons).
And alternatively, is it possible to do it in a better way? (maybe using a mixin or actually nesting in some way). Instead of using &
and ~
, I tried using CSS counters, but I didn't find a way to add styles depending on the value of the counter.
NOTE: I don't want to use JavaScript, just HTML and CSS/SASS to generate the rules.
You can generate those & ~ & ...
selectors using for
loop, string interpolation and concatenation:
// function to repeat string:
@function r($string, $times) {
$result: "";
@if $times >= 1 {
@for $i from 1 through $times {
$result: $result + $string;
}
}
@return $result;
}
// generate rules:
@for $n from 1 through 10 {
.rd:checked {
$s: r(' ~ &', $n - 1);
$s: '&' + $s;
@if ($n % 2 == 0) {
#{$s} ~ #oddeven {
background: red;
}
} @elseif ($n % 2 == 1) {
#{$s} ~ #oddeven {
background: green;
}
}
}
}
#oddeven {
color: white;
background: red;
padding: 1em;
position: absolute;
top: 50px;
right: 0;
left: 100px;
}
I know it doesn't produce the exact same CSS as yours, but with some more playing that can be achieved too.
Working demo:
.rd:checked ~ #oddeven {
background: green;
}
.rd:checked ~ .rd:checked ~ #oddeven {
background: red;
}
.rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven {
background: green;
}
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven {
background: red;
}
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven {
background: green;
}
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven {
background: red;
}
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven {
background: green;
}
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven {
background: red;
}
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven {
background: green;
}
.rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ .rd:checked ~ #oddeven {
background: red;
}
#oddeven {
color: white;
background: red;
padding: 1em;
position: absolute;
top: 50px;
right: 0;
left: 100px;
}
<input type="radio" class="rd" name="rd0" />
<input type="radio" class="rd" name="rd0" /><br/>
<input type="radio" class="rd" name="rd1" />
<input type="radio" class="rd" name="rd1" /><br/>
<input type="radio" class="rd" name="rd2" />
<input type="radio" class="rd" name="rd2" /><br/>
<input type="radio" class="rd" name="rd3" />
<input type="radio" class="rd" name="rd3" /><br/>
<input type="radio" class="rd" name="rd4" />
<input type="radio" class="rd" name="rd4" /><br/>
<input type="radio" class="rd" name="rd5" />
<input type="radio" class="rd" name="rd5" /><br/>
<input type="radio" class="rd" name="rd6" />
<input type="radio" class="rd" name="rd6" /><br/>
<input type="radio" class="rd" name="rd7" />
<input type="radio" class="rd" name="rd7" /><br/>
<input type="radio" class="rd" name="rd8" />
<input type="radio" class="rd" name="rd8" /><br/>
<input type="radio" class="rd" name="rd9" />
<input type="radio" class="rd" name="rd9" />
<div id="oddeven">
RED background if even number of checked radios (or nothing).<br/>
GREEN background if odd number of checked radios.
</div>
This is currently not possible in CSS without manually doing it as you have done here.
However, when CSS4 selectors become available, you could use :nth-child()
and :last-child
together.
.rd:last-of-type:nth-child(even of :checked) ~ #oddeven {
background: red;
}
.rd:last-of-type:nth-child(odd of :checked) ~ #oddeven {
background: green;
}
#oddeven {
color: white;
background: red;
padding: 1em;
position: absolute;
top: 50px;
right: 0;
left: 100px;
}
<input type="radio" class="rd" name="rd0" />
<input type="radio" class="rd" name="rd0" /><br/>
<input type="radio" class="rd" name="rd1" />
<input type="radio" class="rd" name="rd1" /><br/>
<input type="radio" class="rd" name="rd2" />
<input type="radio" class="rd" name="rd2" /><br/>
<input type="radio" class="rd" name="rd3" />
<input type="radio" class="rd" name="rd3" /><br/>
<input type="radio" class="rd" name="rd4" />
<input type="radio" class="rd" name="rd4" /><br/>
<input type="radio" class="rd" name="rd5" />
<input type="radio" class="rd" name="rd5" /><br/>
<input type="radio" class="rd" name="rd6" />
<input type="radio" class="rd" name="rd6" /><br/>
<input type="radio" class="rd" name="rd7" />
<input type="radio" class="rd" name="rd7" /><br/>
<input type="radio" class="rd" name="rd8" />
<input type="radio" class="rd" name="rd8" /><br/>
<input type="radio" class="rd" name="rd9" />
<input type="radio" class="rd" name="rd9" />
<div id="oddeven">
RED background if even number of checked radios (or nothing).<br/>
GREEN background if odd number of checked radios.
</div>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With