How can I generate a style for parent and a number of descendants in SCSS?

Viewed 32

I want to generate this kind of CSS, where a style is applied to an element and several of its descendants, using SCSS

.a, 
.a .b, 
.a .b .c,
.a .b .c .d { /* Styles */ }

In this related question How to apply a style to both parent and child in SCSS I learned that I could achieve something similiar using &. I've tried this:

.a {
  &, .b {
    &, .c {
      &, .d {
        color: blue;
      }
    }
  }
}

which resulted in the following css:

.a, 
.a .d, /* Not wanted */
.a .c, /* Not wanted */
.a .c .d, /* Not wanted */
.a .b, 
.a .b .d, /* Not wanted */
.a .b .c, 
.a .b .c .d {
  color: blue;
}

I understand that this happens, because & will include the whole previous selection, which always includes .a (or .a .b on the next stage).

However, I'm not sure how to fix this.

Is it possible to get the result I want using SCSS?


Please note that I'm mainly asking out of curiosity and that I'm not very familiar with some aspects of SCSS. Mabye functions, interpolation or mixins could be used?

Retrictions

  • Selectors could be much longer and much more complex then in the example above. I would like to repeat them only as often as necessary.
  • There could be many more styles I want to apply, not just one.

Discarded solutions

Some obvious, but rather bad solutions I could think of

  • hard-coding it exactly as I would do in plain CSS. Well the point of question is how to avoid exactly this ;)

  • repeating the style on each level. Even with variables this would be more repetition then hard-coding it. I mean something like this:

    .a {
      color: blue;
      .b {
        color: blue;
        .c { ...
    
  • using color: inherit. In this case I would still need to repeat the inherit.

2 Answers

I don't know if I understood it correctly, but if b, c, d,... are all children of a I think you might use child combinator, doing something like:

.a > .b > .c > .d { 
   color :  blue
}

Let me know if that helped.

I think the sass code below can make the selectors you want. Notice that color property is an Inherited property and is not good for testing in this case, so I used another property to test my code for this example. Also notice that .b, .c and so on in the $selectorMain variable must have a space before to work correctly:

@use "sass:list";
@use "sass:string";
$selectorMain: (".a", " .b", " .c", " .d");
$nextLevel: list.nth($selectorMain, 1);
$finalList: ();
@for $i from 1 through list.length($selectorMain) {
  $currentElement: list.nth($selectorMain, $i);
  @if $i >= 2 {
    $nextLevel: string.insert($nextLevel, $currentElement, -1);
  }
  $finalList: list.append($finalList, $nextLevel);
}
@debug $finalList;
@each $selector in $finalList {
  #{$selector} {
    border-top: 1px solid blue;
  }
}

This is the html code that I used to test my sass result:

<div>
  <div class="a">
    <p>div with "a" class</p>
    <div class="b">
      <p>div with "b" class</p>
      <div class="c">
        <p>div with "c" class</p>
        <div class="d">
          div with "d" class
        </div>
      </div>
    </div>
  </div>

  <div class="a">
    <p>div with "a" class</p>
      <div class="c">
        <p>div with "c" class</p>
        <div class="d">
          div with "d" class
        </div>
      </div>
  </div>
</div>

Related