解決!CSS設計のBEMをわかりやすく解説|エレメント編

HTML、CSSをある程度学習していくと、CSSで修正を入れると他の箇所の表示が崩れてしまったり、CSSのネストが深くなりすぎて、修正がしづらくなったりしたことがありませんか?

どういうCSSの書き方がベストなのか?現場レベルのCSSを知りたいとか?思ってくると思います。

そういう人におすすめできるのが今回のお題のCSSの記法、「BEM記法」というのがあります。

修正するたびに他の箇所が崩れる〜!!管理しやすいCSSの書き方を教えて欲しい!

BEM記法の基礎をこの記事でマスターすると、CSSの影響範囲を極力抑えて、共通パーツ化できるので、管理しやすく、ページ数が多くなればなるほど、効率よく書くことができるので、ぜひこの機会にBEM記法をマスターしておいてください。

僕はフリーランスコーダーとして4年ほど経過して、ディレクター兼コーダーとして年間50サイト以上を手掛けております。

BEM記法でCSSの影響範囲を最小に絞り込もう

まずは「BEM記法」のメリットについて理解を進めていきましょう。

BEM記法のメリット

・BEM記法の書き方でCSSの影響範囲を最小にできる

・CSSのネストを最小にできる。

・CSSの影響範囲を絞り込むことでパーツを共通化(コンポーネント化)できる

BEM記法の最大のメリットは影響範囲を最小にでき、共通パーツ化できるということになります。

BEMの記述方法により、他とクラス名が確実に被らないので、クラス名をネストする必要がなくなります。無駄にネストする必要がないので、CSSの視認性のアップを見込めます。クラス名は唯一無二なので効率的にHTMLを共通パーツ化できるのもメリットですね。

むしろ自分はBEM記法を使い出してから、もうBEM記法以外の書き方には戻れないほどになっております。

反対にBEM記法のデメリットをみていきましょう。

BEM記法のデメリット

・すべてのHTMLタグにクラスをつけないといけなくなる

・クラス名が長くなる

BEMは基本的にはHTMLタグすべてにクラス名をつけて管理するというルールがあります。

なので、込み入ったデザインでHTMLを深くネストしていかないといけない時にクラス名をつけていくのが面倒に感じる時があります。ですが、そういうルールにしておくことでCSSをネストして書かなくてもいいのでメリットでもあります。

また後術しますが、エレメントやモディファイアを書く場合、エレメント名はアンダースコアを2つ(__)をブロックの後に繋げてその後にエレメント名を書き、モディファイアはスラッシュ2つ(--)をエレメントやブロックの後に繋げてその後にモディファイア名を入れるので、クラス名が必然と長くなってしまいます。それもエレメントか、モディファイアかを分けるための記述ルールなので、仕方ないとはいえるかもしれないです。

BEM記法のクラス名の付け方を学ぼう

BEM記法はブロック、エレメント、モディファイアでHTML構造を作るので、上記添付のように、その頭文字をとってBEMとなります。

ざっくりというと、大外のHTMLタグを成り立たせてるのがブロックで、そのブロック内にあるのがエレメントとなります。

まずは今回作ったサンプルの表示をみていきましょう。よくWebページで表示されるようなCookieのダイアログを作ってみました。

こちらをBEM記法の構造でイメージすると下記のようになります。

ブロック内にエレメントが入っている構造をイメージしていただけると思います。

HTMLでみると下記画像ような感じになります。

赤枠の大外にある「dialog」というクラスがブロックになります。

そして、dialogブロックの配下にあるのがエレメントで、エレメントは「dialog__inner」という書き方で、親のブロック名に続けてアンダースコア2つで区切ってエレメント名を付けます。

このようにエレメントは親のブロック名を引き継いでエレメント名を作っていきます。このようにBEM記法はブロックとエレメントで構成されています。これがBEMの記述方法になります。

BEM記法で書くことでブロック名を他のブロック名と被らないようにすれば、他ののクラス名と被ることはなく、下記添付のようにCSSの影響範囲もブロック内のみの範囲となってくれます。

daialogのブロックとエレメントのCSSの影響範囲は赤枠から出ることはないです。なので、これはいわばCSSの擬似的なコンポーネントといえるかと思います。なので、ブロックをごっそり他の箇所に移しても問題なく表示されることとなります。コンポーネントのわかりやすい参考記事がありましたので、詳しく知りたい人はどうぞ。

どこかのHTML内に含まれてようが、一番大外にあろうがCSSは他と絡み合ってないので、問題なく表示されるのがブロックになります。

あとクラスの書き方で注目したいのがキャメルケースを採用しているところです。一般的にはクラス名はハイフンで区切って書くパターンが多いかと思いますが、これはあくまで僕の考えですが、BEMはアンダースコアやハイフンを多用するので、命名の間違いを起こしにくくするのと、クラス名を極力短くするためにキャメルケースを採用しています。

ハイフン区切りにすると、ブロック名にまでハイフンが入ってくるとだいぶクラス名が長くなってしまうので、キャメルケースを採用することでよりシンプルに書くことが可能になってきます。

BEMのクラス命名規則をまとめると...

・エレメント名はブロック名にアンダースコア2つで区切ってエレメント名を付ける

・単語の区切りはキャメルケースを採用して極力短くシンプルにする

ちなみにですが、エレメントはエレメントの後にエレメントを繋ぐ書き方はNGとなります。

一応、HTMLのソースをコピペ用に載せておこうと思います。実際にエディタ上で書いてみてください。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>BEM講座</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="main">

    <div class="dialog">
      <div class="dialog__inner">
        <h2 class="dialog__heading">Cookieを有効しますか?</h2>

        <p class="dialog__text">
          Cookieの説明文のテキスト。Cookieの説明文のテキスト。Cookieの説明文のテキスト。Cookieの説明文のテキスト。Cookieの説明文のテキスト。Cookieの説明文のテキスト。Cookieの説明文のテキスト。
        </p>

        <div class="dialog__btnBox">
          <div class="dialog__btn">
            <a href="#" class="dialog__btnLink">
              <span class="dialog__btnText">有効にしない</span>
            </a>
          </div>

          <div class="dialog__btn">
            <a href="#" class="dialog__btnLink">
              <span class="dialog__btnText">有効にする</span>
            </a>
          </div>
        </div>
      </div>
    </div>

  </main>
</body>
</html>

次はCSSでdialogブロックを書いていくとどうなるのかを確認していきたいと思います。

BEM記法でCSSを書いてみよう。

BEM記法でCSSを書いていくとこのようになるかと思います。

.main {
  width: 100%;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
}

/* dialog block start */
.dialog {
  max-width: 780px;
  width: 100%;
  background: #fff;
  border-radius: 14px;
  filter: drop-shadow(0 0 10px rgba(0 ,0, 0, 0.4));
}

.dialog__inner {
  padding: 40px;
}

.dialog__heading {
  text-align: center;
}

.dialog__text {
  margin: 0;
}

.dialog__btnBox {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 40px;
  margin-top: 30px;
}

.dialog__btn {
  max-width: 180px;
  width: 100%;
}

.dialog__btn:first-of-type .dialog__btnLink {
  background: #dcdcdc;
}

.dialog__btnLink {
  display: block;
  background: #fff;
  border: 1px solid #666;
  color: #333;
  text-decoration: none;
  text-align: center;
  padding: 10px 5px;
  transition: all .25s ease;
}

.dialog__btn .dialog__btnLink:hover {
  color: #fff;
  background: #666;
}

.dialog__btnText {
  font-size: 14px;
}
/* dialog block end */

わかりやすくコメントアウトでブロック部分を囲んでおきブロックの範囲をわかりやすくしております。

ちょっとクラス名が長くなりますが、すべてBEM記法で書くとブロック名から始まっているので、どこからどこまでがdialogブロックのCSSなのかがわかりやすいですね。

特にCSSには目立った書き方はないですが、全てのHTMLタグにクラス名を配置しているので、ネストして書く必要がないので、CSSがシンプルですね。ボタンの色を変えるのにfirst-of-typeを使って少しネストしていますが、今回はHTMLをシンプルにするためにこのような書き方になってしまいました。

BEM記法はブロックの中にさらにブロックをネストすることができます。今回は同じ形のボタンが2つ並んでいるので、この部分をエレメントではなく、ブロックに変更してみましょう。

ブロック内にネストしてブロックを配置してみよう

今回作ったダイアログボックスは中にボタン要素が入っています(下記添付参照)。

もしかしたら、他の箇所でも同じデザインのボタンが存在してくる可能性もあるかもしれないです。

現状のエレメントの形では使い回しできないので、このボタン部分をブロックとして独立させて、使い回ししやすいようにブロックとして使えるようにしてみましょう。

dialogブロックの中にボタンのブロックを作ります。イメージは下記のような感じになります。

まずはHTMLから変更していきましょう。

ボタン部分のHTMLをブロックに変更しました。今回ブロックが入れ子になったので、コメントアウトを入れてわかりやすくしております。

上記を確認いただけるとわかりますが、dialogブロックの中に「btnSquare」というブロックを作り、「btnSquare」ブロック内の要素は「btnSquare」のエレメントで構成してあります。

このようにボタン「btnSquare」をブロックにしてしまうことで、使い回せるパーツとして他の場所でも使えるようになりました。

ブロックとしての使いわます共通パーツを作る重要なポイントがあります。

それは使いまわす用のブロックには外側のmarginなどのレイアウトを調整するプロパティを付けないというのが必須となります。その理由は、使いまわす用のブロックにmarginを付けてしまうと配置する場所によりmarginの打ち消しの作業が発生してしまい、コンポーネントとして使いにくくなるからです。

なので、使いまわす用ブロックの外側をエレメントなどで使いわます用のブロックに包括して、その包括したエレメントにmarginを設置するように対応します。そうすることで使いまわす用のブロックにはmarginを付けなくてもいい状態にすることができます。

使いまわす用ブロックのポイント

使いまわす用ブロックにはmargin(外の余白)を付けない。

一応、コピペ用にHTMLを貼っておきますので、画面で確認してみてください。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>BEM講座</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <main class="main">

      <!-- dialog start -->
      <div class="dialog">
        <div class="dialog__inner">
          <h2 class="dialog__heading">Cookieを有効しますか?</h2>

          <p class="dialog__text">
            Cookieの説明文のテキスト。Cookieの説明文のテキスト。Cookieの説明文のテキスト。Cookieの説明文のテキスト。Cookieの説明文のテキスト。Cookieの説明文のテキスト。Cookieの説明文のテキスト。
          </p>

          <div class="dialog__btnBox">

            <!-- btnSquare start -->
            <div class="btnSquare">
              <a href="#" class="btnSquare__link">
                <span class="btnSquare__text">有効にしない</span>
              </a>
            </div>
            <!-- btnSquare end -->

            <!-- btnSquare start -->
            <div class="btnSquare">
              <a href="#" class="btnSquare__link">
                <span class="btnSquare__text">有効にする</span>
              </a>
            </div>
            <!-- btnSquare end -->

          </div>
        </div>
      </div>
      <!-- dialog end -->
      
    </main>
  </body>
</html>

ブロック単位でコメントアウトを入れて改行を入れておくと、上記のようにみやすく配置できます。

ボタンをブロック化した時のCSSはこのような感じになります。

.main {
  width: 100%;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
}

/* dialog block start */
.dialog {
  max-width: 780px;
  width: 100%;
  background: #fff;
  border-radius: 14px;
  filter: drop-shadow(0 0 10px rgba(0 ,0, 0, 0.4));
}

.dialog__inner {
  padding: 40px;
}

.dialog__heading {
  text-align: center;
}

.dialog__text {
  margin: 0;
}

.dialog__btnBox {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 40px;
  margin-top: 30px;
}
/* dialog block end */

/* btnSquare block start */
.btnSquare {
  max-width: 180px;
  width: 100%;
}

.btnSquare:first-of-type .btnSquare__link {
  background: #dcdcdc;
}

.btnSquare__link {
  display: block;
  background: #fff;
  border: 1px solid #666;
  color: #333;
  text-decoration: none;
  text-align: center;
  padding: 10px 5px;
  transition: all .25s ease;
}

.btnSquare .btnSquare__link:hover {
  color: #fff;
  background: #666;
}

.btnSquare__text {
  font-size: 14px;
}
/* btnSquare block end */

dialogブロックのCSSと、btnSquareブロックのCSSが完全に分離してみやすくなりました。ブロック単位でCSSを分けておくことでよりCSSが管理しやすくなりますので、そのように調整しておきます。

今までの内容でBEM記法のブロックとエレメントについて学びました。

また最後のモディファイアに触れてなかったのですが、簡単にモディファイアについて説明すると、共通パーツとして使いまわすことができるといいましたが、そのブロックを使いまわす際にブロックを再利用できるのがモディファイアという使い方になります。

例えば、背景色だけ違ってそれ以外同じのパーツに、また新たに別のブロックを作るのは、効率的にあまり良くないので、そういう状況でモディファイアを使って既存ブロックをカスタマイズして再利用できるようになります。

モディファイアを使えるようになるとかなり柔軟にブロックを扱えるようになるので、非常に効率よく作業できるのでぜひマスターしておきましょう。

次は、BEM記法をどのくらいのレベル感で現場で通用するのか?と解説したいとおもいます。

BEMの現場で使われているレベル感を解説

今のレベル感でも全然使っていっても大丈夫です!

全てのBEM記法をまだ学んでない状態でもガンガン使っていきましょう。実際に使ってみることで脳にインプットしていけるので、ビギナーはブロックとエレメントのみをまずはしっかりと頭に叩き込んでおいた方がいいかもです。

しかし、現場で実際に使われているレベルはもっと効率的で運用性を意識して使われております。僕自身現場でしごかれたのでよくわかるのですが、現場レベルは本当に徹底しております。

現場レベルのBEM記法のレベル感(例)

現場のレベル感の例

・CSSのエレメントとモディファイアの配置を決めておく。

・SASSでエレメント、モディファイアはブロック名の後のクラス名を切り離して書く

・SASSでブロック単位でファイル分けをする

・モディファイア同士でネストしない。

要はただ書くだけではなく、運用面を意識して、SASSで効率よく書くことが求められております。

そして、ルールを作ってそれを遵守して書いていくことで、他との統一性をとることができます。主にチームでコーディングする際にルール決めをしておかないと、後で収集がつかなくなるので、チームコーディングの際は最初のルール決めが非常に重要になってきます。

「じゃあ、チームコーディングしないなら必要ないですよね?」と思ったそこのあなたは少し認識違いを起こしてるかも知れないです。

チームコーディングでなくても10ページレベルのコーディングとなるとルールを決めて書いていくと迷いを消すことができ、パーツ化もしっかりと出来てるので、コーディングスピードが上がるし、結局ページ数の多い案件ほど差が出てくると思われます。

CSS設計のBEMの中のモディファイアについても詳しく解説している記事を書きました。モディファイアを使うことでブロックやエレメントをもっと柔軟に使えるようになります。下記からモディファイア編をどうぞ。

モディファイア編を読んだらSASS編もどうぞ。

下記記事でBEM記法の書籍を紹介してますので、BEMについてもっと詳しく学習したい人はおすすめですので良かったら読んでみてください。

※上記書籍ではモディファイアはアンダースコア1つと書いてますが、実際の現場はモディファイアはハイフン2つなので、その点にだけ注意してください。

上記書籍ですが、各パーツでのBEMの書き方が詳しく書かれていて基本的なHTML構造を学べるので、高額となりますが買って損はないと思います。

これまでのコードを一応、ダウンロードできるようにしておきましたので下記ボタンからダウンロードして直接コードを確認できます。

もし今回わからない箇所などありましたら、下記からご相談いただけましたらありがたいです。

この記事を書いた人

アバター

トノムラマサシ

masashi
Webサイト屋兼ブロガー|過去に企業常駐などを経て現在はフリーランスでディレクションとコーダーとして活動しております。JS大好きなのでJSの仕事依頼お待ちしております。京都在中で5人家族。