※この記事は2017年4月7日に執筆された記事です。現在は仕様が異なる可能性があります。
Webの開発をやったことのある方なら誰しも、「CSSって結局どう書くのがベストなの?」という悩みを感じたことがあるでしょう。
一見簡単なCSSですが、一度書き始めるとそのあまりの自由さに、まるで大海原に放り出された赤子のような気分になってしまいますよね。
人生何事も、ある程度制約があったほうがやりやすいものです。
そんなわけで今日はCSSの設計について考えてみましょう。
目次
どんな設計があるの?
CSS設計について調べてみると、OOCSSだとかSMACSSだとかFLOCSSだとか、いろいろな設計思想があることがわかります。
- OOCSS:Slide Share – Object Oriented CSS
- SMACSS:SMACSS
- FLOCSS:GitHub – FLOCSS
FLOCSSを例に見てみましょう。
概観としては、
- Foundation
- Layout
- Object
の三つがあって、さらにObjectの中に、
- Component
- Project
- Utility
があり、これらにCSSを分類して書くという設計用となっています。
FLOCSSの設計に従うことで、
- CSSがコンポーネント化されるので使いまわしや修正に強くなり、
- MindBEMdingのネーミングルールによってクラスの役割が明確化され、
- 全体として管理のしやすいソースコードが出来上がる
というメリットを得られます。
とはいえ、「ハイハイ、これに従って書けばいいのね」と書き始めたのも束の間、ほとんどの人は思うことでしょう。
「このパーツってComponentとProjectのどっちだ…」
「Utility使いまくらないと全然レイアウト実現できない…」
「Layoutに書くこと少なすぎ…」
多くのWebサイト制作では、FLOCSSのポテンシャルを生かしきれずに、そのメリットがなんだかよくわからないままCSSを書いていくことになってしまいがちです。
相当大規模なプロジェクトならまだしも、ページ数の知れているWebサイトなんかではここまで巨大な設計図を引かなくても、うまいこと組み立てられそうですよね。
ということで、FLOCSSのいいところどりをした、よりスリムな設計「FLOU」を考えてみることにしましょう。
F、L、O、Uの4つに分けよう
基本のアーキテクチャとしては、Foundation、Layout、Object、Utilityの4つに分けましょう。
例えばこんな組み方にしたいとき、
それぞれの役割は、図にするとこのようになります。
(分かりやすくするために一部簡略化しています。)
F、L、O、U、の4つが、Webページに必要な要素を過不足なく担当できているのがわかると思います。
具体的なコーディングルールは、下記のような感じです。
Foundation
- リセットCSS、NormarizeCSSなどの、すべてのベースとなるCSS
- 基本的にコードは追加しない
- foundation.scssに記述
foundation.scss html, body, h1, h2, h3, h4, ul, ol, dl, li, dt, dd, p, div, span, img, a, table, tr, th, td { margin: 0; padding: 0; border: 0; letter-spacing: 0.5px; font-weight: normal; font-size: 100%; font-family: 'Hiragino Kaku Gothic ProN', 'ヒラギノ角ゴ ProN W3', Meiryo, メイリオ, Osaka, 'MS PGothic', arial, helvetica, sans-serif; vertical-align:baseline; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; line-height: 1.8; word-wrap: break-word; }
Layout
- パーツの配置や、ラッパーとしての幅や高さなどを決定するクラス
- 接頭辞として
l-
をつける - layout.scssに記述
layout.scss .l-justify-left { display: flex; justify-content: flex-start; } .l-justify-right { display: flex; justify-content: flex-end; } .l-justify-center { display: flex; justify-content: center; }
Layoutにパーツの配置の役割を持たせるのは、「SMACSS」の思想を取り入れているイメージですね。
Object
- ページをまたいで使われる各種パーツを定義するクラス
- そのパーツ内で常に同様の振る舞いをするものに関してのみスタイルを定義
- object.scssに記述
object.scss .boxA { width: 100px; height: 100px; color: #ff0000; } .boxB { width: 200px; height: 200px; color: #00ff00; }
Utility
- 調整用のクラス
- margin、padding、font-size、colorなどを付与するのに使用
- 他種類のパーツ間の空き調整や、パーツとして認められないような、自由な振る舞いをする要素に対してはこちらのクラスを使用
- 接頭辞として
u-
をつける - utility.scssに記述
utility.scss u-mt10 { margint-top: 10px; } u-mt20 { margint-top: 20px; } u-mt30 { margint-top: 30px; }
FLOCSSではUtilityクラスはObjectの中の一つとして扱われますが、空きの調整などは基本的にUtilityに任せるルールにし、Objectと同粒度のクラスとしてガンガン使っていきましょう。
この設計を、F、L、O、Uの頭文字をとって、ここでは便宜的にFLOUと呼ぶことにします。
なお、classの命名ルールについては今回は触れませんが、FLOUの設計はMindBEMdingの記法と相性が良いので、こちらも適宜取り入れてみてください。
FLOU設計のメリットは?
さて、このFLOUの設計を使うとどんなハッピーなことがあるのでしょうか。
レイアウトが劇的に楽
まず言えるのが、「モジュールの配置に関していちいちCSSを考える必要がなくなる」ということです。
Layoutに適切なクラスを用意しておくことによって、モジュール(Object)たちを柔軟に、かつスピーディーに配置していくことが可能になります。
例えば、上記のflexboxを使ったLayoutクラスを駆使すれば、下記のように柔軟にモジュールを配置することができます。
ここで使っている.l-justify-left
などのクラスは、子要素に依存しないレイアウトのクラスとなるので、どんなモジュールに対しても使い回すことができます。
(依存を完全に0にしているわけではありませんが。)
レイアウトだけを担当するクラスを用意しておくというのは、Bootstrapなんかと同じ発想ですね。
旧ブラウザ対応などでflexboxが使えない場合でも、float: left;
やdisplay: tabel-cell;
などを駆使したレイアウトのクラスを用意しておくだけで、マークアップコストが劇的に下がります。
また、配置のためのクラス以外にも、
layout.scss .l-w25 { width: 25%; } .l-w50 { width: 50%; } .l-w100p { width: 100%; } .l-h100p { height: 100%; }
のように、ラッパーの幅や高さを指定したりするLayoutクラスを用意しておくのも強力ですね。
このように、レイアウトに関する記述をLayoutクラスとして切り出し、使い回すことで、Object内で定義するモジュールを柔軟に配置することができるようになります。
CSSの見通しが良くなる
FLOUを使う二つ目の利点として、「ファイル全体の見通しが良くなる」というものがあります。
例えば、
object.scss .boxA { width: 100px; height: 100px; color: #ff0000; margin-top: 20px; /* ←これに注目 */ } .boxB { width: 200px; height: 200px; color: #00ff00; margin-top: 20px; /* ←これに注目 */ } .textA { color: #ff0000; font-weight: bold; margin-top: 20px; /* ←これに注目 */ }
とするよりは、
utility.scss .u-mt20 { margin-top: 20px; /* ←これに注目 */ }
object.scss .boxA { width: 100px; height: 100px; color: #ff0000; } .boxB { width: 200px; height: 200px; color: #00ff00; } .textA { color: #ff0000; font-weight: bold; }
として、各モジュールにhtml上で.u-mt20
を付与する方が、全体の見通しが良くなります。
ここでは、単に
- それぞれのモジュールに対する記述が減ったので見やすくなった
ということに加えて
- それぞれのモジュールにとって本質的でない記述が減った
ということも重要です。
上記の前者の例のmargin-top: 20px;
は他のモジュールとの関係を定義するのに必要なだけであって、それぞれのモジュールには直接的には関係がない記述ですよね。
このように、モジュール自体が他のモジュールとの関係に関するプロパティを持っていると、想定してなかったモジュールの組み合わせによって、「上の空きが大きすぎる」や「横並びになってくれない」などの不具合が生じかねません。
一方、FLOUの設計であれば、モジュール(Object)と配置(Layout)、調整(Utility)を分けたことで、
- モジュールのデザイン修正をしたい場合はObjectクラスをいじって、
- 配置に関してはLayoutクラスの変更で対応し、
- モジュール間の空きをなど調整したい場合はUtilityクラスを変更すればよい
ということになるので、仕様の変更に強くなります。
オブジェクトの修正でない、例えばレイアウトの変更や、数px単位の微妙な空き調整なんかは、結局html上でのクラスの付け替え作業になるので、他のページへの影響をケアする必要がなくなるというのも嬉しいところです。
FLOUで書くときのポイントは?
FLOUでは、どの粒度でモジュールを切り出していくのかがポイントになってきます。
理想的な切り出し方は、
- できるだけ小さい単位で切り出す
- しかし、常にセットで使うものに関しては一つのモジュールにまとめる
というところです。
例えば、こんなパーツを作るとします。
この場合は、フォームとボタンをセットでモジュールとして切り出すのでなく、別々で切り出す方が良いでしょう。
なぜなら、もし「やっぱりこのページだけはフォームとボタンを縦並びに変更したい」となったときに、セットで切り出していた場合は、
- 従来の「フォームとボタンが横並びのモジュール」に加えて、
- 新規の「フォームとボタンを縦並びのモジュール」をCSSファイルに追加する
ということになりますが、別々で切り出していれば、
- html上で、子要素を横並びにするLayoutクラスから子要素を縦並びにするLayoutクラスに変更する
という対応だけで済み、モジュールのパターン(今回で言う「フォーム」と「ボタン」)をCSS上で増やすことなくデザインが実現できるからです。
このように、モジュールはできるだけ小さい単位で管理し、配置に関してはLayoutクラスとUtilityクラスに任せることで、CSSの肥大化を抑えることができるのです。
とはいえ、もちろん「このパーツはどんな場面でもセットで使う」と言い切れるものに関しては、セットで管理するのがベターでしょう。
例えば、WebNAUTではトップページや各カテゴリーページなどで使われている記事カードのモジュールのCSSは、一つのセットとして管理しています。
- カード内の要素は個別に切り出して使うことはないと考えられる
- トップページ一覧の記事カードのデザインが変わるときは、他のページの記事カードのデザインが変わるときでもあると考えられる
というのが、その理由です。
まとめ
CSSの設計に関しては、SMACSSやFLOCSSなどのオブジェクト指向の設計に従うことで、メンテナンス性に優れたコードを書くことができます。
しかし、どの粒度でオブジェクトを切り出すのか、またオブジェクト以外のクラスはどう扱うかなどはサイトの特性によってまちまちなので、「どんなサイトにでも使える万能な設計手法」というものは存在しません。
そんなときは、今回ご紹介したミニマムな設計である「FLOU」をベースに最適解を模索し、自己流の設計としてアレンジしてみてはいかがでしょうか!