あなたのお役に立ちたいわんど。

【BEM】よりよいCSSセレクタの命名則を探して【実務で使ってみた】

みなさんこんにちは!
快適なCSSライフをお過ごしでしょうか。
軽量な動作にも関わらず無限の可能性を秘めたCSSさん今日もありがとう。

私も本格的にWEBサイトを作り初めて早2年。
シンプルが故に奥が深いCSS。ただ、そのシンプルさの中にたびたび疑念が湧き上がります。
具体的に言うなれば、「セレクタの命名則ってもっと正しく書けるのではないだろうか…」という疑念です。
CSSを見つめるときCSSもお前を見つめているのだとはよく言ったものです(?)。

極端な話、要件通り動いていれば最低ラインは満たしているわけですが、
プロジェクトの規模が大きくなればなるほど、セレクタの命名則が一貫してしていて、なおかつ合理的なものでないと、
サイトが破綻するばかりか、開発体験にもストレスが発生するし、良くないことばかり。
私もランディングページのコーディングでは、強く意識しなかったものの、WORDPRESSでコーポレートサイトを作るくらいの規模感になった時に、特にそのようなことを考えていました。

それを踏まえ、今回はそんなCSS設計に指針を提供してくれる参考書「Web製作者のためのCSS設計の教科書(著:谷 拓樹氏)」を引用しながら、
よりよいセレクタの命名則について考えていきます。

よいCSS


この本では、冒頭に”よいCSS”とは以下の4点を満たしているものであるという説明があります。

・予測しやすい
・再利用しやすい
・保守しやすい
・拡張しやすい

CSS Architecture by Philip Walton https://philipwalton.com/articles/css-architecture/

きっぱり言語化されていて、よいCSSという漠然とした概念が少しだけはっきりします。
逆に考えると、これを満たしていないCSSはストレスを生みがちとも言えると思います。

上記の4点はチーム開発において非常に重要なポイントであるというのは、想像に難くないのではないでしょうか?
ですが、チームの前に個人の話を忘れてはいけません。
例えそれが個人で開発するプロジェクトであっても、この4点は非常に重要です。
忘れないでください。プログラミングにおいて昨日の自分はもはや別人です。
なんだこの子供が散らかしたみたいなわんぱくコードは。ひどい。俺の方がうまくかける。何これ。カオスの具現化。ワ〜〜。ねえ誰これ書いたの。
はい。
自分でした。

それが何であるか、そしてどこにあるかが、明日の自分でも容易に分かること。
これを満たせれば開発は確実に快適になります。

どう書くのか


先の4点は目標ですが、重要なのは[HOW]です。

*今回前提としてSCSSを利用できる環境を想定します。(コンパイルを前提にすれば開発にSCSSを用いることができないというケースはないと思いますので。)

仮に、このようなHTMLのブロックとCSSがあったとします。

<div class="container">
  <div class="container-column left">
    <div class="container-img">
      <img src="" alt="Andplusのひよこ" />
    </div>
  </div>
  <div class="container-column right">
    <p class="container-title">Andplusのひよこ</p>
    <div class="container-description">
      ひよこチヤンです。今日も元気です。ピヨピヨ。
    </div>
  </div>
</div>

.container {
  display: flex;
  gap: 0 30px;
}

.container-column {
  &.left {
    width: 150px;
  }
  &.right {
    flex-grow: 1;
    line-height: 1.5;
  }
  .container-img {
    img {
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
  }
  .container-title {
    font-size: 20px;
    font-weight: bold;
    margin: 0 0 10px;
  }
  .container-descrition {
    font-size: 16px;
  }
}

ごく一般的な構造で、表示上問題ないといえば問題ありません。

ですが、少しソワソワします。
なぜソワソワするのか具体的には以下のようなことです。

・containerという一般的すぎるクラス名。他の箇所で同名セレクタのスタイルが書かれていた場合干渉してしまう可能性が高い。(保守しにくい、再利用しにくい)
・同じく left right というクラスも分岐として用いているが、これも一般的すぎて干渉の恐れがある。(保守しにくい、再利用しにくい)
・container-column container-title というセレクタは機能こそするが、HTMLの階層とはリンクしておらず、セレクタが指示しているものが何か直感的にわかりづらい。(保守しにくい、拡張しにくい、予測しにくい)

このブロックという最小単位でみても、良いCSSの4原則に反する箇所が多く見受けられます。
また、これらの問題は全て
・CSSセレクタの命名則に具体的で合理的なルールがない
ことに起因すると言えます。
書いている本人の中での命名則は一貫しているかもしれませんが、
言語化できるルール、共有しやすいルールである方が、個人レベルでもチームレベルでも好ましいことに変わりはありません。

ここでは、本の中で説明されている有名なCSS記法の一つであるBEMを紹介しつつ、そのメリットとデメリットに触れていきます。

BEM


BEMでは、HTMLブロックをModuleとModifierという単位でとらえます。

大まかには、それぞれはこういう分類になります。

Block -> 大きな括り
Element -> ブロックの中の要素
Modifier -> ブロックやエレメントの変化

それを先ほどのコードに当てはめて考えると

Block  ->  <div class=”container”></div>
Element ->  <div class=”container-column></div>
Modifier -> <div class=”container-column></div> における class=”left”

となります。

そして、クラス名は
[Block名]__[Element名]–[Modifier名]
という繋ぎで記述します。
Elementは_ _(アンダーバー)、Modifierは- -(ハイフン)でつなぐというのがポイントです。

それを踏まえて書き直したHTMLとSCSSがこちらです。

<div class="container">
  <div class="container__column container__column--left">
    <div class="container__column__img">
      <img src="" alt="Andplusのひよこ" />
    </div>
  </div>
  <div class="container__column container__column--right">
    <p class="container__column__title">Andplusのひよこ</p>
    <div class="container__column__description">
      ひよこチヤンです。今日も元気です。ピヨピヨ。
    </div>
  </div>
</div>

.container {
  display: flex;
  gap: 0 30px;
  &__column {
    &--left {
      width: 150px;
    }
    &--right {
      flex-grow: 1;
      line-height: 1.5;
    }
    &__img {
      img {
        width: 100%;
        height: 100%;
        object-fit: cover;
      }
    }
    &__title {
      font-size: 20px;
      font-weight: bold;
      margin: 0 0 10px;
    }
    &__description {
      font-size: 16px;
    }
  }
}

先の記述からの大きな変化は、ブロックにまつわるSCSS記述を完全にツリー化することができたことです。
実は先の記述も、BEMではありませんが、クラス名をなんとなく-(ハイフン)をつなぐ命名則なので、同様にツリー化することが可能ではあります。
ですが、BEMではElementとModifierという書き分けが存在するので、セレクタからHTMLの階層をより予測しやすいという点で優れています。
.container__column–left
このセレクタを見るだけで、「このコンテナーというブロックには、カラムが存在し、これはそのうち左側を指している」ということが予測できるということです。

HTMLは階層構造です。それにスタイルをあてていくわけですから、CSSも同様に階層構造になっていることは、とても直感的な対応です。
SCSS環境が整備されていない場合でも、SCSSのコンパイルシステムを用意し、BEMとSCSSを併用すべきというのも理解いただけるのではないでしょうか。

少し余談になりますが、近年ではShopifyがSCSSの標準でのサポートを廃止しましたが、あれはレンダリングの際にSCSSをコンパイルするにあたって応答時間が長くなるのを避けるためなので、
開発者がSCSSをコンパイルしてCSSとしてアセットすることは一向に構いません。なお、Shopifyもその方法を推奨しています。

ShopifyテーマでSassが非推奨となります https://www.shopify.com/jp/blog/partner-theme-sass-depricated

BEMのメリットとデメリット


私はこのBEMを用いて、コーポレートサイトをいくつかリリースしました。
それを経て、実践におけるBEMのメリットとデメリットを紹介します。

【メリット】

・セレクタにルールがあるので、クラス名何にしたらいいかな〜と悩む時間がなくなった。
  ->これはブログのラッパーだな .blogWrapper
  ->これはブログのタイトルと概要だな .blogWrapper__title .blogWrapper__description
  ->概要の中のボタンだな .blogWrapper__description__button とどんどん決められる。

・セレクタを見ればどんな要素か大体わかるのがやっぱり便利。
  ->HTMLとCSSについて書いた時のことを忘れていても、その場で構造と役割をすぐに思い出せる。

・HTMLの構造とSASSの構造がリンクするのもやっぱり便利。
  ->HTMLを眺めて、編集する箇所がわかった後、SCSSにおいても構造をなぞるように該当の箇所に行けるので直感的
  ->独立した命名則を用いていたときは、セレクタの検索機能ばかり使っていたが、同名のヒットや、そのジャンプするようなカーソルの動きが若干のストレスだった。

・セレクタの一意性が高いので、スタイルが干渉してしまったという事故はほぼ0になった(でかい)

===========================================================================================

特に最後のメリットは素晴らしいことです。
BEMを導入してから、自分の書いたスタイルが干渉を起こしたケースはほぼ0になりました。

これはクラス名の一意性が担保されていることの象徴ですが、
ルールなしに、常に一意のクラス名を考えるというのは頭が疲れる作業です。
ですが、BEMに従っていれば、あまり広範囲のことに頭をめぐらせずとも、自然に一意のクラス名が生まれます。

次はBEMのデメリットです。

【デメリット】

・&記法の性質上、クラス名が分割されるので、クラス名から一発で検索がかけられない
  ->BEMのツリー構造が直感的とは言えど、クラス名でSCSSにコード検索をかけたいときもままあります。ただこの場合は、”.container__column–left”という文字列で一発で検索できるわけではなく、”container”という単語から検索をかけていくことになるので、そこは少し不便です。

・階層が深くなると命名がやたら冗長になる。
  ->BEMに忠実に従っているとこの問題が発生します。具体的には.blogWrapper__items__item__column__texts__linkのようなクラス名が生まれえるということです。ただ、名が体を表すBEMにおいてはトレードオフとも言えるのではないでしょうか。

・ここまできてツリー構造を崩す?となる
  ->上記のクラス名冗長問題が発生したとき、ブロックの中に新たにブロック作って、セレクタのツリーを一度リセットする必要があるかもしれません(BEMのルールとして、ブロックの中にブロックを作るのはOK)。では、どのタイミングでブロックを新たに切るのが適切か?という問題は往々にして発生します。

・途中で擬似クラスが挟まれるとアンド(&)記法が破綻する
  ->これは地味ですが、無視できない問題です。
具体的には、:hoverにスタイルを組んでいきたいときなどです。
特にCSSの6行目に注目してください。

<div class="container">
  <div class="container__column container__column--left">
    <div class="container__column__img">
      <img src="" alt="Andplusのひよこ" />
    </div>
  </div>
  <div class="container__column container__column--right">
    <p class="container__column__title">Andplusのひよこ</p>
    <div class="container__column__description">
      ひよこチヤンです。今日も元気です。ピヨピヨ。
    </div>
  </div>
</div>

.container {
  display: flex;
  align-items: center;
  gap: 0 30px;
  &:hover {
    .container__column__img {
      transform: scale(1.1);
    }
  }
  &__column {
    &--left {
      width: 100px;
    }
    &--right {
      flex-grow: 1;
      line-height: 1.5;
    }
    &__img {
      overflow: hidden;
      img {
        width: 100%;
        height: 100%;
        object-fit: cover;
                transition: transform 0.3s ease-in-out;
      }
    }
    &__title {
      font-size: 20px;
      font-weight: bold;
      margin: 0 0 10px;
    }
    &__description {
      font-size: 16px;
    }
  }
}

&:hoverの先の枝では、&による連結記法が崩壊しているのが分かります。
ここもツリーの記法に倣って

.container {
  &:hover {
    &__column__img {
      transform: scale(1.1);
    }
  }
}

などと書き進めていきたいところですが、
.container:hover__column__img
というセレクタ指定はCSSで無効です。
このように擬似クラスの記述がscssのツリーの中に出現した場合、&を用いた連結記法を継続できないことがあります。

総括


最後に紹介したデメリットのように、BEMも完璧ではありません。一つのCSS設計の方法です。

ただ、セレクタの命名則に統一感がないと感じている人が導入するのは大いにありです。
慣れるまで時間がかかると感じても、一度導入してしまえば保守段階でのメリットが大きい印象です。

この書籍においては、他にも、BEMの親戚であるOOCSS, SMACSS, MACSS, FLOCSSが紹介されています。

CSSと最適なセレクタ命名則は永遠のテーマであり、最適解がないからこそ、さまざまな方法があります。

近年のjsフレームワークを用いた開発では、CSS-Module(フレームワークが自動で一意のクラス名を割り当てるため、コーダーは他コンポーネントとのクラス名の干渉を考慮しなくて良くなる)などの、モダンな方法も普及しつつあります。
どんなCSS運用が適しているかは、規模や、前提条件によって変わります。
ぜひその都度最適な方法を見つけてみてください!

ではまた!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

< すべての記事を見る >

Web制作の株式会社あんどぷらす
ECサイト構築サービス「ウルトコ」
イベントスペース「fuigo」
CS-Cartの情報ポータル「STOCK」