CSS 設計の長い夢

スタイルシートの夢

この項目の担当 @shikakun

今朝、夢を見ました。夢のなかで、僕はスタイルシートを書いていました。

そのスタイルシートは、書きやすくて、読みやすくて、壊れない。そんな「運用しやすい」CSS でした。満足げに仕事を終えたところで目が覚めると、僕の隣り(ローカル環境のリポジトリ)には、ウェブサービスの長い歴史のなかでさまざまな人が関わって育てられてきた、1万行くらいに肥大化した CSS がスヤスヤと寝息を立てて眠っているのでした。

気がつくと CSS が 1万行くらいになっているのは、なんでなんだろう?

夢で会った「運用しやすい」CSS で仕事がしたいけど「運用しやすい」ってどういうことなんだ。その手がかりとして、ここでは Philip Walton さんが 2012年に『CSS Architecture』という記事で示していた、CSS を設計するときに目指すべき 4つのゴールを紹介したいと思います。もっと詳しく知りたい方はぜひ 原文Hiroki Tani さんによる日本語訳)を参照ください!

(1) 予測しやすい

予測しやすい CSS とはルールが期待通りに振る舞うことを意味する。ルールを追加・更新したとき、そのルールが意図せずサイトの一部に影響を与えるべきではない。滅多に変更されない小規模なサイトであれば、このことはあまり重要ではないが、数十、数百ページの大規模なサイトであれば、予測しやすい CSS は必須といえる。

例えば、こんな CSS があったとします。

.widget {
  background: yellow;
  border: 1px solid black;
  color: black;
  width: 50%;
}

黄色いウィジェットが便利なので、サイドバーにも置きたくなってしまった。サイドバーに置いてみると、横幅は 200px ぐらいがいいかな…

#sidebar .widget {
  width: 200px;
}

トップページは黄色だと目立ちすぎだから、白色がいいな。

body.homepage .widget {
  background: white;
}

こうなると、もはや .widget はいったいどんな形をしているのか、誰も把握できない。サイドバーやトップページにあるとき .widget の見た目がどんな風に変身してしまうのか、予測がつかない。

(2) 再利用しやすい

CSS のルールは抽象的で、十分に分離されているべきである。それはパターンとすでに解決した問題を書きなおす必要なく、既存のパーツから新しいコンポーネントを速くつくることができるということだ。

body.homepage にある背景が白い .widget がかっこいいから body.aboutpage でも使いたいな。

body.homepage .widget,
body.aboutpage .widget {
  background: white;
}

こう書くと、使う場所が増えるごとに追記していかなくてはならなくなる。できれば、同じコードはもう書かなくていいほうが、いいよね。

(3) 保守しやすい

サイトに新しいコンポーネントと機能が追加・更新されるか、再編される必要があるとき、既存の CSS のリファクタリングを必要とすべきではない。ページにコンポーネントX を追加するときに、そのわずかな存在によってコンポーネントY を壊すべきではない。

リニューアルすることになりました、.widget の背景はピンクになります!ってなっても、対応しやすいだろうか。コピペを繰り返していたら、どうなるだろう。全部書き換えなくちゃ、漏れがあったら修正しなくちゃってなったら、どうしよう。

(4) 拡張しやすい

サイトが大きく、複雑に成長していくにつれて、通常はたくさんのデベロッパがメンテナンスのために必要となる。 拡張しやすい CSS とはひとりのデベロッパか、大きなエンジニアチームかを問わず、容易に管理できることを意味する。またそのサイトの CSS アーキテクチャに、巨大な学習曲線を必要することなく容易に近づけるという意味でもある。あなたが今日 CSS を触る唯一のデベロッパだからといって、先々にも常にあなただけであるというわけではない。

この CSS を触るのが自分じゃなかったら。自分しか触らないとしても、半年後の自分だったら。class 名が l とか r で、left とか right のことって、わかるだろうか。そもそも半年後、この要素がページの左にあったり右にあったりするだろうか。

うーん、これから僕たちが目指す「運用しやすい」CSS とはどういうものなのか、なんとなくわかってきたような気がする。

それに、つらいのは僕たちだけじゃないんだ。みんなつらい思いをしてるから、もうすでにいろんな設計手法が考えられているみたいです。

代表的な CSS 設計手法

この項目の担当 @otthi

ここで紹介する CSS 設計は OOCSS を代表とする多くのプロジェクトで用いられている設計手法です。 いずれの設計手法も前項の「運用しやすい」CSS を具現化したものであるという点において共通しています。

既存プロジェクトの CSS に立ち向かう!

この項目の担当 @otthi

さて、これまで見てきた考え方や設計手法はいわば理想的なものです。しかし、我々の眼前にはすでに 1万行くらいに肥大化してしまった CSS が立ちはだかっています(!)これまで見てきたものを既存のプロジェクトの CSS にどう適用していけばよいでしょうか。

ここでは既存のプロジェクトの肥大化した CSS を以下の点にポイントを置きつつコンポーネント化して整理する流れを考えてみます。

(0) 流れ

(1) 既存の CSS ファイルを元に SCSS ファイルに変換する

例えば、既存の CSS ファイルが old_version.css だとしたら old_version.scss に変換し、特定のディレクトリに置きます。

└── assets
    └── scss
        └── old_version.scss

(2) イニシャライズ CSS や共通の箇所のスタイルを分離する

old_version.scss に記述されているイニシャライズ CSS やヘッダー・フッターのスタイルを各 SCSS ファイルにまとめ直します。

└── assets
    └── scss
        ├── _base.scss   // イニシャライズ CSS
        ├── _layout.scss // ヘッダーやフッターの CSS
        ├── common.scss  // _base.scss や _layout.scss を @import する
        └── old_version.scss

(3) CSSLint を使って、修正しやすいところから整理していく

主に基本的におさえておいた方がいい項目のエラーをつぶす。

その他にも CSSLint では色々な警告を通知してくれますが、状況や必要に応じてチェックしてみてください。

ハック

ハックを使っていると、新しいブラウザで予期しない表示になってしまうことがあるため、極力避けた方がよいです。

ルールの優先度

ブラウザ互換性

(4) コンパイル

compass などで SCSS ファイルのコンパイルの設定を行います。

├── assets   // コンパイル前のファイルを置くディレクトリ
│   └── scss
│       ├── _base.scss
│       ├── _layout.scss
│       ├── common.scss
│       └── old_version.scss
└── public  // 公開用ファイルを置くディレクトリ
    └── css // コンパイル後のCSSファイルを置くディレクトリ
        ├── common.css
        └── old_version.css

(5) スタイルのスコープ(あえて)

page_A.html

<div class="scope--page-a">
  ...(ヘッダー・フッターなどを除いたpage_A.htmlのコンテンツ)
</div>

page_a.scss

.scope--page-a {
  ... /* page_A.html用のルールを記述 */
}
├── assets
│   └── scss
│       ├── _base.scss
│       ├── _layout.scss
│       ├── common.scss
│       ├── old_version.scss
│       └── pages
│           ├── page_a.scss
│           └── page_b.scss
└── public
    ├── css
    ├── page_A.html
    └── page_B.html

リファクタリングを行う際のポイントと FLOCSS について

リファクタリングを行う際のポイントはゴールになる全体像をイメージしておくことだと思います。「どの CSS 設計手法をベースとして用いるか」「どの部分のルールを参考にするか」など、プロジェクトの内容や規模によってリファクタリングしやすい設計ルール(全体像)を決めましょう。

なお、筆者は代表的な CSS 設計手法のうち、FLOCSS の考え方をリファクタリングの際に取り入れることがしばしばあります。FLOCSS をベースにする理由を紹介したいと思います。

(1) 理由その1 カテゴライズしやすい

既存の CSS でも、いくつかのカテゴリーに分けられているかと思います。FLOCSS の CSS 設計にある「レイヤー」はそのカテゴリー分けに近い考え方になので置き換えやすくなっています。

FLOCSS 既存 CSS
Foundation イニシャライズ CSS
Layout ヘッダー・フッターなどのレイアウト部分
Object その他メインコンテンツの各部品など

(2) 理由その2 ルールと役割を分けて考えることができる

既存の CSS では「その他メインコンテンツの各部品など」の部分がカオスになっていることが多いですが、FLOCSS では「Object」を更に 3つのカテゴリーに分けていることにより、リファクタリングを進めていく中で役割をはっきり切り分けることができます。

  1. Component
  2. Project
  3. Utility

ref: FLOCSS

Component とは

Project とは

Utility とは

(3) 理由その3 既存クラスとリファクタリング後のクラスを分けやすい

FLOCSS では Object に含まれる Component・Project・Utility のクラス名には、それぞれプレフィックスをつけるルールになっています。また MindBEMding の命名規則を取り入れています。

既存プロジェクトのコードにもよりますが、プレフィックスや独特の命名規則によって、要素に指定されているクラス名がリファクタリング前・後のどちらのクラスかがパッと見で判断することができます。

レイヤー プレフィックス
Component .c-**
Project .p-**
Utility .u-**
├── assets
│   └── scss
│       ├── foundation
│       │   ├── _normalize.scss    // イニシャライズ CSS
│       │   ├── _typography.scss   // フォント
│       │   └── _base.scss         // 背景
│       ├── layout
│       │   ├── _header.scss
│       │   ├── _sidebar.scss
│       │   └── _footer.scss
│       ├── object
│       │   ├── compornent
│       │   │   ├── _button.scss
│       │   │   ├── _grid.scss
│       │   │   ├── _alert.scss
│       │   │   └── _icon.scss
│       │   ├── project
│       │   │   ├── _article.scss  // コンテンツ・記事
│       │   │   └── _product.scss  // 商品・ギャラリー
│       │   └── utility
│       │       ├── _align.scss    // 例外的に行うデザイン調整
│       │       └── _helper.scss   // ヘルパー class
│       ├── _settings.scss
│       ├── common.scss
│       ├── old_version.scss
│       └── pages
│           ├── page_a.scss
│           └── page_b.scss
└── public
    ├── css
    ├── page_A.html
    └── page_B.html

ハッ!

なんだ、夢か…。でも、がんばろう。

参考文献