決着!CSS設計のBEMをわかりやすく解説|モディファイア編

CSS設計のBEMという方法でブロックやエレメントを使い回す際に再利用をするモディファイアという手法について書いてきます。

CSS設計のBEMという方法を前回はブロックとエレメントについて書かきましたが、今回はその続きとなるモディファイアになります。

もしCSS設計のBEM記法でブロックやエレメントがわからないという人は、前回の記事でBEMのブロックとエレメントについて詳しく解説してますのでよかったらどうぞ!

BEMのブロックを使い回したいんだけど、ブロックを効率よく使い回すにはどうすればいいんだろう?

BEM記法のブロックとエレメントですが、ブロックをパーツとして使い回す際に、よく色だけが違うバージョンのパーツや、少し幅広いパーツなどが実際の案件などでもよくみるのではないでしょうか?

そういう場合は新たにブロックを別で作ったり、エレメントを新たに作ったりしてもいいのですが、効率や運用性的に部分的にだけスタイルが違うようなパーツは一つにまとめておきたいところですよね。また新たにブロックを作った場合はCSSの記述量がガッツリ増えてしまうのも後の管理が大変そうです。

なので、そういう場合は、ちょっとの記述でブロックやエレメントを再利用することができるもモディファイアを使うのが最適になってきます。

では、具体的にどういう状況でモディファイアを使用すればいいのでしょうか?

前回dialogブロックを使って解説したので、今回も同じ例で解説していきます。

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

モディファイアでブロックやエレメントを再利用して使い回す

こちらが前回記事で使ったdialogブロックになります。

こちらのdialogブロックはボタンが2つあって、一つは背景色がグレーで、もう一つは背景色が白で、それ以外のスタイルは全く一緒となります。

ボタンはbtnSquareというブロック名で前回最後にブロックにしました。こういう場合はモディファイアを使って、btnSquareの背景色を違うバージョンを作っていきましょう。

一旦、全HTMLと全CSSを載せておきます。

<!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>
.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 */

現状はボタンは2つともbtnSquareブロックで作ってあり、CSSで:first-of-type擬似要素を使い1つ目の要素だけに背景色をグレーにしてます。

こちらの方法ですが、一見問題なさそうですが、パーツとして使い回す場合に少し面倒めんどくさいことになります。例えば、こちらのdialogボックスでパーツとして使い回して、「有効にする」の背景が白の方のボタンをやっぱり左側に配置したい。となった時、当然HTMLを書き換える必要がありますが、そうなるとCSS擬似要素の:fisrt-of-typeも書き換えないといけなくなります。

当然ブロックなので、他の場所にも使いまわす前提でブロックとして設計しているので、他の場所では同じ一つ目のボタンがグレーだという保証はないですよね。

なので、使い回すパーツとして考えるなら設計的に:first-of-typeの擬似要素はあまり良くないとないといえるかもしれないです。

このような状況がモディファイアの出番となります。

BEMのモディファイアの書き方をマスターしよう

:first-of-typeや、:last-of-typeなどの擬似要素を多用すると、HTMLからはどのようにスタイルが当たっているのかが全くわからなく、状況把握だけでかなりの時間が必要になるので、基本的にパーツとして使い回す前提なら:first-of-typeなどのスタイルの当て方はしないようにしたいですね。

CSS擬似要素の:fisrt-of-typeは外して、モディファイアで左の背景色を変更していきましょう。

<!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 btnSquare__link--gray">
                <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>

背景はaタグの「btnSquare__link」というエレメントに背景カラーを当てている(hoverの関係で)ので、「btnSquare__link」というエレメントに「btnSquare__link--gray」というモディファイアを設置します。

上記添付のように「btnSquare__link」のエレメントに「btnSquare__link--gray」というようにモディファイア名をつけて、モディファイアはハイフン2つ「--」を区切って付けます。

次はbtnSquare__link--grayモディファイアにスタイルを付けます。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__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__link--gray {
  background: #dcdcdc;
}

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

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

.btnSquare__linkクラスの下に.btnSquare__link--grayクラスを入れました。このように.btnSquare__link--grayモディファイアを.btnSquare__linkクラスよりも下に書くことでCSSの優先順位がモディファイアの方が上になるので、モディファイアでスタイルが書き換えることができます。

あと、:first-of-typeなどの擬似要素を使うとCSSの優先順位が上がって困ったことはないでしょうか?

:first-of-typeなどの擬似要素を使うと1つ優先順位が上がってしまいます。なので、その下に上書き用のクラスを書いても上書きできないので、結局クラス名をネストして上書きする必要が出てきます。

先ほどは:hover部分のクラス名ネストしてCSSの優先順位を強制的に上げてましたが、今回モディファイアを使うことで、:hoverのネストが必要が無くなって(擬似要素を外したので)よりシンプルなCSSになりました。

モディファイアのメリット

・HTMLからどのようなスタイルが付いているのかが想像しやすくなる

・擬似要素ではなくモディファイアにすることで使い回すパーツとして使いやすくなる

・モディファイアは優先順位が上がらないので、CSSがよりシンプルになり運用がしやすくなる

上記にモディファイアのまとめを書きましたが、モディファイアの真骨頂はやはり「擬似要素ではなくモディファイアにすることで使い回すパーツとして使いやすくなる」というのが一番メリットだと思います。

BEMはパーツとして使い回すことができる記法なので、それに適した書き方は必要になってきます。モディファイアはまさにパーツとして使い回すベストな方法だと思います。

今回モディファイアを使うことで、新規ブロックを作らないで既存ブロックを再利用できました。

ブロックをモディファイアで今回は再利用して、違うスタイルのボタンを作ることができたのですが、再利用の線引きって難しくないですか?

どの程度のスタイル変更なら既存ブロックの再利用(モディファイア)がいいのか?どこ範囲以上なら新規ブロックがいいのか?この辺りの線引きが微妙に難しいですよね。その辺りに付いて次は書いていきたいと思います。

モディファイアのスタイル変更は最小にして使い分けよう。

既存ブロック、エレメントでモディファイアを使うか、それとも新規ブロック、新規エレメントを作るかの線引きは、結論からいうと答えは個人の裁量によります。

このあたり僕の考えでいうと、

モディファイアを使う線引き

・そのスタイル変更はブロック全体の機能から逸れてないか?

・あまりにもHTMLがごちゃごちゃしそうなら(HTMLにモディファイアが多すぎることになるなら)新規ブロック、エレメントを考える

上記の2点で判断します。

本質に戻るとBEMはパーツとして使い回すことを前提としているので、ブロックの機能に逸れるモディファイアはパーツとしての統一性がなくなってくるし、HTMLがごちゃつくと後々の運用に支障が出てきそうです。

そうならない範囲でモディファイアを採用するか、新規でブロック、エレメントを作るか?を判断するということになります。

モディファイアの作り方のコツはユーティリティのような感じで作っていきます。ユーティリティとは1つのクラスに1つのスタイルのことです。ユーティリティの参考動画がこちらです。9:00〜みるといいです。

ですが、完全に1つのモディファイアに1つのスタイルでないといけないかというとそうでもないです。

例えばですが、今回のボタンに幅が違うモディファイアを設置してみましょう。

広めの幅のバージョンのモディファイアを作る感じでいくので、「btnSquare--large」というモディファイアにしてみましょう。

まずはCSSに「btnSquare--large」というモディファイアを設置します。

.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--large {
  max-width: 220px;
  width: 100%;
}

.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__link--gray {
  background: #dcdcdc;
}

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

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

幅が違うバージョンのモディファイアなので幅の指定はブロックに幅のスタイルを設置しているので、ブロックに「btnSquare--large」というモディファイアを設置しました。

次はHTMLに「btnSquare--large」モディファイアを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 btnSquare__link--gray">
                <span class="btnSquare__text">有効にしない</span>
              </a>
            </div>
            <!-- btnSquare end -->

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

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

HTMLの方は有効にするのボタンの方に「btnSquare--large」モディファイアを設置してみました。

わかりやすく下記にスクショも載せておきます。

これで表示をみてみると下記のような感じになります。

「btnSquare--large」モディファイアを設置して、「有効にする」のボタンだけの横幅を大きくすることができました。今回の「btnSquare--large」モディファイアではwidth関連のモディファイアとなるので、下記スタイルのようにwidth系のスタイルのみを設置しましたが、width関連のスタイルなら複数のCSSのプロパティを入れても大丈夫です。

.btnSquare--large {
  max-width: 220px;
  width: 100%;
}

上記のように幅関連のモディファイアなら幅のみのプロパティを設置するというふうにモディファイアを組むと運用的にも使いやすい状態になります。

もし、モディファイアが複数になってきて、1つのHTMLタグで2、3個以上モディファイアを付けないといけない状況ならHTMLのクラス部分がごちゃつくので、新規ブロックや新規エレメント作った方がいいかもです。

モディファイアの命名のコツをもう一つ書くと、今回--largeと命名できたのは、ブロックにデフォルトで幅のスタイル(max-width)があるからで、それを相対的に捉えてデフォルトの設定よりも大きいので--largeとモディファイアで命名できました。

なので上の添付のようにデフォルトのブロックのスタイルを設定しておくと、それよりも大きければ--large、小さければ--smallなど、命名が付けやすくなるので、それも覚えておくといいかもです。

モディファイアの設置のポイント

・HTMLがごちゃつきそうなら新規ブロック、エレメントを考える

・モディファイアのスタイルはユーティリティのようなイメージで作るが、スタイルの複数設置はOK

・ブロックやエレメントのデフォルトのスタイルから相対的に捉えて命名を考える

モディファイアの作り方は上記でまとめましたが、逆にNGな書き方はモディファイアにモディファイアを付けることですね。

例えば今回の例でいうと、「btnSquare__link--gray」モディファイアに幅広のスタイルを後付けしたいので、「btnSquare__link--gray--large」というモディファイアに更にモディファイアを付ける書き方はNGとなります。エレメントもそうなのですが、エレメントにエレメント「btnSquare__link__item」をという書き方もNGになります。

モディファイアのNGな書き方

ブロックやエレメントにはモディファイアは一つしか繋がないというルールとなります(複数を繋げて書かない)。

今回はモディファイアについて深く掘り下げて書きましたが、次はSASSを使って効率良くBEMを書く方法について書きました。

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

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

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

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

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

この記事を書いた人

アバター

トノムラマサシ

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