かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Sass (SCSS) BEMで書く時に @at-root は必要か?

CSS を Sass (SCSS) + BEM ( Block-Element-Modifier ) で書いているプロジェクトに参加してて、BEMに合うようにするために @at-root を使ってくださいと言われ、 @at-root というものを知らなかったので調べたメモ。

結論としては現在では Sass で BEM な CSSセレクタを作るために @at-root は必要なさそうです。 ( Ruby Sass v.3.5.6 で確認 )

超ざっくりした BEM 概要

Block__Element--Modifier のフォーマットでクラスを書く。
状態ごとに別のクラス名になるから基本的にシングルクラスになる。

@at-root

@at-root はネストを解除する

.foo {
  @at-root {
    .bar {
      color: #111;
      @at-root {
        .baz {
          color: #222;
        }
      }
      .qux {
        color: #333;
      }
    }
  }
  .quux {
    color: #444;
    @at-root {
      .corge {
        color: #555;
      }
    }
  }
}

コンパイル

.bar {
  color: #111;
}
.baz {
  color: #222;
}
.bar .qux {
  color: #333;
}
.foo .quux {
  color: #444;
}
.corge {
  color: #555;
}

@at-root 内に書かれているものは、ネストがいくつ重なっていても無視してrootに配置されます。 (正直使いみちが謎…)

Sass (SCSS) で BEM なセレクタを書く

& で文字列を結合してクラス名にする時は @at-root は不要

.block {
  color: red;
  &__element {
    color: blue;
    &--modifier {
      color: yellow;
    }
  }
}
.block {
  color: red;
}
.block__element {
  color: blue;
}
.block__element--modifier {
  color: yellow;
}

@at-root があっても BEM 的なセレクタにはなる

.block {
  color: red;
  @at-root {
    &__element {
      color: blue;
      @at-root {
        &--modifier {
          color: yellow;
        }
      }
    }
  }
}

コンパイル

.block {
  color: red;
}
.block__element {
  color: blue;
}
.block__element--modifier {
  color: yellow;
}

歴史的経緯

ref. Sass 3.3で追加された「&」の新機能と@at-rootまとめ解説 | HTML5Experts.jp

@at-root は Sass 3.3 で導入された。
Sass 3.2 の頃ネストさせて BEM 的なセレクタを作ろうとしてもエラーになっていた。

.block {
 &--modifier { ... } 
}
// Sass 3.2 ではエラーになっていた

当時セレクタに文字列結合させるには #{&} を使っていたが、この記法はネストさせると親セレクタが二重に出力されてしまう問題があった。

.block {
  #{&}__element {
    color: #333;
    #{&}--modifier {
      color: #444;
    }
  }
}

コンパイル

.block .block__element {
  color: #333;
}

.block .block__element .block .block__element--modifier {
  color: #444;
}

※ 新しいSass Ruby Sass v.3.5.6 でも #{&} で書くと同じように出力されます。

ネストしている親を出力しない @at-root を使うと、上記の問題が解消された。

.block {
  @at-root {
    #{&}__element {
      color: #333;
      @at-root {
         #{&}--modifier {
          color: #444;
        }
      }
    }
  }
}

コンパイル

.block__element {
  color: #333;
}

.block__element--modifier {
  color: #444;
}

どのバージョンからかは不明だが & で直接セレクタの文字列結合ができるようになっているので、新しいSass (Ruby Sass v.3.5.6 で確認) では、@at-root 無しで直接 BEM 的な CSS が出力できるようになっている。

.block {
  &__element {
    color: #333;
    &--modifier {
      color: #444;
    }
  }
}

コンパイル

.block__element {
  color: #333;
}

.block__element--modifier {
  color: #444;
}

SassでBEM的なCSSセレクタを作るには、このような歴史的経緯があったようです。
@at-root の使い方がイマイチ謎ですが、公式の例をみた感じ、@at-root + 関数 で使うのが一般的っぽい

@mixin unify-parent($child) {
  @at-root #{selector-unify(&, $child)} {
    @content;
  }
}

.wrapper .field {
  @include unify-parent("input") {
    /* ... */
  }
  @include unify-parent("select") {
    /* ... */
  }
}

CSS

.wrapper input.field {
  /* ... */
}
.wrapper select.field {
  /* ... */
}

Sample

See the Pen Sass nest compile test by KIKIKI (@chaika-design) on CodePen.

 

ポエム

Sass の公式にある @at-root の使い方みたいなのを見てもイマイチ使いみちがピンときていません…

個人受託してた時はずっとFLOCSS的な書き方をしていたので、BEMクラス名が長くてびっくりしてます。確かにクラス名を見れば状態がわかるので便利ですが。
あと、個人的に Modifier はマルチクラスで良いんじゃないのかなーってお気持ちがある。


[参考]

CSS設計に関して
Sass @at-root