LoginSignup
7
8

More than 3 years have passed since last update.

CSSのクラス名を書く前に最低限知っておくべきルール&BEMを改良してみた

Last updated at Posted at 2019-10-13

CSSはWebサービスの『ユーザー体験』を支える重要な技術で、CSSによってサービスのユーザビリティやメンテナンスコストが左右されると言っても過言ではありません。しかし、管理がずさんだったり、エンジニアがよく理解していないがために破綻しかけている現場を、筆者はこれまでいくつも見てきました。そこで、最低限これだけは知っておいて欲しいルール『詳細度命名規則』の2点についてなるべくわかりやすく解説します。

CSSが破綻するメカニズム

現場にCSS設計(ルール)がない状態で色んな作業者が思い思いにコーディングしていくと、いつしか「指定したのに反映されない」「修正したら他の画面が崩れた」「影響範囲が読めず修正できない」という状況に陥って、本来の開発が遅々として進まなくなることがあります。

CSSの強さはランク付けされている

そもそもCSSには以下のような「詳細度」が存在し、これに基づいて優先度が決定されています。

セレクタ・指定 SS S A B C
全称セレクタ(*) 0 0 0 0 0
疑似要素(::before) 0 0 0 0 1
要素型(div) 0 0 0 0 1
属性セレクタ([type="submit"]) 0 0 0 1 0
擬似クラス(:hover) 0 0 0 1 0
classセレクタ(.sample) 0 0 0 1 0
idセレクタ(#sample) 0 0 1 0 0
インラインスタイル(style="") 0 1 0 0 0
!important 1 0 0 0 0
  • それぞれのランクごとで計算し、上位のランクを超えることはできない(classセレクタを11個利用してもidセレクタ1個に勝てない)
  • 詳細度が等しい場合は、後に記述されたものが優先される

計算例

div               A=0 B=0 C=1  詳細度 =   1
ul li             A=0 B=0 C=2  詳細度 =   2
ul ol+li          A=0 B=0 C=3  詳細度 =   3
.sample div       A=0 B=1 C=1  詳細度 =  11
.sample .item     A=0 B=2 C=0  詳細度 =  20
#sample           A=1 B=0 C=0  詳細度 = 100
#sample.sample    A=1 B=1 C=0  詳細度 = 110
!important        SS=1       詳細度 = 10000

これらを理解できていないと、CSSを反映させるために試行錯誤して時間を費やし、最終的には「ええい!important付けちゃえ!」が横行します。importantが付いた要素はその後の修正ができなくなり、WEBサービスは破綻に向かって突き進むことになります。

『BEM』を改良してみた

CSSでは「クラス名」のスコープが全てグローバルなので、クラス名同士が激しく衝突します。このため、クラス名自体の命名を工夫して衝突を防ぐ必要があるのです。(Vue.jsなどのモダンフレームワークではscopedで擬似ローカルスコープにできますが、これも原理は同じで、フレームワークがセレクタ名を工夫することで衝突を避けています)
CSS設計(ルール)にはOOCSS、BEM、SMACSSなどたくさんありますが、一般に最も普及しているルールがBEMです。広く認知されているため、作業者のアサインがスムーズになったり、外注とのやりとりがしやすくなるといったメリットがあります。
しかし、BEMには「命名が冗長になる」というデメリットがあるため、今回はこれをより使いやすくカスタマイズして扱ってみましょう。

BEMとは

BEMは、CSSをBlock(塊)・Element(要素)・Modifier(状態)で管理する考え方です。DOMを機能や役割の「塊」ごとに分けてマークアップします。
id指定や!importantを禁止してclass指定だけで管理することで、詳細度のシガラミから解かれてシンプルに管理できるようになります。

先ず、それぞれの意味と使い方を説明します。

Block (ブロック)

  • 要素の「塊」
  • 機能や役割が判るように命名する ※ブロック名の命名が最重要です
  • ブロック名はアッパーキャメルケースを採用する(様々な命名を表現できますし、このブロックをしっかり命名することで、サードパーティcssとのコンフリクトを防いだり、以下の命名がシンプルになります)

Element (エレメント)

  • 塊の中にある各要素
  • とにかくシンプルに命名する
  • ブロック名でユニーク化できているので、Block__liくらいシンプルでもOK!

Modifier (モディファイ)

  • ブロックあるいはエレメントの「違う状態」を指定する場合に使用
  • 状態が判るように命名する
  • -redのようにハイフン始まりで単独のクラスとして指定する ※ハイフンの直後に数字は禁止、必ず英字にしてください
<div class="Block">
  <p class="Block__element"></p>
  <p class="Block__element -modifier"></p>
</div>

このように塊の親要素であるBlockを指定し、その中にElementを指定していきます。Elementの命名はBlock__elementのようにアンダーバー2つで繋ぎます。

マークアップ例

論より実物を見てみましょう。
スクリーンショット 2019-10-05 13.57.00.png
↑例えばこういったアイコンカードのようなパーツを作る場合、

<div class="IconCard">
  <img class="IconCard__icon" src="/images/taka.png">
  <p class="IconCard__name">たかたか</p>
</div>
//SCSS
.IconCard{
    display: flex;
    width: 200px;
    padding: 10px;
    border-radius: 10px;
    background: #90ee90;
    box-shadow: inset 0 -10px 10px rgba(0,0,0,.1),
    0 0 0 2px rgb(255,255,255),
    1px 1px 3px rgba(0,0,0,.3);
    align-items: center;
    @at-root{
        .IconCard__icon{
            overflow: hidden;
            width: 50px;
            height: 50px;
            margin-right: 10px;
            border-radius: 50%;
        }
        .IconCard__name{
            font-size: 16px;
            overflow: hidden;
            width: 140px;
            white-space: nowrap;
            text-overflow: ellipsis;
            color: #2c2f34;
        }
    }
}

このように IconCard というブロックを用意し、その中にIconCard__iconIconCard__nameというエレメントを記述します。クラス名から役割や機能が推測できるように命名します。

  • SCSSで @at-root を使っているのは、クラスの詳細度を低く抑えることでより管理しやすくするためです。
  • エレメントは&__icon&__nameのように省略することもできますが、.IconCard__iconと記述した方が視認性がよく管理しやすいです。

次に下記のように「説明文」を追加する場合どのようなマークアップになるでしょう?
スクリーンショット 2019-10-05 13.57.22.png

<div class="IconCard">
  <img class="IconCard__icon" src="/images/taka.png">
  <p class="IconCard__name">たかたかたかたかたかたかたかたか</p>
  <div class="IconCard__description">
    ホゲホゲホゲホゲホゲホゲホゲホゲホゲホゲホゲ
  </div>
</div>
//SCSS
.IconCard{
    font-size: 16px;
    display: flex;
    width: 200px;
    padding: 10px;
    border-radius: 10px;
    background: #90ee90;
    box-shadow: inset 0 -10px 10px rgba(0,0,0,.1),
    0 0 0 2px rgb(255,255,255),
    1px 1px 3px rgba(0,0,0,.3);
    align-items: center;
    flex-wrap: wrap;
    @at-root{
        .IconCard__icon{
            overflow: hidden;
            width: 50px;
            height: 50px;
            margin-right: 10px;
            border-radius: 50%;
        }
        .IconCard__name{
            font-size: 16px;
            overflow: hidden;
            width: 140px;
            white-space: nowrap;
            text-overflow: ellipsis;
            color: #2c2f34;
        }
        .IconCard__description{
            font-size: 16px;
            width: 100%;
            margin-top: 10px;
            padding: 10px;
            color: #2c2f34;
            border-top: 1px solid #708090;
        }
    }
}

IconCard__description エレメントが追加されましたね。
これもクラス名を見ただけで「IconCardブロックの中にある説明に関するエレメントだ」と一目で役割が判ります。

今度は背景が「黒い」バージョンを作りたい場合はどうするでしょう?
ここで登場するのが「Modifier」です。

<div class="IconCard -black">
  <img class="IconCard__icon" src="/images/taka.png">
  <p class="IconCard__name">たかたかたかたかたかたかたかたか</p>
  <div class="IconCard__description">
    ホゲホゲホゲホゲホゲホゲホゲホゲホゲホゲホゲ
  </div>
</div>
//SCSS
.IconCard{
    display: flex;
    width: 200px;
    padding: 10px;
    border-radius: 10px;
    background: #90ee90;
    box-shadow: inset 0 -10px 10px rgba(0,0,0,.1),
    0 0 0 2px rgb(255,255,255),
    1px 1px 3px rgba(0,0,0,.3);
    align-items: center;
    flex-wrap: wrap;
    &.-black{
        background: #000;
        .IconCard__name {
            color: #fff;
        }
        .IconCard__description {
            color: #fff;
        }
    }
    @at-root{
        .IconCard__icon{
            overflow: hidden;
            width: 50px;
            height: 50px;
            margin-right: 10px;
            border-radius: 50%;
        }
        .IconCard__name{
            font-size: 16px;
            overflow: hidden;
            width: 140px;
            white-space: nowrap;
            text-overflow: ellipsis;
            color: #2c2f34;
        }
        .IconCard__description{
            width: 100%;
            margin-top: 10px;
            padding: 10px;
            color: #2c2f34;
            border-top: 1px solid #708090;
        }
    }
}

IconCard ブロックの背景色が黒いバージョンを指定したいので、IconCard ブロックに対して -black クラス(Modifier)を付与します。
それに伴って IconCard セレクタ内に下記のスタイルが追加されていますね。

&.-black{
    background: #000;
    .IconCard__name {
        color: #fff;
    }
    .IconCard__description {
        color: #fff;
    }
}

IconCard-black クラスが付与されている場合は、背景を黒色(#000)にし、 IconCard__nameIconCard__description の文字色は白色(#fff)にする」という指定です。

今回は便宜的に -black としていますが、実務では -primary など要素の役割にもとづいて命名します。例えばサイトカラーなどをリニューアルして要素の色が青に変更された場合、 -black などの色の名前で指定していると全ての要素を -blue に変更する必要が出てきて今後の作業が大変になるからです。また、Modifierをアッパーキャメルケースで記述することも、ユニーク性や命名の多様性を高めるうえで有効です。

実際に触って感覚を掴んでください。Modifier(-blackクラス)を付けたり外したりするだけで、違う状態のBlockを再現できることが解るはずです。


See the Pen
BEMサンプル
by たかたか (@koutaka)
on CodePen.


Untitled presentation.png

このように、サイトのパーツを役割の「塊(Block)」で分けることにより、再利用しやすく管理しやすい運用が可能となります。
現場に命名規則がなかったり、すでにあるがうまく機能していない場合は、一度これらを参考に見直してみてはいかがでしょうか。

7
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
8