本付録は、メインシリーズであるWordPressのテーマ作成に役立ちそうな情報を補足していくパートだ。
前回から具体的な設計手法に入り、一つ目のOOCSSを解説した。
考え方は二つ、ストラクチャーとスキン、コンテナとコンテンツを分離する、というもの。
シンプルでこれ単体ではあまり使わないが、他の設計手法の基になっている。
その点に着目して今回以降の内容を見てみるのもいいだろう。
以下がその記事だ。
さて、今回は具体的な設計手法二つ目、SMACSSを解説しよう。
一気に話が具体的になり、色々と実感も湧いてくることだろう。
8つの考え方とも照らし合わせながら進めていきたい。
なお、本付録は以下の本を参考にしている。
気になる方は、実際に本屋で中身を見てみたり、買ってみたりしよう。
SMACSS
では本題へ。
SMACSSはScalable and Modular Architecture for CSSの略で、スマックスと読む。
日本語に直すと、拡張可能かつモジュール的なCSS設計、となる。
公式サイトは以下で、この中からドキュメントをPDF等で見ることもできる。
日本語に直されているので、より詳しく見たい方はそこから参照いただければと思う。
これはCSSを5つの役割にカテゴライズしているのが特徴だ。
- ベース
- レイアウト
- モジュール
- 状態(ステート)
- テーマ
前回のOOCSSの内容は、この5段階のうちモジュールの部分に該当すると考えられるだろう。
SMACSSではその他幅広く、ベースやレイアウトにも触れられている。
まずは全体像を把握するため、ざっと概要レベルでそれぞれが何を表しているかと命名規則を見てみよう。
ベース
ベースとは、リセットCSSを含むデフォルトの設定だ。
あるページの各要素について、原則となるその見た目を定義していく。
ここでは単なる要素型セレクタ―を始め、疑似クラスや子セレクター、兄弟セレクタ―などを使うことができる。
しかし、IDセレクタ―やクラスセレクタ―を使うことはできない。
あくまで、何も指定しない場合のデフォルトを定義するに留めよう。
また、!important
も使ってはいけない。
他の内容でこれを上書きすることも十分考えられ、それを阻止してしまうからだ。
命名規則についても、IDやクラスを使わないので何もないことは明らかだろう。
レイアウト
レイアウトは、ページ全体を分割するために使う。
例えばヘッダー領域とメインコンテンツ領域、フッター領域といった感じ。
この中に後述するモジュールが含まれる形になる。
ここはIDセレクタ―、あるいはクラスセレクタ―を使うことになる。
それぞれが一つだと分かりきっていれば、ここだけIDセレクタ―が使える。
命名規則は、クラス名の場合は先頭にl-
というものをつける。
IDセレクタ―は他で許容されておらず、そもそもIDセレクタ―の時点でレイアウトだと分かるので何もつけなくてもいい。
…ただ、後の詳細で触れるが、参考書ではここもクラスセレクタ―だけを使うことを推奨している。
今回もそれに倣うことにしよう。
モジュール
モジュールは、以前解説した通りのものだ。
やはり使いまわせる部品のことで、上のレイアウトの中だったり、あるいは他のモジュールの中に含まれる。
例えば、メニューの各項目やそのメニュー自身もモジュールだろう。
繰り返し使うことが想定されているので、当然ながらクラスセレクタ―を使うことになる。
命名規則は、先頭にm-
をつける…という感じにしてもいいのだが、SMACSSではほとんどのクラスがモジュールを表すことになる。
そのため、不必要に冗長となってしまうので、モジュール名のみをつけることが推奨されている。
ある特定のモジュールに含まれる別のモジュール、といった時は、親となるモジュールを接頭語としてつけるようにしよう。
状態(ステート)
状態とは、ある対象が状況によって変化するような場合に使うものだ。
この状況にも定義があり、それは詳細の中で見ていく。
例えば、jQueryの中でやったようなスクロールによって画面に追従するようなスティッキーヘッダー。
その時もやっていたが、JavaScript側でクラス名の付け外しを行い、それによって適用されるスタイルを変えていた。
その時につけるクラスがこの状態だという認識でいいだろう。
他にも、ボタンが押せるかどうかで表示を変える際なんかにもこれを使う。
命名規則は、is-
という接頭語をつける。
テーマ
テーマは、公式のドキュメントで使われるプロジェクトは少ないと言われている通り、あまり深くまで理解する必要はないかもしれない。
使いどころとしては、ユーザー側で色を選択したり、言語を選択したりできるような時に、それぞれを用意しておくような感じだ。
各分類の詳細
さて、各設定をこんな5つのカテゴリーに分類しているのがSMACSSだ。
全体像はなんとなく分かったと思うので、それぞれの詳細を見ていこう。
ベース
考え方は、一つ目の特性に応じてCSSを分類するというものだ。
ページ全体で使う、各要素に対する共通の設定を行っていく。
のだが、ここであれこれ設定しすぎてしまうと考え方3つ目の影響範囲がみだりに広すぎないに反してしまう。
そのため、本当に必要なものだけを定義するようにしよう。
ただ、一つだけ強く推奨されている設定がある。
body
タグの背景色、プロパティで言えばbackground-color
だ。
この理由は、ユーザー側で独自のCSSを適用していた場合、その背景色によっては文字が一切見えない、という状況に陥ってしまう。
それを防ぐためのもので、これだけはしっかり設定しておきたい。
他の注意点としては、上の概要にも書いたが!important
を使ったり、ID名やクラス名での指定もしない。
あくまで他に何も設定しなかった場合の内容を定義したいので、!important
はここで使うとそれが上書きできなくなる。
ID名やクラス名も特定の状況になるので、基本設定から外れてしまう。
ちなみにリセットCSSもこのベースルールに含まれるのだが、その使用に関してSMACSSでは注意書きがある。
よく考えて使わないと、一度打ち消したルールを再度設定することになり、結果的にソースコード量が増えるとのこと。
そのため、使うなら欠点もしっかり理解した上で使おう、という内容が書かれている。
レイアウト
レイアウトでも、考え方は一つ目の特性に応じてCSSを分類するに該当する。
概要でも述べた通り、ここではヘッダーやメイン、フッターなどといった領域を分けていく。
ちなみに、公式ドキュメントでは前解説したモジュールをコンポーネントという呼び方をしており、そのうち主要なものがこのレイアウト、その他レイアウトや別のコンポーネント内に配置される小さなコンポーネントをモジュールとしている。
しかし、レイアウトでは通常ページの中で一回しか使わないようなヘッダー、フッターといったものを含んでいる。
こんな背景があり、このレイアウトルールにのみIDセレクタ―の使用が許容されている。
…のだが、上でもちらっと書いた通り、本シリーズでは参考書に倣いクラス名を使うことを想定しておこう。
理由なのだが、命名規則にある。
命名規則では、クラス名なら先頭にl-
をつけるのだが、ID名はどちらでもいい。
というのも、今書いた通りなのだが、IDセレクタ―はここでしか使えない。
そのため、IDというだけでレイアウトだと分かるのだ。
…が、それだとレイアウトを表す対象が2種類になってしまい、混乱の種になる。
それを避けるために統一しよう、というお話だ。
ちなみにだが、例えばページの種別によってレイアウトを変更したい、といった場合があると思う。
具体的には、WordPressでアクセスされたページがトップページならこれ、それ以外ならこれ、といった感じ。
その時には、変化させるページのbody
タグにもレイアウトを表すクラス名をつけ、それの子孫要素として設定すればいい。
つまり、以下のような形だ。
<!-- 通常時 -->
<body>
<header class="l-header">
<!-- 省略 -->
</header>
<!-- 省略 -->
</body>
<!-- トップページ -->
<body class="l-top">
<header class="l-header">
<!-- 省略 -->
</header>
<!-- 省略 -->
</body>
/* 通常のヘッダー */
.l-header {
/* 省略 */
}
/* トップページのヘッダー */
.l-top .l-header {
/* 省略 */
}
これで詳細度も一つ上がるので、その内容で上書きできるというわけだ。
モジュール
ここがSMACSSのメインだろう。
大前提として、やはりモジュールはどこでも使える部品。
そのため、ここではそれが配置されている場所に影響されないか、言い換えれば配置する場所を変えても問題ないかを常に意識しておくことが大事だ。
関連する考え方は…
- 2つ目:HTMLとスタイリングが疎結合である
- 3つ目:影響範囲がみだりに広すぎない
- 4つ目:特定のコンテキストにみだりに依存していない
- 8つ目:拡張しやすい
この4つだ。
二つのブロック、上二つと下二つに分けて見ていこう。
まず上二つについて。
モジュールに対しては基本的にIDや要素型セレクタ―を使わず、クラスセレクタ―のみを使う。
そもそもが使いまわすことを想定しているので、一つしか入れれないIDで指定することはないだろう。
これで最初のHTMLとスタイリングが疎結合であるを満たせる。
ただ例外もあり、それが十分に予測がつくものであれば、要素型セレクタ―を使うこともできる、と公式ドキュメントには書かれている。
この予測がつくというのは、セマンティック性という言葉で説明されている。
セマンティック性とはそれが何なのかを表す度合いで、言い換えれば具体性だろうか。
大雑把には、以下のように考えることができる。
- 特定のモジュール名をつけたクラス名はセマンティック性が高い
h2
、h3
やfigure
などはそれ自体に多少意味があり、中程度のセマンティック性を持つdiv
、span
は非常に汎用性が高く、逆にセマンティック性は低い
こんな感じ。
クラス名は非常にセマンティック性が高く、もちろんそのまま使って問題ない。
しかし、中程度で例に挙げたh2
やh3
などは、あるモジュールの中という条件であればある程度のセマンティック性を保つことができる。
ただ、これも子孫セレクターではなく、子セレクタ―を使うことが推奨だ。
そうすればより曖昧性を排除できるし、影響範囲も狭めることにも繋がる。
そのため、例えば以下のような書き方はOKとされている。
.module > h2 {
/* モジュール内のh2タグへの設定 */
}
これでセマンティック性を向上させることができる。
それに対し、セマンティック性が低いspan
タグやdiv
タグ。
これらは例えあるモジュールの中、という条件をつけたとしてもまだセマンティック性を十分にはできないだろう。
以前サンプルでやったメディア領域を覚えているだろうか。
見て欲しいところだけ抜粋すると以下のような形になっていた。
<div class="bl_media">
<!-- 省略 -->
<div class="bl_media_body">
<h2 class="bl_media_title">記事タイトルテキスト</h2>
<p>記事本文</p>
<h3 class="subTitle">サブタイトル</h3>
<span>テキスト</span>
</div>
</div>
元は単にモジュールであるbl_media
の子孫要素として設定していたが、それだと記事本文のところにspanタグが使われるとそこにも設定が反映されてしまうという問題点があった。
その時は、子セレクタ―を使って範囲を狭めることで対応していた。
確かにそれで今の問題は解決できるが、それでもセマンティック性は十分に高いとは言えない。
そこで、SMACSSではこのspan
やdiv
には、必ずクラス名をつけてスタイリングすることを推奨している。
上の例であれば、span
タグにsubText
のようなクラス名をつけ、それで設定しよう、ということだ。
…と書いてきたが、そもそも公式ドキュメントのこの節のタイトルは、「要素セレクタを避ける」だ。
そのため、h2
やh3
など中程度のセマンティック性を持つと紹介したものに対しても、基本的には最初に書いた通り常にクラス名をつけて、それでスタイリングするのがいいだろう。
そうすれば、例えば見出しのレベルやそもそもタグ自体を変えたいといった場合への対応も楽になる。
さて、話は変わって、下二つのブロックを見ていく。
特定のコンテキストにみだりに依存していないと拡張しやすいに関連するポイントだ。
ここでは、ある特定の状況のみ変化させたいモジュールを見てみよう。
例えば、ボタンのモジュールがあったとする。
通常は独立して使われ、その時のスタイルはbtn
クラスで定義されているとする。
これを、例えばサイドメニュー内で使う時だけ幅を狭めたい、といった状況だ。
サイドメニューのクラスをsidemenu
としたとき、何も考えないとCSSは以下のようになることが想像できるだろう。
.btn {
/* ボタンの設定 */
}
.sidemenu {
/* サイドメニューの設定 */
}
.sidemenu .btn {
/* サイドメニュー内のボタンの設定 */
/* 上書きする内容だけ定義 */
}
現時点ではこれでいいだろう。
そこで、さらに今設定したボタンの後ろにもう一つボタンをつくり、異なるサイズを割り当てたいとしよう。
この時は…
.btn {
/* ボタンの設定 */
}
.sidemenu {
/* サイドメニューの設定 */
}
.sidemenu .btn {
/* サイドメニュー内のボタンの設定 */
/* 上書きする内容だけ定義 */
}
.sidemenu .btn:nth-of-type(2) {
/* サイドメニュー内の追加ボタンの設定 */
/* 上書きする内容だけ定義 */
}
そろそろ、なんかまずそうな雰囲気がしてきたのが分かると思う。
確かにこれで今のところスタイルは実現できているし、既存の設定にも手を触れていない。
しかし、これだとその二つのボタンを入れ替えた途端に崩壊する。
ここで、サブクラスという考え方を導入する。
何も、こういったボタンにクラス名を追加してはいけないわけではない。
ということで、もしサイドメニュー内に配置する場合は、btn-small
といったサブクラスを追加で付与する。
つまり、通常使う時のボタンはbtn
というクラス一つ、サイドメニュー内で使う場合はbtn
とbtn-small
の二つのクラスを持つ、ということ。
さらに、追加するボタンにはまた別で、それが元より広ければbtn-long
といったクラス名もつけてみる。
要するに、HTML側は以下のような感じになるということ。
<div class="sidemenu">
<a class="btn btn-small" href="...">...</a> <!-- サイドメニュー内 -->
<a class="btn btn-small btn-long" href="...">...</a> <!-- サイドメニュー内、横に広い -->
</div>
<a class="btn" href="...">...</a> <!-- 通常時 -->
考え方の8つ目、拡張しやすいでやったモディファイアのイメージだ。
これで、CSSでは以下のように設定することが可能となる。
.btn {
/* ボタンの設定 */
}
.sidemenu {
/* サイドメニューの設定 */
}
.btn.btn-small {
/* サイドメニュー内のボタンの設定 */
/* 上書きする内容だけ定義 */
}
.btn.btn-long {
/* サイドメニュー内の追加ボタンの設定 */
/* 上書きする内容だけ定義 */
}
こうすると何が嬉しいかというと、今サイドメニューの中で使うことを想定していたが、CSSで見ればそこが関連しなくなった。
つまり、コンテキストに依存しなくなったということ。
また、モディファイア的な使い方をすることで拡張も容易だ。
…さて、一つだけ気になる点がある。
今、btn-small
やbtn-long
といったクラス名をつけた。
これはモディファイア的な意味でつけている。
しかし、以前やった例だと、あるモジュールに含まれる別のモジュールとして、例えばsidemenu-btn
のように書いていたこともあった。
こちらは子モジュールであることを表している。
この違いが分かりづらい。
…残念ながら、SMACSSではそこについては触れられていない。
やるとすれば、子モジュールはハイフン一つ、モディファイアはアンダースコアを使ったりハイフンを二つにしたり、といった分け方をオリジナルで定義して使うことだろう。
ここは柔軟に、その時どうするのがいいか考えて対応していこう。
状態(ステート)
関連する考え方は二つ、一つ目の特性に応じてCSSを分類すると、八つ目の拡張しやすいだ。
これは他の全てのスタイルを拡張したり上書きしたりするものになる。
これだけ書くと一つ上のサブクラスと似ているように思えるかもしれないが、明確に条件が定義されている。
一つ目は、レイアウトやモジュールの大元となる要素に追加で割り当てることができること。
二つ目は、JavaScriptによって変化すること。
この二つ目が重要で、これはページが表示された瞬間だけでなく、それ以降も変化しうることを意味している。
サブクラスとの違いはここだろう。
サブクラスの場合は、そのクラス名をあらかじめ付与することにより、場所の変化に対応していた。
つまり、一度ページが表示されれば、そこから変化することはない、ということ。
それに対し、状態は元からそのクラスがついているかもしれないが、それが表示された以降も変わる可能性がある。
具体例としては概要でも書いたが、スティッキーヘッダーが追従しているかどうかがこの状態に当てはまる。
スティッキーヘッダーでは画面がスクロールされたとき、そのスクロール量によって該当モジュールにJavaScriptからクラス名を付けたり外したりしていた。
これは上の二つの条件を満たすので、この状態だということができる。
その他にも、例えばサイドメニューをボタンによる開閉式にした場合や、ボタンを押せるかどうかをテキスト入力欄で制御する場合なんかもこれに該当する。
セレクターは、状態を表すそのクラス名一つのみを使う。
また、他のスタイルを全て上書きして反映させるために、ここでのみ!important
を使うことが許容される。
とはいえ、これはやはり危険なものなので、可能な限りは避けるべきだろう。
本当に必要な場合のみ使うようにしたい。
命名規則は、接頭語としてis-
をつける…のだが。
例えばあるボタンを押せることを表すときにis-active
のようなクラス名をつけると、どの対象について制御するものなのかが分かりづらい。
そのため、クラス名の中にこれで制御するモジュールのクラス名を入れることが推奨されている。
今出した例で言えば、is-btn-active
といった形だ。
テーマ
ここがなかなかに分かりづらいし、使う機会もそう多くないようなので、分からなかったら最悪飛ばしてもいいかもしれない。
これは、例えばサイトの基本色であったり、見た目をユーザー側に選択してもらえるようにし、それで変更するような場合に使う。
例えば、WordPressの管理画面、デフォルトではメニューが黒になっている。
この色は、メインシリーズ側の初回でやった通り設定によって変えることができる。
こんなものを実装するときに使う、というイメージでいいだろう。
やり方としては、そもそもCSSを分けるという手がある。
デフォルトではあるテーマを定義しているファイルを読み込んで置き、それとは別で特定の設定のみを上書きするテーマファイルを用意しておく。
それを、例えばドロップダウンリストなんかで選択してもらい、それによって上書きするファイルを読み込む、という感じだ。
変更対象となるレイアウトやモジュールが少なければ命名規則を考えなくてもいいとは思うが、大規模なテーマの場合には、対象となるモジュールに接頭語としてtheme-
を付けることが推奨されている。
公式ドキュメントでは、もう一つタイポグラフィーというものについても解説しているが…ちょっとテーマは今回使う予定はないので省略することにしよう。
気になる方はドキュメントを読んでみて欲しい。
おわりに
今回は、SMACSSを解説した。
だいぶ具体性が上がり、なんとなくどういった感じで組むか分かってきたかもしれない。
細かいところはわりと柔軟で、小規模でそこまで厳密でなくとも問題ないという場合はSMACSSそのままでも十分だ。
しかし、大規模になってきたら、自分でそこもしっかり決めておかないと後で混乱してしまうだろう。
あるいは、そういった細かい部分は別の設計手法を組み合わせる、ということも当然できる。
そこはやはりそれぞれの状況によって変わってくるので、色々と試したい。
なお、今回は参考書の記載を参考に解説しているのでこれで終わりにしているが、SMACSSの公式ドキュメントで見ると上の内容はまだ半分程度。
他にも、そもそものCSSがどう評価されるかといった話なんかも載っているので、より理解を深めたい方は一度読み込んでみることをオススメする。
さて、次回はBEMという設計手法を解説する。
他の内容と比べてかなり厳格、かつ強力な手法のようだ。
もちろんこれまでのものと考え方が共通している部分もあるので、そこに注目しながら進めてみよう。
2021/3/17追記
次回の内容、BEMの解説前半を公開した。
ちょっと解説量が多く、まずは基本部分を前半としてまとめた。
後半ではこの内容を補足していく形なので、この基本をしっかり抑えてから次に進んでいこう。
コメント