BEMとSassの「&__Element」記法を使わない派に転向した理由

数年間、BEMとSassの「&__Element」記法を使ってコーディングしていました。しかしあるきっかけでその記法を使わない派に転向したところ、想像していたのとは違うメリットを感じました。その理由などをご紹介します。

はじめに(「&__Element」記法とは)

※「&__Element」記法という言葉は造語です。

CSS設計手法のBEMと、Sassの&(親参照)を組み合わせて、以下のように書けます。

scss
.awesomeBlock {
  margin: auto;

  &__awesomeElement {
    color: red;
  }
}

Sassの&は親(インデントのひとつ上)と同じように振舞うため、このSassをコンパイルしたCSSは↓こうなります。

コンパイル後のcss
.awesomeBlock {
  margin: auto;
}

.awesomeBlock__awesomeElement {
  color: red;
}

&:hoverのような使い方以外に、このようにクラス名の連結として&が使われることは現場ではよくあると思います。

ブロックの部分を書かなくて良いし、仮にブロック名に変更があっても一番上だけ変えれば良いので、効率的だなと思って数年間この記法を使っていました。

しかし、とあるきっかけでこの記法をやめ、現在はSassでも

scss
.awesomeBlock {
  margin: auto;
}

.awesomeBlock__awesomeElement {
  color: red;
}

と書くようにしています。

その理由、実際にやってみて感じたメリット・デメリットを書いていきます。

「&__Element」記法をやめたきっかけ

以前、とあるシステムのフロントエンド設計見直しのお仕事をいただきました。

そのシステムの見直し前のSassは↓こんな感じでした。

scss
.list {
  margin: {
    right: auto;
    left: auto;
  }

  &.type-ul {
    ul {
      list-style-type: disc;

      li {
        &:before {
          content: "*";
        }
      }
    }

    @media all and (min-width: 992px) {
      .lang-en & {
        letter-spacing: 0.025em;
      }
    }
  }
}

(コードはイメージです)

これを見て感じたのはインデントが深すぎて出力されるCSSが想像できない!ということでした。

コンパイルすると↓このようなCSSになるのですが

コンパイル後のcss
.listBlock {
  margin-right: auto;
  margin-left: auto;
}
.listBlock.type-ul ul {
  list-style-type: disc;
}
.listBlock.type-ul ul li:before {
  content: "*";
}
@media all and (min-width: 992px) {
  .lang-en .listBlock.type-ul {
    letter-spacing: 0.025em;
  }
}

なんかもう普通にCSS書いた方がシンプルでは?という感じです。

このSassを直していく中で、とくに

  margin: {
    right: auto;
    left: auto;
  }

の部分に違和感を感じました。

インデントを下げるという行為に対して得られるメリットが、入力する文字数を少し省略できることだけ

Sassの中でインデントが下がっている場所は詳細度が高かったりセレクターの親子関係が作られていることもあります(というかそういう使い方がメイン)。

なので、インデントを下げるという行為を慎重に使うため、「&__Element」記法を含む、文字を省略するためのインデントをいったん排除してみたのがきっかけです。

やってみて感じたメリット・デメリット

メリット

先に紹介した例のSassを見直し、以下のような感じにしました。

scss
.listBlock {
  margin-right: auto;
  margin-left: auto;

.listBlock__ul {
  list-style-type: disc;

  @media all and (min-width: 992px) {
    .lang-en & {
      letter-spacing: 0.025em;
    }
  }
}

.listBlock__item {
  &:before {
    content: "*";
  }
}

インデントが下がっている部分は、メディアクエリの中であったり、擬似要素であったり、詳細度が高いものだけです。

インデントが下がっている部分をとくに気をつけて見ることで、バグの少ないCSSが書けそうです。

この見通しの良さが私にはとても気持ちの良いものに感じました。

ちなみに、これを「&__Element」記法で書くと↓こうなります。

scss
.listBlock {
  margin-right: auto;
  margin-left: auto;

  &__ul {
    list-style-type: disc;

    @media all and (min-width: 992px) {
      .lang-en & {
        letter-spacing: 0.025em;
      }
    }
  }

  &__item {
    &:before {
      content: "*";
    }
  }
}

「&__Element」記法をやめてから、この中のとくに以下の強調部分に関して

scss
.listBlock {
  margin-right: auto;  margin-left: auto;
  &__ul {
    list-style-type: disc;
    @media all and (min-width: 992px) {
...

CSSとしては等価(詳細度が同じ)なのにインデントの深さが違うという点について違和感を覚えるようになりました。

「&__Element」だと検索できないから非効率?

Twitterやブログをみる限り、このメリットを推す方が多い印象です。

大規模なシステムやサイトでは .Block__Elementでエレメントを直接検索できないのが不便だとか。

しかし、私はこれにメリットを感じませんでした。

大規模サイトであればこそブロック単位で適切にファイル分割されているべきだから、サイトの規模と検索には関係があるのかな?

また個人的にはBEMのエレメントに直接ジャンプするということをしたことがありません。

デメリットはなかった

実際に「&__Element」をやめてみると、当初考えていたデメリットは実作業においてほぼ影響がありませんでした。

入力する文字数が多くなって開発効率が落ちる懸念について

エディターの補完機能で、先頭の.を入力した時点でクラス名の候補が表示されるので、これで十分でした。

1

BEMのブロックごとに適切にファイル分割ができていれば、この候補の先頭に必ず親ブロックが表示されるので、.のあとにエンターを押すだけです。

ブロック名変更があったときに面倒という懸念について

そもそも一度決めたブロック名を変更するということがほとんどありませんでした

仮にあったとして、こちらもエディターの一括置換などで十分カバー可能です。

終わりに

以上から、Sassのインデントに詳細度、親子関係、擬似要素などの役割を明確に持たせ、見通しの良い設計にできるメリットがデメリットを上回ったと感じるため、今後も「&__Element」を使わないスタイルで行こうと思っています。

正直なところ、「&__Element」記法を使うか使わないかといったことは些細な話なので、あまりこだわりはありません。

今回の件で得た学びは、ある手法を実際に現場で使う前と後では、メリットデメリットの考え方が変わるという点でした。

別の手法や新しい設計、こういったものを試すことで、従来使っていた手法の良し悪しも見えてきます。

新しい手法を試すための余裕をいつも持っていたいと思います。それが難しいのですが・・・(笑)

最後までお読みいただきありがとうございました。