morishitterのCSSの書き方(2016年夏)

今、自分がどうやってCSSを書いているのかについてまとめる。

CSSを書く前にすること

持論だが、「デザインの意図を正確に理解した上で書かれたCSSは破綻しない」と思っている。 しかし、自分ひとりでサービスを作るときような、デザインの決定権を持つ人とUI実装者が同じである場合を除いて、デザインの意図を正確に伝え、理解することは難しい。

僕が1番時間を使うのがこの工程だ。

今の仕事ではデザイナーがSketchファイルを作成し、エンジニアがそれを元に実装する。 Sketchファイルを開き、アートボードをひたすら眺めデザインの矛盾がないかを確認し、「なぜこのようなデザインなのか」を質問しまくる。

ここで良い質問と提案をするためにも、エンジニア側に最低限のデザインに対する知識が必要だと思う。 最近読んだ本だと、「みんなではじめるデザイン批評―目的達成のためのコラボレーション&コミュニケーション改善ガイド」が良かった。 かなり現場寄りな内容で、コミュニケーションの取り方について書いてある。

みんなではじめるデザイン批評―目的達成のためのコラボレーション&コミュニケーション改善ガイド

みんなではじめるデザイン批評―目的達成のためのコラボレーション&コミュニケーション改善ガイド

ビルドフロー

僕はCSSプリプロセス、最適化といった一連のフローでPostCSSのエコシステムを使っている。

プリプロセスとしては、

を利用している。 できるだけ将来CSSの仕様に入るようなものだけを使うようにしている。

ポストプロセスとしては、

などを使っている。

他にリンターとして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-familybox-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はよくわかってなくて、雑な命名でもなんとかやっていきたい」って人には良いと思う。 似たようなことを以前も書いた。

morishitter.hatenablog.com

あと、CSS Modulesはセレクタ名を適当な一意な文字列に置き換えてそれをHTMLのclass属性値と対応させるってことをやっているんだけど、それがそもそも筋が悪い。 できるだけ標準化されたものを使いたくて、各ブラウザでのShadow DOMの実装が待たれる。 ちなみに、@scopeルールは消えた

どうすれば壊れないCSSが書けるの?

まず基本として、CSSのカスケーディングという機能について正確に理解する必要がある。 詳細度の計算方法と、値が継承されるプロパティについて理解できると良い。

また、ここまでで「デザインの意図を正確に理解した上で書かれたCSSは破綻しない」と述べた。 あまりに変更が繰り返されるデザインにはそもそもそこには意図がなく、行き当たりばったりでデザインされていることが多い。 それでは良いCSSの設計ができるはずもない。 壊れない、良いCSS設計には、良いデザインプロセスが必要になる。

この良いデザインプロセスに変えるというのが難しくて、個々人のモノ作りに対するリテラシーによるものが大きいので、組織レベルで変えるにはどうすればいいんだろうと思ってる。 良いモノ作りがしたい。

最近どうなの?

近況です。

f:id:morishitter:20160726153328p:plain

いつものアレです。