BEMをSassで快適に書く方法

BEMを取り入れてCSSを書くようになりました。BEMを使うと、ブロックごとにモジュール化し、名前空間を持たせるようなことができるようになり、わかりやすく破綻しにくいCSSが書けるようになるため、大変気に入っています。さらにSassで書くと、SassにはBEMを快適に書けるような機能が備わっているので、普通にCSSを書くよりもだいぶCSSを書くのが楽しくなります。今回はそんなBEMの概要とSassを使ってBEMを書く自分なりのテクニックを紹介します。

bem
BEM. Block, Element, Modifier

BEMとは

BEMの概要

もうすでにいろんなブログやQiitaなどで書かれていると思いますが、自分の頭を整理するためにも書いておきます。BEMはロシアの検索エンジン大手のYandexにより以下を達成するために開発された「cssの記述方法論」です。CSSセレクタの「命名規則」とも言われています。

  • 標準的なプロジェクトのための迅速な開発と長続きする成果
  • 多くの人を巻き込んだプロジェクト
  • 拡張可能なチーム
  • コードの再利用性

Block, Element, Modifier

BEMは、セレクタの役割を「Block」「Element」「Modifier」の3つの概念に分けて考えるCSSの設計思想です。BEMのドキュメントによると、Block、Element、Modifierは、それぞれ以下のように定義されています。

Block

「Block」は独立したエンティティ(実体)であり、アプリケーションを「構成するパーツ」です。Blockは単一で用いることも可能だし、他のBlockを含んで複合的に用いることも可能です。Block名は基本的にユニークなものをつけ、パーツごとの独立性を保つようにします。
bem-block

Element

「Element」はいくつかの機能を備えた「Block」の部品(要素)です。Elementはコンテキスト(文脈)に依存し、Element自身が属するBlock内でのみ意味をなします。
bem-element

Modifier

「Modifier」は「Block」または「Element」の見た目や振る舞いを変更するプロパティです。Modifierは名前や値を持ちます。複数のModifierを同時に使うことも可能です。Modifierは違いがほとんどないBlockやElementを作ることを避けるために使います。
bem-modifire

詳細はBEMのドキュメンをご参照ください。

BEMの書き方

BEMの基本スタイル

BEMの基本スタイルは以下の通りです。ルートとなる「block」に対して、「element」と「modifire」をそれぞれ「__(アンダースコア2つ)」「_(アンダースコア1つ)」で繋げてセレクタを作ります。

  • block { }
  • block__element { }
  • block_modifier { }
  • block__element_modifier { }

modifierを「_(アンダーバーひとつ)」ではなく「(ハイフン2つ)」で繋ぐmindBEMdingという書き方もあり、こちらを使う方が多かったりします。

BEMの例

BEMを使ってどのようにスタイルを書くか例を挙げてみます。以下のサンプルは、ボタンが3つ並んでいて、1つだけ赤色になっています。

HTMLは以下のように書きます。共通する部分は「navi__button」で指定して、赤い色となるボタンにはさらに「navi__button_red」を指定しています。(ちなみに、このように複数のclassを指定してひとつの部品を作り上げる書き方を「マルチクラス」と呼びます。)

<div class="navi">
  <button class="navi__button">.navi__button</button>
  <button class="navi__button navi__button_red">.navi__button_red</button>
  <button class="navi__button">.navi__button</button>
</div>

CSSは以下のように書きます。「.navi__button」は「element」となっており、共通するボタンのスタイルを定義します。「.navi__button_red」は「modifier」となっており、特定のスタイルを定義します。

.navi {
  border: 1px solid #ccc;
  padding: 16px;
  text-align: center;
  background-color: #fafafa;
}
.navi__button {
  width: 200px;
  padding: 8px 0;
  border: 1px solid #ccc;
}
.navi__button_red {
  background-color: red;
}

Sassを使う

sass/scss
Sass: Syntactically Awesome Style Sheets

Sassが使える環境であることを前提に書いていきます。Sassの環境構築や基本的な使い方などについては以下を参考にしてください。

Sassの「&」を使いこなす

BEMをSassで書く上で威力を発揮するのが「&」です。BEMのためにあると言っても過言ではないくらい重要な機能なので少々詳しく説明しておきます。「&」はJavaScriptで言う「this」のようなもので、親セレクタを参照するためのセレクタとなります。

例えばSassで「&」を使って以下のように書いたとします。

#main {
  color: black;
  a {
    font-weight: bold;
    &:hover { color: red; }
  }
}

コンパイルすると以下のようなCSSが吐き出されます。「&:hover」と書いたところですが、「&」が親セレクタの「a」を参照したことによって、「a:hover」に置き換わっていることがわかると思います。

#main {
  color: black;
}
#main a {
  font-weight: bold;
}
#main a:hover {
  color: red;
}

Sassの「&」を使ってBEMを一気に書く

Sassの「&」はBEMを書くときに大いに役立ちます。上で説明したようにBEMは、blockを起点として、elementとmodifierを__や_で繋げて書いていきます。つまり繋ぎ元と繋ぎ先はそれぞれ親と子の関係となるため、Sassの「&」がここで威力を発揮します。

論より証拠、以下の例を見てください。まず吐き出されるCSSから見ていきます。これをCSSで一から書くとなるとしんどいと思います。セレクタの文字数が多いので、ミスも出やすい状況にあります。

.block {
  color: #fff;
  width: 100%;
}
.block__element1 {
  width: 30px;
}
.block__element1_modifier1 {
  background-color: #ccc;
}
.block__element1_modifier2 {
  background-color: #111;
}
.block__element1_modifier2_modifier3 {
  color: #ddd;
}
.block__element1_modifier2_modifier4 {
  color: #ccc;
}
.block__element2 {
  width: 60px;
}

次に上記のCSSを吐き出す前のSassで書いたものを見てみると、どうでしょうか。何度も同じセレクタ名を書く必要がないため無駄がなく、シンプルかつ構造が把握しやくなっています。「&」が良い役割を果たしているのがわかると思います。

.block { /* block */
    color: #fff;
    width: 100%;
    &__element1 { /* element */
        width: 30px;
       &_modifier1 { /* modifier */
            background-color: #ccc;
        }
        &_modifier2 { /* modifier */
            background-color: #111;
            &_modifier3 { /* modifier */
                color: #ddd;
            }
            &_modifier4 { /* modifier */
                color: #ccc;
            }
        }
    }
    &__element2 { /* element */
        width: 60px;
    }
}

メンテナンスも非常にしやすくなっています。例えば、Block名を変更する場合でも、トップのBlock名だけを変えるだけで変更が済みます。またSassの「&」を使うことでElementやModifierの追加や入れ替えなども簡単に行えます。BEMを書くなら、Sassは外せないということがわかると思います。

Sassの機能をフルに使ってBEMを書く

以下のサンプルは、「ナビゲーションエリアに複数の色違いのボタンを配置する」ことを想定したBEMのスタイルをSassで書いたものです(便宜的にModifierの量を増やすために細かく指定しています。またマルチクラスなアプローチ向けの書き方で書いています)。Sassの「&」を使っていることで、シンプルで構造的に書けていることがわかると思います。これでも全然構いませんが、Sassの他の機能を使うと、もっとシンプルで無駄なく書けるようになります。そうしたSassの便利な機能をいくつか紹介していきます。

.navi {
    &_color {
        &-background {
            &-red {
                background-color: red;
            }
            &-gray {
                background-color: gray;
            }
        }
    }
    &__button1 {
        padding: 8px 0;
        width: 100px;
        &_color {
            &-background {
                &-red {
                    background-color: red;
                }
                &-gray {
                    background-color: gray;
                }
            }
            &-font {
                &-black {
                    color: black;
                }
                &-white {
                    color: white;
                }
            }
        }
    }
    &__button2 {
        padding: 16px 0;
        width: 300px;
        &_color {
            &-background {
                &-red {
                    background-color: red;
                }
                &-gray {
                    background-color: gray;
                }
            }
            &-font {
                &-black {
                    color: black;
                }
                &-white {
                    color: white;
                }
            }
        }
    }
}
navi.scss

@mixinで共通化

@mixinを使うと、スタイルの共通部分を外出しして一つの定義としてまとめておくことができます。定義した@mixinは、使いたい箇所で「@include」を使って呼び出します。@mixinは引数を持たせることもできるので、かなり柔軟に扱うことができます。

上記のサンプルの共通部分を@mixinを使って外出ししたものが以下となります。だいぶコードが短くなりました。

.navi {
    /* @mixiの定義 */
    @mixin __button($padding, $width) {
        padding: $padding;
        width: $width;
    }
    @mixin -background-color {
        &-background {
            &-red {
                background-color: red;
            }
            &-gray {
                background-color: gray;
            }
        }
    }
    @mixin -font-color {
        &-font {
            &-black {
                color: black;
            }
            &-white {
                color: white;
            }
        }
    }
    @mixin _color {
        @include -background-color;
        @include -font-color;
    }
    &_color {
        @include -background-color; /* @mixiの呼び出し */
    }
    &__button1 {
        @include __button(8px 0, 100px); /* @mixiの呼び出し */
        @include _color; /* @mixiの呼び出し */
    }
    &__button2 {
        @include __button(16px 0, 300px);
        @include _color; /* @mixiの呼び出し */
    }
}
navi.scss

繰り返し処理

Sassでは@each、@for、@whileといったループ処理を行う機能も備わっています。ここではリストを作って、そのリストをループさせて共通のスタイルを適用させるようにしています。BEMに活かすコツとしては、セレクタ名と値を同じものにしてリストを作ることです。

上記のサンプルにリストを使ったループ処理を当てはめてみました。今回はそれほどではなかったですが、リストの値が多くなればなるほど、効力を発揮すると思います。

.navi {
    @mixin -font-color {
        $color: black, white;
        &-font {
            @each $c in $color {
                &-#{$c} {
                    color: $c;
                }
            }
        }
    }
    @mixin -background-color {
        $color: red, gray;
        &-background {
            @each $c in $color {
                &-#{$c} {
                    background-color: $c;
                }
            }
        }
    }
    @mixin _color {
        @include -background-color;
        @include -font-color;
    }
    @mixin __button($padding, $width) {
        padding: $padding;
        width: $width;
        &_color {
            @include _color
        }
    }
    &_color {
        @include -background-color;
    }
    &__button1 {
        @include __button(8px 0, 100px);
    }
    &__button2 {
        @include __button(16px 0, 300px);
    }
}
navi.scss

SASS記法で書く

Sassはscss記法とsass記法と2つの書き方に対応しています。cssの書き方に近いscss記法が主流となっています。BEMを書くとネストが多くなるため、どうしても「{}」の量が増えてしまいます。そこでよりシンプルに書けるsass記法を用いて書いてみます。sass記法は、scss記法と比べて以下のような違いがあります。

  • {} → いらない
  • インデント → 必須(ブロックを表すため)
  • 文末の「;」 → いらない
  • 「:」の後の半角スペース → 必須
  • @mixin → 「=」で置き換え可能
  • @include → 「+」で置き換え可能
  • 拡張子 → .sass

上記のサンプルをsass記法で書くと以下のようになります。だいぶスッキリしました。

.navi
    =-font-color
        $color: black, white
        &-font
            @each $c in $color
                &-#{$c}
                    color: $c
    =-background-color
        $color: red, gray
        &-background
            @each $c in $color
                &-#{$c}
                    background-color: $c
    =_color
        +-background-color
        +-font-color
    =__button($padding, $width)
        padding: $padding
        width: $width
        &_color
            +_color
    &_color
        +-background-color
    &__button1
        +__button(8px 0, 100px)
    &__button2 
        +__button(16px 0, 300px)
navi.sass

以上をコンパイルすると以下のようなCSSが吐き出されます。

.navi_color-background-red {
  background-color: red;
}
.navi_color-background-gray {
  background-color: gray;
}
.navi__button1 {
  padding: 8px 0;
  width: 100px;
}
.navi__button1_color-background-red {
  background-color: red;
}
.navi__button1_color-background-gray {
  background-color: gray;
}
.navi__button1_color-font-black {
  color: black;
}
.navi__button1_color-font-white {
  color: white;
}
.navi__button2 {
  padding: 16px 0;
  width: 300px;
}
.navi__button2_color-background-red {
  background-color: red;
}
.navi__button2_color-background-gray {
  background-color: gray;
}
.navi__button2_color-font-black {
  color: black;
}
.navi__button2_color-font-white {
  color: white;
}
navi.css

Sassを使ったBEMのシングルクラス的な書き方

上記ではマルチクラス的な書き方でずっと紹介してきました。というのもその方が書きやすいからです。正直SassでBEMのシングルクラスを書くのは難しいです。自分もいろいろ試行錯誤しましたが、結局いい書き方が思い浮かびませんでした。ただ、完全にシングルクラスが書けないわけではないので、何となくこう書けばいいのかなという書き方を紹介しておきます。

@extendで継承

Sassは、あるセレクタに指定してあるスタイルを継承することができます。スタイルを継承するには「@extend」を使います。

例えば、以下のようにSassを書いたとします。fooとbaaの2つのセレクタがあり、barがfooを@extendしています。

.foo {
  width: 300px;
  height: 80px;
}
.bar {
  @extend .foo; /* .fooのスタイルを継承 */
  color: red;
}
sample.scss

上記をコンパイルすると以下のようなCSSが吐き出されます。ある要素のclassにbarを指定すると、fooとbarの両方のスタイルが適用されます。シングルクラスが実現できていることがわかると思います。

.foo .bar {
  width: 300px;
  height: 80px;
}
.bar {
  color: red;
}
sample.css

@extendを使ってBEMのシングルクラスを書く

上記をBEMに応用していきます。BEMでは「Element」の部分が継承元となるかと思います。ただSassの「&」を使って書いていると、@extendができません。そこで自分が考えた方法となりますが、継承元のスタイルをSassの「%」を使って以下のように書くようにします。(Elementと同じセレクタ名にして継承元を作っておくと分かりやすくなると思います。)

.navi {
  @mixin __button($width, $height) {
    width: $width;
    height: $height;
  }
  /* 継承元 */
  %__button {
    border: solid 1px #999;
    box-shadow: 0 2px 2px #aaa;
  }
  &__button {
    &_large {
      @extend %__button; /* %__buttonのスタイルを継承 */
      @include __button(300px, 80px);
    }
    &_small {
      @extend %__button; /* %__buttonのスタイルを継承 */
      @include __button(100px, 40px);
    }
  }
}
navi.scss

上記をコンパイルすると以下のようなCSSが吐き出されます。

.navi .navi__button_large, .navi .navi__button_small {
  border: solid 1px #999;
  box-shadow: 0 2px 2px #aaa;
}
.navi__button_large {
  width: 300px;
  height: 80px;
}
.navi__button_small {
  width: 100px;
  height: 40px;
}
navi.css

これくらいの規模だとシングルクラスもそれなりに書けますが、Modifierのパターンが増えるにつれ、コードを書く量が膨大になっていき、最終的には手に負えなくなると思います。他に効率的な書き方があればいいんですが、今の所自分は思い浮かびません…。

だからと言ってシングルクラスで書いてはいけないということではないので、マルチクラスと併用して臨機応変に最適な場所で使い分けていけばよいと思います。

BEMにおけるファイルの扱い

ファイル名のつけ方

BEMではBlock名をファイル名とすることが通例となっています。例えばBlock名が「navi」となっていれば、「navi.scss」とファイル名をつけます。Block名をファイル名としておくことで、Block名は一意のものとなり他のBlock名と衝突することが起こらなくなります。それ以外にも、ファイル単位でモジュール化させることができるので、機能ごとに開発もしやすくなります。

モジュール化と@import

Sassは「@import」を使うと外部ファイルを読み込むことができるようになっています。Blockごとにファイルを作りモジュール化させておくことで、必要なモジュールをインポートして使うという使い方ができるようになります。@importを使って読み込む際は、拡張子の前の部分だけ記述すればOKです。

@import "navi";

※読み込まれるファイル自身はコンパイルされる必要はないので、ファイル名の前に「_(アンダースコア)」をつけておくことで、コンパイルされないようになります。今回の例では「_navi.scss」とします。読み込む時は、アンダースコアをつけずに「@import “navi”;」でOKです。

まとめ

BEMで書かれたセレクタは一見複雑に見えて取っ付き難い感じもしますが、今回紹介してきたようにSassの様々な機能を駆使することで、シンプルに、そしてわかりやすく書くことができるようになります。Sassを使用している方であれば、ぜひこの投稿を参考にしていただいて、BEMを書いてみていただければと思っています。Sassの理解もだいぶ深まると思います。BEMに関しては、以下のサイトが参考になります。

CSSの設計思想はまだまだ発展途上段階にあると思いますが、今後もSassのようなCSSのプリプロセッサの進化と共に発展していくものと思っています。移り変わりの激しいフロントエンドの世界なので、BEMやその記述法の進化にも注目していきたいですね。併せて自分の中の方法論なんかもしっかりと確立していきたいですね。

最後に、CSS設計周りに関する本を紹介しておきます。BEMについても書かれています。

Web制作者のためのCSS設計の教科書
  • 『Web制作者のためのCSS設計の教科書 モダンWeb開発に欠かせない「修正しやすいCSS」の設計手法』
  • 著者: 谷 拓樹
  • 出版社: インプレス
  • 発売日: 2014年7月24日
  • ISBN: 4844336355

コメント一覧

  • 必須

コメント