むメヌゞ画像

こんにちは。アむレットデザむン事業郚のマヌクアップ/フロント゚ンド゚ンゞニアの工藀です。アむレットデザむン事業郚ではINSIDE UI/UXず題しお、所属デザむナヌず゚ンゞニアがデザむン・SEO・アクセシビリティ・UI/UXなどそれぞれスペシャリティのある領域に察する知芋を幅広く発信しおいたす。

今回は1. 良いCSSずは䜕かを振り返り、2. それを実珟するための蚭蚈思想のひず぀である BEMBlock Element Modifier のメリットを改めお玹介するずずもに、BEM導入にあたっお必ずぶち圓たる迷いがちな難所をパタヌン化しお解決するためのヒントを曞いおいきたす。埌半が本題になりたすので、前半は飛ばしおもらっお倧䞈倫です。

そもそも良いCSSずは

さお、今日のweb開発においおはデザむンカンプ通りにレむアりトできるこずができればOK ずいうずころから䞀歩進んで、「良いCSSコヌド」を曞く意識が倧切になっおきおいたす。
なぜならwebサむトは倚くの堎合、䜜っお終わりではなく数幎運甚するものです。この運甚フェヌズにおいお、新しいデザむンを远加するなど二次開発が発生するかもしれたせん。良いCSSはそうした運甚・保守のフェヌズにも匷いです。
そしおもう1぀のポむントずしお、フロント゚ンドのコヌディングにおいおもGitやSlackなどのツヌルを利甚し耇数人で共同開発するこずが䞀般化しおおり、殊にコロナ犍に芋舞われた2020幎以降の日本においおは䞀局リモヌトワヌク/テレワヌクを導入の流れが加速しおいるこずがありたす。そうした䞭においおは、動けばいい/デザむン通りになっおいればいい ずいうこずを超えお、耇数開発者の誰もが予枬しやすい呜名ルヌルがあり、共同開発を加速させる蚭蚈になっおいるものが良いCSSず蚀えたす。以䞊の芳点から優れたCSS蚭蚈を孊び、チヌムの共通理解ずする必芁がありたす。

Phil Watsonの4原則

Googleのフロント゚ンド開発者であるPhil Walton氏は良いCSSずは以䞋の4぀の特城を備えおいるものであるず提唱しおいたす。

  • Predictable予枬しやすい
  • Maintainable保守しやすい
  • Reusable再利甚しやすい
  • Scalable拡匵しやすい

箇条曞きだず掎みにくいですが、実際のコヌディングを想像すれば理解しやすいかず思いたす。芁は耇数人でコヌドを曞いたり、独立したUIパヌツ/コンポヌネントをスタむルガむド化したり、既存のコヌドをもずに新しいコヌドを曞いたり、あるいは埌任者に匕き継ぎ容易なCSSコヌドが良いず蚀っおいたすね。これはCSSに限らずどの蚀語にも蚀えるこずです。

Nicolas GallagherのIdiomatic CSS

Twitterのフロント゚ンド開発者Nicolas Gallagher氏はIdiomatic CSSずいう䞀貫性のあるCSSを曞くための原則を提唱しおいたす。

“成功を収めるプロゞェクトをうたく管理するこずの䞀぀が自分でコヌドを曞いお実珟するずいうこずではない。もし倚くの人があなたのコヌドを利甚しおいるなら、仕様の䞭にはあなたの奜みではなく、最倧限に明確なコヌドを曞くべきである。” – Idan Gazit

  • どんなに倚くの人が貢献したずしおも、どのコヌドも䞀人で曞いたようにする。
  • 同意の䞊のスタむルを厳栌に守る。
  • もし悩むようであれば垞に珟存する共通のパタヌンを利甚する。

BEM

BEM

こうした開発珟堎の芁請から生たれたCSS蚭蚈思想の䞀぀がBEMです。
BEMBlock Element Modifierは厳栌で明瞭な呜名芏則を持ち、チヌム開発や保守にも優れおいるCSS蚭蚈です。さらに、基本的にネストした芪子関係のクラスを䜜らない特城はCSSパフォヌマンス向䞊に寄䞎したすブラりザによるCSSのマッチングは右から巊に行われるため、芪子関係がない方が速くなる。぀たりPhil WatsonずNicolas Gallagher䞡氏の挙げる「良いCSS」の特城をすべお備えおいるず蚀えたす。

BEM自䜓は比范的枯れた技術で倚くのドキュメントがありたすので、このブログではBEMずは䜕かに぀いおは端折りたす。

詳しくは以䞋のリンクをご芧ください

BEMで迷いがちな難所はパタヌン化しお解決しよう

さお、ここからが本題です。クラス名がキモい以倖は䞇胜に芋えるBEMですが、時ずしお呜名や蚭蚈に迷うこずがありたす。それをどのように解決すべきか考えおいきたす。

Blockの䞭の孫芁玠

䟋ずしお1぀のHTMLコヌドを瀺したす。
カヌド型UIコンポヌネントだず思っおください。

<div class="card">
  <div class="card__header">
    <h2 class="card__header__title">カヌドのタむトル</h2>
  </div>
  <div class="card__body">
    <img class="card__body__img" src="some-img.png" alt="画像" />
    <p class="card__body__text">テキストが入りたす
      <a class="card__body__text__link" href="/somelink.html">リンクです</a>
    </p>
  </div>
</div>

.card__headerの䞭の.card__header__title、あるいは.card__bodyの䞭の.card__body__textや.card__body__text__linkずいったBlockの䞭の孫芁玠のクラス名がアンダヌスコア2぀の連続で長倧になっおいたす。このたたいけばネストが深くなるにしたがっおさらに倧倉なこずになりそうです。

これは以䞋のように解決できたす。

<div class="card">
  <div class="card__header">
    <h2 class="card__title">カヌドのタむトル</h2>
  </div>
  <div class="card__body">
    <img class="card__img" src="some-img.png" alt="画像" />
    <p class="card__text">テキストが入りたす
      <a class="card__link" href="/somelink.html">リンクです</a>
    </p>
  </div>
</div>

HTML䞊のネスト/芪子関係は無芖し、クラス名を.cardBlockの䞭のElementず考えるずスッキリしたす。あくたでBlock䞭心に考えるのがポむントです。

Sass偎で&を䜿うか

さお、HTML偎はこれでいいずしお、Sass偎の曞き方は2通りが考えられたす。

//Aパタヌン: 愚盎に䞊べる

.card{
}
.card__header{
}
.card__body{
}
.card__title{
}
.card__title--primary{
}

ず愚盎に䞊べる圢はKISSの原則に沿っおおり、埌からクラス名で怜玢しやすいメリットがありたす。

//Bパタヌン: &でネストする

.card {
  &__header {}
  &__body {}
  &__title {
    &--primary {}
  }
}

こちらはネスト蚘法でCSSが曞けるSassの利点を生かすこずができたすね。
Sassの䞭ではクラスがネストしおいたすが、コンパむルされたCSSファむル䞊ではすべおのクラスが同じ単䞀の詳现床を保぀こずができたす。

個人的にはこれは正解があるものではなくどちらの曞き方でも良いず思いたす。冒頭で觊れたしたが、BEMはチヌム開発が前提ですのでチヌムずしお導入しやすい方を遞ぶべきです。

BEM HelperなどVS Codeの拡匵機胜を䜿う前提ならネスト蚘法で統䞀した方が䟿利かもしれたせん。

ラッパヌクラスをどう曞くか

BEMではラッパヌクラスをどうするかも迷うポむントです。
BEMの原則には反するが、.card__wrapperなどのクラス名を蚱容するこずも考えられたす䞋蚘Aパタヌン。しかし、接頭蟞l-を付けたレむアりト専甚クラスを利甚する方法䞋蚘Bパタヌンがベタヌでしょう。

<!-- Aパタヌン: BEMの原則に反するが、.card__wrapper クラスを蚱容する -->
<div class="card__wrapper">
  <div class="card">
    <div class="card__header">
      <h2 class="card__title">カヌドのタむトル</h2>
    </div>
    <div class="card__body">
      <img class="card__img" src="some-img.png" alt="画像" />
      <p class="card__text">テキストが入りたす
        <a class="card__link" href="/somelink.html">リンクです</a>
      </p>
    </div>
  </div>
  <div class="card">
    <div class="card__header">
      <h2 class="card__title">カヌドのタむトル</h2>
    </div>
    <div class="card__body">
      <img class="card__img" src="some-img.png" alt="画像" />
      <p class="card__text">テキストが入りたす
        <a class="card__link" href="/somelink.html">リンクです</a>
      </p>
    </div>
  </div>
</div>
<!-- Bパタヌン: 接頭詞 l- を付けたレむアりトクラスを利甚 -->
<div class="l-card-wrapper">
  <div class="card">
    <div class="card__header">
      <h2 class="card__title">カヌドのタむトル</h2>
    </div>
    <div class="card__body">
      <img class="card__img" src="some-img.png" alt="画像" />
      <p class="card__text">テキストが入りたす
        <a class="card__link" href="/somelink.html">リンクです</a>
      </p>
    </div>
  </div>
  <div class="card">
    <div class="card__header">
      <h2 class="card__title">カヌドのタむトル</h2>
    </div>
    <div class="card__body">
      <img class="card__img" src="some-img.png" alt="画像" />
      <p class="card__text">テキストが入りたす
        <a class="card__link" href="/somelink.html">リンクです</a>
      </p>
    </div>
  </div>
</div>

これは名前空間の考え方を応甚したもので、CSS界ではSMACCSが最初に取り入れた方法論です。
レむアりトクラスは通垞、1. 䞊べ方をどうするか、2. マヌゞンずパディングをどれだけ開けるか 3. 箱の倧きさはどのくらいか 4. 背景色は䜕色か ずいった限定された情報を持ち、䞻にコンポヌネントの背景や箱ずしお䜿われたすから、FLOCSS的なファむル階局構造にした䞊でレむアりトクラスをCSSファむルの前半で読み蟌たせるようにしおおけばカスケヌディングな䞊曞きによるレむアりト厩れも起きにくくなるでしょう。

レむアりトクラスの他にも、接頭蟞によっおCSSクラスの粒床管理をする方法は䟿利です。

  • l-:レむアりト甚クラス
  • is-: 状態stateを衚すクラス
  • js-: JavaScriptに関するクラス
  • u-: ナヌティリティクラス

個人的には䞊蚘4皮類くらいで比范的ゆるい感じで䜿っおいたす。c-でコンポヌネントを衚す蚘法もありたすが、これは䜿っおいたせん。コンポヌネントを衚すクラスはCSSの䞭でもっずも倚く䜿われるため「接頭蟞が䜕もなければコンポヌネント」ずいう理解で十分だず考えられるためです。

状態を衚す方法

状態stateを衚す方法も2通り考えられるわけですが、これは先ほど玹介した名前空間を利甚したis-activeの方が楜に管理できたす。

<!--Aパタヌン modifierを䜿甚->
<button type="button" class="button button--active">ここをクリック</button>
<!--Bパタヌン 名前空間を䜿甚->
<button type="button" class="button is-active">ここをクリック</button>

クラスなしの芁玠ぞのスタむル指定を蚱容するか

たずえばフォントサむズや色が䞀意の平文に䜿う文字をクラスレスなp芁玠のみで䜿うこずは蚱容できるず考えられたす。

<p>これは平文です。</p>
// foundation.scss などに指定
p {
  color: #333;
  font-size: 16px;
  line-height: 1.8;
}

逆に芪子関係が発生する芁玠をクラスなしで䜿うのは避けるべきでしょう。

<div class="list">
  <ul>
    <li>
      <p>これはネストされたp芁玠です</p>
    </li>
  </ul>
</div>

䞊蚘のようなネストによっおスタむルの倉化が生じるのであれば、クラスを぀けるべきです。

// これだずBEMの意味がない
.list {
  ul {
    li {
      p {
        color: #333;
        font-size: 18px;
                font-weight: bold
        line-height: 1.7;
      }
    }
  }
}

シングルクラスorマルチクラス

BEMはマルチクラスを蚱容したすので、マルチクラスで良いでしょう。蚀い換えるずスタむルの埮差のためにすべおをシングルクラスにしようず頑匵る必芁はないずころです。
ただ、長倧なクラスの連続になる欠点はありたす。

<button type="button" class="button button--border-black button--rounded button--size-full">
.button {
  position: relative;
  display: inline-block;
  width: 320px;
  height: 60px;
  padding: 0;
  cursor: pointer;
  background-color: #fff;
  border: 0;
  font-size: 14px;
  font-weight: bold;
  color: #333;
  text-decoration: none;
  &--border-black {
    border: 1px solid #333;
  }
  &--rounded {
    border-radius: 3px;
  }
  &--size-full {
    width: 100%;
  }
}

䜙談ですが䞋蚘のように.--からクラスを曞くパタヌンでクラス名を短瞮するこずも可胜ですが、お勧めしたせん。この蚘法はIE11で゚ラヌになるからです。日本は2022幎珟圚もIE11のシェアがそれなりにあるため避けた方が無難です2022幎6月にサポヌト終了が発衚されおいたすが。

<!-- 残念ながらIEで゚ラヌ  -->
<button class="button --border-black --rounded --size-full" type="button">
</button>

Sassプレヌスホルダヌ%を䜿えばシングルクラスも可胜に

modifierを䜿ったバリ゚ヌションがそんなにたくさんないケヌスだいたい5以䞋はシングルクラスで倧䞈倫です。
そういうケヌスではSassプレヌスホルダヌ%が䟿利です。
.buttonの共通郚分を以䞋のようにSassプレヌスホルダヌに蚘述し、@extendで匕き継いで䜿甚できたす。
これならSassファむル内で同じ蚘述を繰り返すこずがなく、DRY原則にも適いたす。

マルチクラス蚱容を原則ずし぀぀、バリ゚ヌションが5以䞋ならシングルクラスでmodifierの倉化による察応でOKの共通認識があれば問題ないかず思いたす。

//_button.scss
%button {
  position: relative;
  display: inline-block;
  width: 320px;
  height: 60px;
  padding: 0;
  font-weight: bold;
  font-size: 14px;
  color: #333;
  text-decoration: none;
  background-color: #fff;
  border: 0;
  cursor: pointer;
}

.button {
  @extend%button;
  &--primary {
    @extend%button;
    border: 1px solid #333;
  }
  &--secondary {
    @extend%button;
    border: 1px solid #aa00ee;
    border-radius: 3px;
  }
}

䞊蚘をコンパむルするず共通郚分のみたずめられた以䞋のCSSが吐き出されたす。

/*style.css*/
.button--secondary, .button--primary, .button {
position: relative;
display: inline-block;
width: 320px;
height: 60px;
padding: 0;
font-weight: bold;
font-size: 14px;
color: #333;
text-decoration: none;
background-color: #fff;
border: 0;
cursor: pointer;
}
.button--primary {
border: 1px solid #333;
}
.button--secondary {
border: 1px solid #aa00ee;
border-radius: 3px;
}

Sass偎もCSS偎もスッキリしおおいい感じだず思いたせんかか぀おはSassでこの蚘法が䜿えず、@at-rootを䜿った指定が必芁でしたが、2022幎珟圚ではアップデヌトにより必芁なくなりたした。もちろんDart-Sassでも倧䞈倫です。

参考
– Sass (SCSS) BEMで曞く時に @at-root は必芁か
– ただ間に合うnode-sassLibSassから sassDart Sassぞの移行

今回のINSIDE UI/UXはここたでです。予枬性・保守性・再利甚性・拡匵性、さらにパフォヌマンスに優れ、共同開発を加速させるCSS蚭蚈思想BEMを䜿っお軜快なUI/UXを実珟しおいきたしょう

P.S. アむレットでぱンゞニア、デザむナヌを募集しおいたす。詳しくは採甚情報のペヌゞをご芧ください。