CSSが破綻する4つの理由

  • 781
    Like
  • 0
    Comment
More than 1 year has passed since last update.

前回の「破綻しにくいCSS設計の法則 15」は思いがけず大変なご好評をいただきました。ただ書いた当人としては、まとまりに欠けていたように思えましたので、今回はもう少し本質的なところを書いてみたいと思います。

  1. CSSとは実際のところ何か
  2. CSSが破綻する理由
  3. 破綻しないCSS設計とは

1. CSSとは実際のところ何か

CSSはJSONと同様にシンプルなフォーマットで、基本的には以下のような構造になっています。

css
セレクタ {
  プロパティ: バリュー;
  プロパティ: バリュー;
}

セレクタ {
  プロパティ: バリュー;
  プロパティ: バリュー;
}

「プロパティとバリュー」のペアをセレクタが束ねていて、それが延々と続きます。もう少し概略化するとこうです。

css
呼び方 {
  スタイル
}

呼び方 {
  スタイル
}

いずれかのスタイルを利用したい場合、HTML側からCSSに書かれている「呼び方」で呼びます。意図せずに呼んでしまったとかは関係ありません。「呼び方」にマッチする内容がHTML側にあれば、スタイルは自動的に適用されます。

例えば、

css
a { text-decoration: underline; }

のようにタグを使用しただけで適用されるものから、

css
body > div { height: 100%; }

のように、構造的にマッチしないと呼び出されないものもあります。

一般的にはclassで呼び出します。

css
.some-box {
  margin: 8px;
  border: 1px solid #999;
}

このように単純な仕組みでありながら管理を難しくしてしまうのは、CSSの以下の特性によるものです。

  • 「呼び方」には強さ(詳細度)という尺度がある
  • スタイルには子要素にまで影響(または継承)するものがある
  • 同じ「呼び方」を何度でも多重に定義できる
  • HTML側の各要素はスタイルを複合的に受け入れる

2. CSSが破綻する理由

CSSには書き順とは別に詳細度という「呼び方の強さ」が存在しているため、大声で呼ばれたスタイルほど書き順を無視して適用されます。よって、各セレクタの詳細度を調整しない場合、CSSは上書き合戦に陥って破綻します。

書き順が無視される例:

css
/* 詳細度 11 */
input[type="text"] {
  background: red;
}

/* 詳細度 10 */
.input--text {
  background: white;
}

CSSのスタイルは子要素にまで影響(または継承)するものがあるため、DOMの上流で適用するスタイルについては慎重に検討する必要があります。不用意なスタイルを上流で適用すると、下流で大量の上書きを必要とし、CSSはいずれ汚くなって破綻します。

記述量が増える一方の例:

css
body {
  font-size: 10px;
}

#container p {
  line-height: 1;
}

CSSは一度定義した「呼び方」であっても、別の場所でスタイルの追記や上書きができてしまいます。よって、定義場所についての方針がない場合、CSSはいずれカオスになって破綻します。

定義が重複している例:

css
.button--submit {
  background-color: green;
}

/* 〜 結構な長さの中略の後 〜 */

.button--submit {
  background-color: blue;
}

あなたがシングルクラス派かマルチクラス派かを問わず、HTMLの各要素はスタイルを複合的に受け入れます。ほとんどの場合、各要素は3つ以上のスタイルを統合して最終的な見栄えを決定しています(これをcomputed styleと言います)。この「組み合わせの妙」をコントロールできない場合、CSSはその目的を果たせずに破綻します。

合成結果が予測できない例:

html
<a href="/" class="btn sub ib l-component">Go home</a>

上記をまとめると、

  • 詳細度が管理されていない場合、
  • 上流のスタイル定義が乱雑な場合、
  • 定義場所についての方針がない場合、
  • 「組み合わせの妙」をコントロールしきれない場合、

遅かれ早かれ、CSSは破綻します。

3. 破綻しないCSS設計とは

破綻する理由がだいぶ見えてきたので、今度はその対策を見ていきたいと思います。下記の4点が上記のそれぞれに対応しています。

  1. 詳細度管理
  2. コンポーネント指向
  3. ファイル管理
  4. 命名規則

詳細度管理

セレクタの詳細度は、上書き合戦にならないように注意しなければなりません。最も簡単な実現方法は、各セレクタの詳細度が10になるようにキープすることです。すると、あとは書き順だけを気にすればよくなります。

ただし例外的に詳細度が高騰してしまうケースもあります。そういう場合は、コンポーネントの外側から上書きしようせずに、内部的に解決しましょう。

css
/* 詳細度 20 */
[type="checkbox"] + .some-label {
  color: blue;
}

/* 詳細度 30 */
[type="checkbox"]:checked + .some-label {
  color: red;
}

コンポーネント指向

DOMの上流にある要素ほど慎重に取り扱う必要があります。逆を言えば、下流の要素は好き勝手にできることを意味します。コンポーネント指向は、他のプラットフォームと同様にウェブでも上手く機能する手法です。コンポーネント指向になるようにデザインしましょう。上流は薄く、下流を厚めに設計します。

ファイル管理

定義場所についての方針が明確でない場合、運用期間が長くなるにつれて重複記述が増えたり、命名の衝突が起きたりします。CSSはエラーを特に吐きません。管理方法でカバーする必要があります。

管理方法としてはclass名に接頭辞を付けるようにし、接頭辞ごとにファイルを分ければ、定義場所が散らばることもなく管理も容易です。

_box.sass
.box-foo
  margin: 8px
  border: 1px solid #999

.box-bar
  margin: 8px
  background: #ccc

命名規則

CSSは「なんという呼び方でどういうスタイルを連れて行くか」ということしかやりません。命名は作業の半分を占め、その重要性は残りの作業以上です。命名の枯渇を回避するために、接頭辞の使用やBEMの採用は、スケーラビリティを高めるのでいいアイデアだと思います。

ただし規則的な命名を心がけるだけでは不十分な場合もあります。ある要素におけるcomputed styleが予測困難になりがちな理由に、リセットCSSがあります。

分かりやすい例として、

html
<button type="submit" class="button button--submit">送信</button>

<a href="/submit" class="button button--submit">送信</a>

上の2つは同じclassを呼んでいますが、表示結果は異なるかもしれません。buttonaで、要素自体が元々持っているスタイルが異なりますし、どうリセットされているかも分かりません。リセットCSSは今も変わらず大きな問題です。

解決方法としては、normalize.cssでリセットを施し、それ以上のリセットは全てclassにて提供する方法があります。従来の手法よりもずっとcomputed styleが予測しやすくなりますので、試してみてください。

謝辞

最後までお読みいただき、ありがとうございました。以上が「破綻しないCSS」のための方法論になります。方法論としてはBEMよりもカバー範囲が広く、SMACSSに影響を受けているため、個人的に「APICSS」と呼んでいます。APIのように安定して利用できるCSSが増えることを期待しています。

追記

もう少し深い内容について、こちらに書きました。興味のある方は合わせてお読みください。

備考

破綻しにくいCSS設計の法則 15」の各章とは以下のように対応させています。

  • 詳細度管理(9, 11, 12, 13, 15)
  • コンポーネント指向(1, 2, 4, 5, 6)
  • ファイル管理(3, 4, 10, 14, 15)
  • 命名規則(1, 2, 7, 9)