本付録は、メインシリーズであるWordPressのテーマ作成に役立ちそうな情報を補足していくパートだ。
前回は、手法の一つであるSMACSSを紹介した。
前々回のOOCSSからだいぶ具体的になった…のだが、これでも細かいところはわりと柔軟で、逆に言えば大規模だとそこも厳密に見ておかないと後で大変なことになる。
小規模であればこれでも組むことは十分可能なので、感覚を掴みたい場合はまずは1ページでやってみるといいだろう。
なお、実践的なサンプルをほとんど出していないが…実際には、付録の後半で手法の一つだけを実際に使ってみるつもりだ。
で、それをWordPressのテーマ作成時にも使う設計としたい。
まあ、それがそもそもこの付録をやっている目的なので、ご了承願いたい。
以下がその記事だ。
さて、今回はBEMという設計手法を紹介しよう。
これはOOCSSのようにモジュールを基本とした考え方なのだが、かなり細かいところまで決められており、制約も色々と強い。
学習コストは多少高いかもしれないが、それに見合う効果もあるものだ。
まずは基本となる考え方があるのでそれについて触れ、その後それぞれの詳細を見ていこう。
ちょっと全量だと一度に全ては多すぎるので、2回に分けることにする。
基本となる部分は今回で解説しきるので、まずは基本をしっかり理解しておきたい。
なお、本付録は以下の本を参考にしている。
気になる方は、実際に本屋で中身を見てみたり、買ってみたりしよう。
BEM
BEMとはBlock、Element、Modifierそれぞれの頭文字を取ったもの。
公式サイトは以下だ。
…実は、BEMでは単なるCSS設計だけでなく、他にもかなり広範囲をカバーしている。
しかし、それらを全て解説しようとすると恐らくシリーズ丸々一個出来上がってしまうだろう。
そのため、やはり参考書に併せて解説させてもらう。
考え方を大雑把に書くと、それぞれの部品を独立したブロックとして作り、それを組み合わせてページを作っていく。
で、名前の通りそれぞれの設定を三つの単位に分解して定義する。
- Block
- Element
- Modifier
この3つをまとめてBEMエンティティと呼ぶ。
BEMエンティティの詳細に入る前に、全体で共通して考えなければいけない原則があるので、そこから紹介していこう。
使用するセレクタ―と詳細度
当てはまる考え方は二つ。
- 2つ目:HTMLとスタイリングが疎結合である
- 5つ目:詳細度がみだりに高くない
BEMでは、基本的にクラス一つのセレクターを使うことを前提としている。
これは詳細度をなるべく均一に保つためで、後で触れるModifierやMixというものによって上書きをしやすくすることを目的としている。
もちろん例外はあるのだが、前提としてはクラスセレクタ―の使用と詳細度の均一があることを覚えておいて欲しい。
なお、HTML側で一つの要素に複数のクラス名がつくことはあるので、その違いには注意だ。
命名規則
それぞれのBEMエンティティのクラスにも命名規則はあり、例えばBlockはこれ、Elementはこれ、という形で定義されている。
その中には、該当のBEMエンティティ自身の名前も入る。
ここで説明したいのは、そのBEMエンティティ自身の名前だ。
具体例はそれぞれで出すが、基本的にはハイフンケースを用いる。
全て小文字で、複数単語ある場合はそれらをハイフン一つで繋げるタイプだ。
BEMエンティティ
以上の原則を踏まえた上で、BEMエンティティの詳細へ。
ここからは、考え方としては色々絡みすぎてて、それぞれで提示すると余計混乱しかねない。
そのため、先に関連する考え方を出しておく。
- 4つ目:特定のコンテキストにみだりに依存していない
- 5つ目:詳細度がみだりに高くない
- 6つ目:クラス名から影響範囲が想像できる
- 7つ目:クラス名から見た目・機能・役割が想像できる
- 8つ目:拡張しやすい
それぞれの内容が、どれに当てはまるかも考えながら進めていくとより理解しやすいだろう。
では順番に、まずはBlockから見ていこう。
Block
Blockは、再利用可能な独立した機能を持つコンポーネントと定義されている。
要するに、どこでも使える、他に影響されない要素だと思ってもらっていいだろう。
で、早速ポイント。
どこでも使える、というのを実現するために、Blockにはそれ自身に対するレイアウト関連のスタイルは設定してはいけない。
例えば、position
やfloat
、margin
などがこれに該当する。
レイアウトはどうするかというと、後で解説するMixというものを使うので一旦は気にしないでおこう。
命名規則は非常にシンプルで、それが何を表しているかをそのままハイフンケースで指定する。
例えば、メニューのブロック。
単なるメニューであればmenu
であったり、ヘッダー用のメニューであればheader-menu
といった感じだ。
今、それが何を表しているかで名前をつけると書いたが、ここも注意が必要だ。
例えば、何かしらのエラーを表示する領域。
エラーなので、通常は赤い文字にしたいだろう。
この時、そのクラス名としてtext-red
としてはいけない。
text-red
で分かる情報は、文字が赤いというだけ。
つまり、これだとそれがエラーに関する表示だということが分からなくなってしまう。
今はエラーを表示したいので、text-error
やmsg-error
、あるいは単にerror
の方が適している。
Element
二つ目のElementにも明確な定義がある。
それは以下二つを満たすものであること。
- Blockの中に存在し、そのBlockを構成するもの
- Blockの外では独立して使用できないもの
この二つ目の定義から、Elementは必ずBlock内に配置しなければいけない。
しかし、Blockの中にElementが必要なければ、無理に作る必要もないので覚えておこう。
具体例として、メニューを分解してみよう。
メニューを一つのBlockとし、中に含まれる各メニュー項目はそのメニューの中でしか使えない、と仮定して考えてみる。
この時は、この各メニュー項目がElementに該当する。
命名規則は、そのElementが含まれるブロック名から始まり、アンダースコアを二つ挟んでそのElementの名前をつける。
ここでもBlockと同じく、そのElementが何者かを表す名前をつけること。
さっき出したメニューの例で見てみると…
<ul class="menu">
<li class="menu__item">メニュー項目1</li>
<li class="menu__item">メニュー項目2</li>
<li class="menu__item">メニュー項目3</li>
</ul>
こんな形になる。
この時はmenu
がBlock名、item
がElement名なので、クラス名がmenu__item
となっている。
もちろん、このElement名が複数単語ならハイフンケースだ。
さて、ここで一つ注意。
ElementはHTML側でネストすることができる。
言い換えると、あるElementの中に、別のElementが存在することは何も問題ない。
実際、上の例でそれぞれのメニュー項目にリンクを貼りたい場合、そのリンクをElementとして入れることができる。
さらにその数は制限されておらず、必要ならいくつでもネストしていい。
…のだが、命名についてはネストしてあることを名前に含ませることは推奨されていない。
理由は、そのBlockの構造が変わる可能性があるからだ。
もしElementがネストされていることを名前に含ませてしまうと、それ以外では使えないことになってしまう。
つまり、例えば元はあるElementを別のElementの中で使っていたが、そのElementをBlockの直下に移動したい、といった場合に対応できなくなるのだ。
これを踏まえると、メニューの各項目にリンクを貼る場合は…
<ul class="menu">
<li class="menu__item"><a class="menu__link">メニュー項目1</a></li>
<li class="menu__item"><a class="menu__link">メニュー項目2</a></li>
<li class="menu__item"><a class="menu__link">メニュー項目3</a></li>
</ul>
こんな形が推奨されることになる。
同時に、構造がネストしている時はCSSのセレクター側も注意が必要になる。
上の例でElementであるmenu__item
やmenu__link
のスタイルを定義するとき、セレクターでは子孫セレクターや子セレクタ―などは使わない。
やはり詳細度は均一が基本なので、以下のようなる。
/* 上三つはOK */
.menu {
/* メニューBlockに対する設定 */
}
.menu__item {
/* メニュー内の項目Elementに対する設定 */
}
.menu__link {
/* メニュー項目内のリンクElementに対する設定 */
}
/* 以下の形は全てNG */
.menu .menu__item { ... }
.menu .menu__link { ... }
.menu_item .menu__link { ... }
.menu .menu__item .menu__link { ... }
ちなみに、今ネストの話をしたのでBlock側のネストも補足しておく。
これも、あるBlockの中に別のBlockが入ること自体は何も問題ない。
そこの詳細は次回の後編で解説するが…じゃあある部品をBlockとElementのどちらにするか、という問題が生じてくる。
例えば、以下のような形。
<header class="header">
<a class="header__btn">リンク</a>
</header>
今はヘッダーBlockの中にボタンElementを持つ形で使用している。
が、このボタンが本当にElementでいいのだろうか、という部分だ。
これについては、一つの基準としてはそのBlock外でも使う可能性があるかどうかで考えるといいだろう。
Blockの定義は再利用可能な独立した部品、Elementの定義はBlockを構成するそれ単体では使えない部品だった。
そのため、他でも使う可能性があるならBlockとして、使わないならElementとして定義すればこの両方を満たせる。
まあ、参考書でもこれが一つの方法だと書いてあるので、この判断基準でなければいけないわけではない。
本シリーズはこの基準を使うが、他にこちらの方がいいと思える方法があればそれを使ってもいいだろう。
Modifier
三つ目のBEMエンティティ、ModifierとはBlockやElementの外観や状態、動作を定義するものだ。
これはBlockやElementに対する補助的な設定で、やはり絶対に作らなければいけないというものではない。
ModifierはすでにBlockやElementがある状態で、それに追加で設定を行う。
つまり、このModifierは単体では使ってはいけないことに注意だ。
ちなみに、Modifierは日本語で言うと修飾子なので、修飾する対象がないとダメだ、というのが分かりやすいと思う。
先ほどのメニュー項目の例で、例えば二つ目の項目が選択されている状態を考えてみる。
この時は、その二つ目に選択されていることを表すModifierのクラス名を追加することになる。
命名規則はこのあとすぐ解説するが、そのModifierのクラス名がmenu__item_actived
だったとすると…
<ul class="menu">
<li class="menu__item"><a class="menu__link">メニュー項目1</a></li>
<li class="menu__item menu__item_actived"><a class="menu__link">メニュー項目2</a></li>
<li class="menu__item"><a class="menu__link">メニュー項目3</a></li>
</ul>
このように、元のElementクラスと一緒にModifierクラスも付与する。
もちろんBlockへも可能なので気を付けよう。
で、肝心の命名規則なのだが、少しややこしい。
先に言葉で説明すると、まずそのModifierを当てるBlockやElementのクラス名から始める。
その後ろにアンダースコアを一つ挟み、Modifier名をつける、という形だ。
上の例では、menu__item
というElementにactived
というModifier名がくっつくことで、menu__item_actived
というクラス名になっている。
このModifier名が厄介で、今のように単に状態を表す場合や、他にもキーと値を持つ場合がある。
キーについてはどんな部分が、値はそのキーがどうなるかを入れ、その間はここもアンダースコア一つを挟む。
文字(キー)を大きく(値)するModifierの場合は、font_large
といったModifier名になるイメージだ。
これをmenu__item
というElementにつけるときは、最終的にmenu__item_font_large
というクラス名になる。
これで分かると思うが、BlockやElementの場合には、それらの名前はそれが何であるかに着目していた。
それに対し、Modifier名はそれがどうであるかに着目する。
どうであるかとは、最初に定義した3パターンで考えると分かりやすいだろう。
外観、状態、動作がどうなっているか、という観点だ。
外観であれば、上で出したfont_large
は文字が大きいことを表す。
状態であれば、これまた上で出したactived
がアクティブになっていることを表す。
動作については…ちょっとこれは英単語をそのまま和訳しただけなので分かりづらいが、振る舞いと言った方が実感が持てると思う。
position_bottom
とすれば、位置が下にあることを表す、といった感じだ。
さて、注意深い方は気づいたかもしれないが、Modifier名だけを見ると大きく二つに分類できる。
単語一つでそれがどうなっているかを表すタイプと、キー・値のペアで表すタイプだ。
前者を真偽値、後者をそのままキーと値のペアと呼ぶことにしよう。
真偽値は主に状態で使われ、上で書いたactived
や他にもselected
、disabled
などといったものになる。
前回のSMACSSにおける状態ルールの接頭語is-
が消えたような形、と言えばわかりやすいだろうか。
キーと値のペアは外観や振る舞いで、何がどうなっているか。
外観の例はテーマ色(theme-color
)が警告(caution
)になっていればtheme-color_caution
。
振る舞いの例は位置(position
)が下(bottom
)ならposition_bottom
。
これもちょっと独特なので、色々な例を見ながら身に付けていくのが一番いいだろう。
おわりに
今回は設計手法の一つであるBEMを解説した。
…のだが、参考書の内容でまだ半分いったかどうかくらいだ。
今回解説したのはBEMの基礎で、これらを理解しておかないと次の内容がさっぱりわからなくなってしまうだろう。
一旦ここまでの内容をまとめてみる。
Blockはそれ単体で成り立ち、再利用可能な部品だ。
ElementはBlockの中で使用し、これ単体では使えない部品。
Element同士のネストは問題ないが、名前はあくまでそれ自身がどのBlockに入っているかという部分だけ含める。
ModifierはそのBlockやElementがどうなっているかを表す設定。
これも単体では使えず、必ず他のBlockやElementに紐づいて使われる。
今のところは、最低限これらだけ抑えておいてもらえれば十分だろう。
次回は、引き続きBEMの解説を続ける。
Modifierに注意点があるのと、他にもBlock同士のネスト、複数のBEMエンティティを一つの要素に付与するMixなどを解説していこう。
もし不安なら、公式サイトも参考にしながら進めてもらいたい。
2021/3/21追記
次回の内容、BEMの後編を解説した。
特にMixが慣れないと厄介なポイントだと思う。
最初は機械的にでもいいので使っていき、感覚を掴んでいこう。
コメント