morishitterのCSSの書き方(2016年夏)
今、自分がどうやってCSSを書いているのかについてまとめる。
CSSを書く前にすること
持論だが、「デザインの意図を正確に理解した上で書かれたCSSは破綻しない」と思っている。 しかし、自分ひとりでサービスを作るときような、デザインの決定権を持つ人とUI実装者が同じである場合を除いて、デザインの意図を正確に伝え、理解することは難しい。
僕が1番時間を使うのがこの工程だ。
今の仕事ではデザイナーがSketchファイルを作成し、エンジニアがそれを元に実装する。 Sketchファイルを開き、アートボードをひたすら眺めデザインの矛盾がないかを確認し、「なぜこのようなデザインなのか」を質問しまくる。
ここで良い質問と提案をするためにも、エンジニア側に最低限のデザインに対する知識が必要だと思う。 最近読んだ本だと、「みんなではじめるデザイン批評―目的達成のためのコラボレーション&コミュニケーション改善ガイド」が良かった。 かなり現場寄りな内容で、コミュニケーションの取り方について書いてある。
みんなではじめるデザイン批評―目的達成のためのコラボレーション&コミュニケーション改善ガイド
- 作者: アーロン・イリザリー,アダム・コナー,長谷川恭久,安藤貴子
- 出版社/メーカー: ビー・エヌ・エヌ新社
- 発売日: 2016/05/24
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
ビルドフロー
僕はCSSのプリプロセス、最適化といった一連のフローでPostCSSのエコシステムを使っている。
プリプロセスとしては、
- postcss-import(
@import
を使ったファイルのインライン展開) - postcss-custom-properties(Custom Propertiesの記法でグローバル変数を定義できる)
- postcss-apply(Custom Sets of Propertiesを展開する)
を利用している。 できるだけ将来CSSの仕様に入るようなものだけを使うようにしている。
ポストプロセスとしては、
- autoprefixer(ベンダープリフィックスの自動付与)
- postcss-flexbugs-fixes(flexboxのバグの中で、CSSの変換のみで対応可能なものを直す)
- csso(minifyツール, PostCSSのプラグインとしての実装もある)
などを使っている。
他にリンターとしてstylelint、フォーマッターとしてstylefmtを使っている。 また、postcss-style-guideを使ってスタイルガイドの生成をビルドフローの中に入れている。
設計手法
次に設計手法、どういう考えでルールセットを分けているかについて。 CSS設計の本質は、セレクタの命名規則ではなく、ルールセットの分割粒度だと思っている。
僕はスタイルを6つのレイヤーに分けて考えている。
ディレクトリの分け方はこんな感じ。
app.css
は全てのレイヤーのCSSファイルを@import
しているだけ。
$ tree src/css -L 1 src/css ├── app.css ├── base ├── components ├── pages ├── patterns ├── settings └── tools
Settingsレイヤー
Settingsレイヤーは、カラーパレットやフォント等、アプリケーション全体で共有する値をCustom Propertiesやプリプロセッサーの変数を用いて定義しておく。
:root { --black: #333; --darkGray: #777; --gray: #aaa; --lightGray: #ddd; --white: #fff; --red: #ff4136; --green: #2ecc40; --blue: #0074d9; }
Toolsレイヤー
CSSには複数のプロパティを組み合わせることで、デザイン的な意味を持つものがある。
代表的なものはテキストを切り取り、末尾に3点リーダーを付けるようなものだ。
Toolsレイヤーでは、@apply
ルールやプリプロセッサーのMixinを使って抽象化し、名前を付ける。
:root { --truncate: { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }; }
このレイヤーは現状プリプロセッサーの利用が必須になるので別になくてもいいと思う。
Baseレイヤー
Baseレイヤーにはnormalize.css等を使った、ブラウザ間のデフォルトスタイルシートの差異をなくしたりリセットしたりするコードを記述する。
また、font-family
やbox-sizing
、ベースとなるfont-size
の指定など、アプリケーション全体の設定もここに記述する。
Patternsレイヤー
Patternsレイヤーは後述するComponentsよりも抽象度の高いレイヤーで、Components間にあるデザインのパターンについて書く。 有名な例だとmedia objectがこれに当たる。
コンポーネントの例として頻繁に「ボタン」が挙げられているのを目にする。 僕は、フォローボタンや送信ボタン、キャンセルボタンと呼ぶものはComponentsで、「ボタン」はPatternsとして定義している。
.button { display: flex; align-items: center; justify-content: center; cursor: pointer; ... } .button:hover { ... } .button:focus { ... }
「ボタンをボタンたらしめる」ためのスタイルをPatternsレイヤーに書き、より具体的なものをComponentsに書くといったイメージ。 また、アニメーションもこのレイヤーに含めている。
Componentsレイヤー
Componentsレイヤーには、実際にユーザーが見て、触れるものを定義する。 Componentsはコンポーネント内のレイアウトについての記述は持つが、コンポーネントと同士の位置関係については考慮しない。
Componentsの命名規則としてはよくBEM (MindBEMding)を利用している。 また、スタイルガイドジェネレーターを使ってスタイルガイドを作るときは、Componentsレイヤーのファイルに生成のために必要な情報(タイトルとかマークアップとか)を書く。
そのコンポーネントがパターンを持つ場合は、class
属性には複数の値を指定することになる。
<button class="button FollowButton"> Follow </button>
Pagesレイヤー
最後にPagesレイヤーについて。 よくデザイナーの人が作っている、Sketchの固定幅のアートボード上に作りこまれている見た目はここ。 (僕たちはこれを見て再利用しやすく、拡張しやすいCSSを書く必要がある。)
Pagesレイヤーでは、最終的にデバイスのディスプレイに表示されることを定義するためのレイヤーで、Componentsを並べ、レイアウトする。 全てのページは、コンポーネントの組み合わせで作っていく。 ざっと、ページ間で再利用するものがコンポーネント、コンポーネント間で再利用するものがパターンというイメージ。
ECSSIST
僕はこの独自の設計思想にECSSIST (Extensible CSS Idealistic Structure Theory)と名前を付けた(「イグジスト」って読ませる)。 別に名前はどうでもいいけど、名前を付けることで理解した気になってもらえやすくなる節がある。
ルールセットの分割は見えているものをより細かくなるように分けるのではなく、スタイルの抽象度で分けるのがいいと思っている。 だから、Atomic DesignでいうAtomsもMoleculesもOrganismsも、目に見えてユーザーが触れるものなのでECSSISTでは全てComponentsになる。
ぶっちゃけHarry RobertsのITCSSに似ていて、ECSSISTもITCSSを自分なりにアレンジしていく中で生まれた。 ITCSSとの違いは、Pagesレイヤーの追加と、汎用クラスを作らないところだ。 「デザインの意図を理解していれば汎用クラスはいらない」という理想論のもとECSSISTは生まれた。
実際に使ってどうなの?
ECSSISTは名前の通り、理想論だ。 デザイナーとエンジニアで別れて仕事をしている以上、そのデザインの意図を完全に理解できることはない。 (またデザインに意図が存在しない場合もある。)
結果、僕は今のプロジェクトではutilities
というディレクトリを切って、そこにいくつかの汎用クラスを定義している。
また、論理的な理由はないが「どうしてもここは譲れない」というデザインもある。
このようなものに対してどうCSSを書くか考え、creative.css
というファイルを作って対応している。
これも同じくHarry Robertsの言うshame.cssと同じようなものだ。
あと、いきなりPatternsを作っていくのは難しい。 見た目だけで再利用を計ると、それがデザインの意図とあわない場合、破綻の原因となる。 僕はまずComponentsをどんどん書いていって、共通化できるものをPatternsにしていくという流れでやってる。
CSSの設計手法なんていろいろあるけど、自分自身の考えやチームの開発体制、デザインプロセスに合わせて調整していくものだと思う。
よくある質問
その他、よく質問されることについての回答。
PostCSSのメリットは何?
- 小さいからコントリビュートしやすい
- 必要な機能を選んで使える
- プラグインを書いて独自のカスタマイズができる
僕はPostCSSを昔から追いかけていて、何かあっても対応できるからってのが大きい。 自分は既存のプリプロセッサーに不満を持っていて、独自のものを作りたいという衝動でPostCSSに目を付けたので、強い意志がない場合はプリプロセッサーはSassとかを使ってもいいと思う。
CSS Modulesについてどう思う?
僕は使わない。
ReactコンポーネントとCSSのコンポーネントの粒度は同じにならないと思っていて、CSSのコンポーネントの方がJSのコンポーネントよりも多くなる(細かくなる)。 JSとCSSのコンポーネントが1対1に対応しないので、CSS ModulesやRadiumなどのCSS in JS系ツールのアプローチは合わないと思っている。
しかしこれは、僕が比較的CSSの設計手法について知見や意見があるからだ。 「CSSはよくわかってなくて、雑な命名でもなんとかやっていきたい」って人には良いと思う。 似たようなことを以前も書いた。
あと、CSS Modulesはセレクタ名を適当な一意な文字列に置き換えてそれをHTMLのclass属性値と対応させるってことをやっているんだけど、それがそもそも筋が悪い。
できるだけ標準化されたものを使いたくて、各ブラウザでのShadow DOMの実装が待たれる。
ちなみに、@scope
ルールは消えた。
どうすれば壊れないCSSが書けるの?
まず基本として、CSSのカスケーディングという機能について正確に理解する必要がある。 詳細度の計算方法と、値が継承されるプロパティについて理解できると良い。
また、ここまでで「デザインの意図を正確に理解した上で書かれたCSSは破綻しない」と述べた。 あまりに変更が繰り返されるデザインにはそもそもそこには意図がなく、行き当たりばったりでデザインされていることが多い。 それでは良いCSSの設計ができるはずもない。 壊れない、良いCSS設計には、良いデザインプロセスが必要になる。
この良いデザインプロセスに変えるというのが難しくて、個々人のモノ作りに対するリテラシーによるものが大きいので、組織レベルで変えるにはどうすればいいんだろうと思ってる。 良いモノ作りがしたい。
最近どうなの?
近況です。
いつものアレです。