SASS : &記法による記述をネストを再現しつつ @extend する

月花です。

今日はSASSの備忘録的なTipsです。

SASSでは、 &(アンパサンド, アンド)によって親セレクタを参照することができます。
この親というのは、事実上の親ではなく、あくまでセレクタ記法としての親なので、たとえば .product というセレクタを .product-image の親にすることもできます。
これは別にDOM上の親子でなくともよいのがSASSの強みですが、こうやってこねくり回していると、じゃあこれを @extend で継承し、フォークしたくなったらどうすればよいでしょうか。

<div class="product">
    <div class="product-image"><img src="xxxxxx"></div>
    <div class="product-text">xxxxxxxxx</div>
</div>

こういうHTMLと、

.product{
    display:block;
    &-image{
        float:left;
    }
    &-text{
        float:right;
    }
}

こういうSASSがあります。

新しく

<div class="article">
    <div class="article-image"><img src="xxxxxx"></div>
    <div class="article-text">xxxxxxxxx</div>
</div>

こういうHTMLができました。

基礎のレイアウトは同じなので、これに当てるSASSは @extend を使ってそこから拡張していきたい、というときに

.product{
    display:block;
    &-image{            // ←ここと
        float:left;
    }
    &-text{             // ←ここってどうやって @extend するの?
        float:right;
    }
}

// だって

.article{
    @extend .product;
    &-image{
        @extend &-image;          // ←ここの &.article-image だから、
                                  //.article-image-imageになってしまう
    }
    &-text{
        @extend .product-text;    // ←これなら通りそうだけど、全部に書くの?
    }
}

という問題が発生しました。

ちなみに、

.article{
    $parent : '.product';
}

単純にこうでいいのではというのは誤りで、コンパイルすると

.product, .article {
  display: block; }
  .product-image {
    float: left; }
  .product-text {
    float: right; }

こうなります。

&-text{} というような書き方では、ネスト上では親でもコンパイル後には兄弟になってしまうのです。
自分自身である & を使ってわかりやすく冗長に書きなおすと、

.product{
    &{
        display:block;
     }
    &-text{
        float:right;
    }
}

これと等価なので、一見子に見えても実は兄弟を指定しているためです。

Tipsなのでさくさくいきましょう。

変数を使って親の名前を保持しておく

.product{                  // ←これを覚えると
    display:block;
    &-image{               // ←ここで使える
        float:left;
    }
    &-text{
        float:right;
    }
}

.article{
    $parent : '.product';            // ← 親を覚えて
    @extend #{$parent};
    
    &-image{
        @extend #{$parent}-image;    // ← ここで展開
        width:80%;
    }
    &-text{
        @extend #{$parent}-text;
        color:black;
    }
}

コンパイル後、

.product, .article {
  display: block; }
  .product-image, .article-image {
    float: left; }
  .product-text, .article-text {
    float: right; }

.article-image {
  width: 80%; }
.article-text {
  color: black; }

と、正しく @extend できた。

りくつ

SASS では、 #{$variable} と書くことで、文字列として展開することができる。
だから、親を変数に収めて、あとは書き足すだけ。
ちなみに、変数にドットを含めず、ドットを前に出すこともできる。

.article{
    $parent : 'product';
    @extend .#{$parent};
    
    &-image{
        @extend .#{$parent}-image;
    }
    &-text{
        @extend .#{$parent}-text;
    }
}

もう一段ネストしたら?

もう一段深くなって

.product{
    display:block;
    &-text{
        float:right;
        &-title{
            font-weight:bold;
        }
        &-description{
            font-size:12px;
        }
    }
}

こう来たら、単純に

.article{
    $parent : '.product';                // ←親を覚える
    @extend #{$parent};
    &-text{
        $child : #{$parent}-text;        // ←子を覚える
        @extend #{$child};
        &-title{
            @extend #{$child}-title;     // ←子を使う
        }
        &-description{
            @extend #{$child}-description;
        }
    }
}

コンパイル後、

.product, .article {
  display: block; }
  .product-text, .article-text {
    float: right; }
    .product-text-title, .article-text-title {
      font-weight: bold; }
    .product-text-description, .article-text-description {
      font-size: 12px; }

.article-text-title {
  color: red; }
.article-text-description {
  color: black; }

以上です。