シナプス技術者ブログ

シナプスの技術者公式ブログ。インターネットで、鹿児島の毎日を笑顔にします。

CSS設計手法「BEM」について

技術部システム開発課の田畑です。よろしくお願いします。
今回はCSS設計手法であるBEMについて書きたいと思います。

最近はBEMを使ってCSSを記述していた(つもり)だったのですが

  • 自己流でやってしまっていた部分があった
  • BEMの目指すところは何か理解が足りなかった

という理由からBEMをもう一度学習しなおすことにしました。

今回学習をすすめるにあたり、以下の本を参考にしました。 具体例が多くとても分かりやすかったのでおすすめいたします。

gihyo.jp

なぜBEMを使うのか

シナプスのホームページは共通CSSとページ別のCSSを読み込ませることでスタイル指定を実現しています。 ところがページ数が大変多く、長年運用しつつページを継ぎ足しながらやってきたため、CSSの数も多くなり以下のような問題が発生しております。

  • スタイルの優先順位が複雑に絡んでおり思い通りにならないことがある
  • 別の場所に流用すると表示が崩れる
  • 制作者にしかわからないクラス名
  • 制作時期によって違う記述方法
  • 使いまわしがしづらいスタイル指定

この問題を解決するためBEMというCSS設計手法をとりいれることになりました。

BEMとは

BEMとは、Block、Element、Modifierの略語です。Webサイトのコンポーネント化のためのフロントエンド設計方法のひとつで、厳格なクラス名の命名ルールが特徴的な手法です。

引用:BEMによるフロントエンドの設計 | 第1回 基本概念とルール | CodeGrid

それぞれの役割は以下のようになります。

Block

使いまわせるパーツ・モジュール。 独立性を保持するため、レイアウトに関する指定はしないこと。 見た目ではなく「何のためにつかわれるのか」を表す命名をする。

Element

Blockを構成しているもの。Block外では独立して使えない。 Block内での構成が変わる可能性があるのでElementの中にElementをネストしない。

Modifier

BlockやElementの状態や見た目を定義する。 Modifierは単独で利用することはできない、BlockかElementのクラスがある状態で使う。

このルールに則り記述していくことで、前述の問題が解消されることが期待できます。

BEMの記述方法

classの書き方

Block、Element、Modifierのそれぞれの区切りに一貫したセパレーターを使います。

block__element--Modifier

※上記はMindBEMdingの命名規則です。シナプスではこちらを使っていきます。

  • BlockとElementの区切りはアンダースコア2つで繋げる
  • BlockもしくはElementとModifierの区切りはハイフン2つで繋げる
  • Block、Element、Modifierを2つ以上の単語で表す場合の単語と単語の区切りはハイフン
  • Modifierのキーは省略可能

Mixについて

HTML要素に異なる複数のクラスをつけることで、複数のスタイルを付与することができます。 「Blockに指定したいスタイル」「Elementに指定したいスタイル」を分離して組み合わせて適用できるので、再利用性を高めることができます。

良いCSSが目指す4つのポイント

良いCSS(BEMなど)が目指していることは何かというと、この書籍の中で以下のことがあげられていました。

  • 予測できる
  • 再利用できる
  • 保守できる
  • 拡張できる

引用:CSS設計完全ガイド ~詳細解説+実践的モジュール集:書籍案内|技術評論社

つまり

  • スタイリングが期待通りに動く、意図しない場所に影響を与えない
  • コードが使い回せる
  • 新しい機能を追加したときに影響を与えたり、壊れたりしない
  • ルールがしっかり決まっており複数人で管理ができる

ということだそうで、今CSS設計で困っている部分をうまく解消できそうです!

CSS設計の8つのポイント

では実際にどのようなことに気を付けて設計していくかというと、以下の内容になります。

  1. 特性についてCSSを分類
  2. HTMLとスタイリングが疎結合である
  3. 影響範囲がみだりに広すぎない
  4. 特定のコンテコストにみだりに依存しない
  5. 詳細度がみだりに高くない
  6. クラス名から影響範囲が想像できる
  7. クラス名から見た目・機能・役割が想像できる
  8. 拡張しやすい

引用:CSS設計完全ガイド ~詳細解説+実践的モジュール集:書籍案内|技術評論社

具体的には

  • HTMLの要素名をセレクターとして利用しない。
  • スコープを絞って影響範囲を狭くする。
  • 利用場所を変えたらスタイルが効かなくなるのを避ける。
  • idをスタイルに利用しない。クラスセレクターを使う。
  • HTMLの影響範囲がCSSのスタイリングと一致する。
  • 影響範囲が想像できるクラス名をつける。
  • マルチクラス設計を使う。

などに注意してコーディングしていきます。

コーディングしてみる

では実際にシナプスホームページ内の組織向けページのコンテンツ部分をBEMでコーディングしてみます。

f:id:neko-pon:20210616093021p:plain
組織専用メニュー - 鹿児島のプロバイダSYNAPSE(シナプス)

まずはページを構成する要素を内容ごとにブロックに分けます。

  • 役割が想像できる名前を利用する

を念頭に置いて名前をつけていきます。

f:id:neko-pon:20210616094531p:plain

これをHTMLで書き出した後、CSSで整えていきます。

HTML

<div class="content content--column">
    <main class="main content__main">
        <div class="main__inner">
        <h1 class="title">組織専用メニュー</h1>
        <div class="pr-banner pr-banner--center">
            <p class="pr-banner__text">法人向けモバイルサービスAcrobizMobile お客様の~即日利用OK!</p>
            <img class="pr-banner__img" src="">
        </div>
        <div class="service-intro service-intro--recommend">
            <h2 class="service-intro__title">ご利用シーンに合わせたおすすめのサービス</h2>
            <ul class="service-intro__list">
                <li class="service-intro__item">1.独自ドメイン導入&複数拠点インターネット接続</li>
                <li class="service-intro__item">2.VPN導入&複数拠点で社内サーバー共有</li>
            </ul>
        </div>
        <div class="service-intro">
            <h2 class="service-intro__title">独自ドメインサービス</h2>
            <ul class="service-intro__list">
            <li class="service-intro__item">ホスティング・プラス</li>
            <li class="service-intro__item">ホームページ改ざん検知サービス</li>
            </ul>
        </div>
        </div>
    </main>
    <aside class="side content__side"></aside>
</div>

CSS

.content--column{display: flex;}
.main__inner{margin: 15px;}
.pr-banner--center{text-align: center;}
.pr-banner__text{margin: 10px;}
.pr-banner__img{
    border: #a8a8a8 1px solid;
    margin: 10px;
}
.main {
    width: 766px;
    border: 1px solid #a8a8a8;
    box-shadow: 1px 1px 1px rgb(0 0 0 / 20%);
}
.side {width: 231px;}
.title{
    font-size: 2em;
    color: #ff8000;
    margin-bottom: 2em;
    padding: .5em;
    border-top: 1px dashed #999999;
    border-bottom: 1px dashed #999999;
}
.service-intro--recommend .service-intro__title{
    margin: 10px auto 15px auto;
    color: #FFFFFF;
    font-weight: normal;
    padding: 5px 0 5px 15px;
    border-left: solid 7px;
    background-color: #336600;
    border-color: #568A0F;
}
.service-intro--recommend .service-intro__list{
    display: flex;
    width: 80%;
    margin: 0 auto 15px auto;
}
.service-intro--recommend .service-intro__item{
    list-style-type: none;
    background-color: #DAF0D2;
    width: 45%;
    display: block;
    margin-bottom: 30px;
    margin-right: 5%;
    padding: 10px;
    background-position: 95% 50%;
    border: solid 1px #92C459;
}
.service-intro--recommend .service-intro__item:last-child {
    margin-right:0;
    border-bottom: solid 1px #92C459;
}
.service-intro__title{
    margin: 10px auto 15px auto;
    color: #FFFFFF;
    font-weight: normal;
    padding: 5px 0 5px 15px;
    border-left: solid 7px;
    background-color: #2F48A1;
    border-color: #3986DB;
    font-size: 1.3em;
}
.service-intro__list{
    width: 80%;
    margin: 0 auto 15px auto;
}
.service-intro__item{
    border-bottom: dashed 1px #777777;
    padding: 10px 0;
    list-style-type: none;
}
.service-intro__item:last-child {border-bottom:none;}

以下のようなことに気を付けて設計しました。

  • idではなくclassで指定する
  • pr-banner、service-introなど役割が想像できる名前を利用する
  • 影響範囲が想像できるクラス名を付ける
service-intro__title
service-intro__list

など

  • service-introのMix(service-intro--recommend)を作成、サービス紹介の中からおすすめとして取り上げているブロックのデザインを変える
  • contentのMix(content--column)で左右にカラムをわけた
  • HTMLの構造に依らないクラス名にした
service-intro__list
service-intro__item

など

上記の内容は組織向けページのみを見て設計したものになります。 そのため全体的な構成を考えて設計した場合には、違ったものになる可能性もあります。

まとめ

今回シナプスの組織向けページを設計してみて、実際にコーディングしてみると迷う部分が多々あり難しいなと感じました。迷わず設計できるようになるにはしばらくかかりそうです。 BEMを導入することで綺麗なCSSを維持できるようにしていきたいです。