第1章 CSS の歴史と Tailwind CSS
1.1 CSS は何を解決するために生まれたのか
CSS(Cascading Style Sheets)は 1996 年に登場しました。それ以前の Web ページは、見た目の指定を HTML の中に直接書いていました。たとえば文字を赤くするには、次のように <font> タグを使いました。
<font color="red" size="5">セール開催中</font>
この書き方には大きな問題がありました。同じ「赤い見出し」をページ全体で 100 か所使っていたら、色を変えるときに 100 か所すべてを書き換えなければならないのです。見た目(赤い・大きい)と構造(これは見出しである)が、同じ場所にべったり貼り付いていました。
CSS はこの問題を解決するために生まれました。CSS の理想はこうです。
- HTML は文書の構造だけを書く(これは見出し、これは段落、これはリスト)
- CSS は見た目だけを別ファイルに書く(見出しは赤く大きく)
<!-- HTML: 構造だけ -->
<h1 class="headline">セール開催中</h1>
/* CSS: 見た目だけ */
.headline {
color: red;
font-size: 2rem;
}
こうしておけば、色を変えたいときは CSS の 1 か所を直すだけで、ページ全体に反映されます。この「構造と見た目を分ける」という考え方を 関心の分離(Separation of Concerns) と呼びます。これは長らく Web 制作の「正しい作法」とされてきました。
1.2 「関心の分離」の理想と現実 — HTML と CSS は本当に分離できたのか
ところが、現場で大きなアプリケーションを作っていくと、理想どおりにはいかないことが分かってきます。
問題は、「構造と見た目を別ファイルに置けば、本当に分離できているのか?」という点です。次の HTML と CSS を見てください。
<div class="hero">
<h1 class="hero-title">ようこそ</h1>
</div>
.hero-title {
color: white;
font-size: 3rem;
font-weight: bold;
}
たしかにファイルは分かれています。しかし hero-title というクラス名は、HTML 側にも CSS 側にも書かれていて、両者は名前で固く結びついています。HTML を見れば「hero-title という CSS があるはずだ」と分かり、CSS を見れば「hero-title という HTML 要素があるはずだ」と分かる。つまり、ファイルは別でも、お互いがお互いを知っているのです。
これは本当に「分離」と呼べるのでしょうか。後ほど第3章で詳しく扱いますが、この「ファイルは分かれているのに依存し合っている」という違和感が、のちに Tailwind CSS が生まれる思想的な出発点になります。ここでは「関心の分離は理想として語られたが、現実には HTML と CSS は名前を介して強く結びついていた」という事実を覚えておいてください。
1.3 CSS が大規模化で壊れる理由
CSS は小さなサイトではうまく機能します。問題は、ページ数が増え、関わる人が増え、何年も運用されるような大規模なプロジェクトで起きます。CSS には、規模が大きくなると牙をむく 3 つの性質があります。
(1) グローバルスコープ
CSS のセレクタは、原則としてページ全体に効きます。.title { color: red; } と書けば、ページ上のすべての .title が赤くなります。これは便利な反面、危険です。誰かが別の場所で .title を定義すると、意図しない場所まで巻き込んでしまいます。JavaScript には関数スコープやモジュールがありますが、素の CSS には「この CSS はこのコンポーネントの中だけ」という仕組みがありませんでした。
(2) 詳細度(Specificity)
CSS には、複数のルールがぶつかったときにどちらが勝つかを決める「詳細度」というルールがあります。たとえば #main .title は .title より詳細度が高く、勝ちます。大規模なプロジェクトでは、この詳細度の競り合いが起きます。「なぜか色が変わらない」「!important を付けないと効かない」といった経験をした人は多いはずです。詳細度の戦いがエスカレートすると、CSS は誰にも制御できなくなっていきます。
(3) ソース順への依存
CSS の「C」はカスケード(cascade)——複数のルールがぶつかったとき、どれを適用するか決めるアルゴリズム——を意味します。カスケードは「origin(どこで定義されたか)→ 詳細度 → ソース順(後に書いた方が勝つ)」の順で勝者を決めます。問題は、この最後の「ソース順」です。詳細度が同じルールがぶつかると、CSS ファイルのどこに書いたかで勝敗が変わります。大規模なプロジェクトでは、ファイルの読み込み順や記述位置のわずかな違いで、意図せず別のスタイルが勝ってしまう——「順番に依存して壊れる」という予測しづらさが生まれます。
補足: よく「カスケード」と混同されますが、親要素の値が子に伝わる継承(inheritance)は別の仕組みです。継承は値が宣言されていない場合の補完であり、ここで言う「壊れやすさ」の原因はカスケード(とりわけソース順への依存)の方です。
この 3 つが組み合わさると、大規模な CSS では「この CSS を消したら、どこが壊れるか分からない」という恐ろしい状態が生まれます。怖くて消せないので、古いスタイルが残り続け、CSS ファイルはひたすら肥大化していきます。CSS は、書いた量に比例して、いえ、それ以上に管理コストが膨らんでいくのです。
1.4 命名規則による戦い — OOCSS / SMACSS / BEM の登場と限界
この「壊れやすさ」に立ち向かうため、開発者たちは命名規則(ネーミングのルール)で秩序を作ろうとしました。代表的なものが OOCSS・SMACSS・BEM です。
- OOCSS(Object Oriented CSS, 2009 年ごろ): 再利用できる「オブジェクト」として CSS をとらえ、構造と見た目(スキン)を分けて考える。
- SMACSS(2011 年ごろ): CSS を Base / Layout / Module / State / Theme の 5 種類に分類して整理する。
- BEM(Block Element Modifier): 最も広く普及した命名規則。
BEM では、クラス名を ブロック__要素--修飾子 という形で構造的に付けます。
<div class="card">
<h2 class="card__title">タイトル</h2>
<button class="card__button card__button--primary">送信</button>
</div>
card がブロック(独立した部品)、card__title がその中の要素、card__button--primary が修飾子(バリエーション)です。クラス名を見ただけで構造が分かり、しかもクラス名が長く具体的なので詳細度の戦いやグローバルスコープの事故が起きにくくなります。BEM は、CSS の壊れやすさに対する優れた処方箋でした。
しかし、BEM にも限界がありました。
- 命名がとにかく大変: 部品を作るたびに「これは何という名前のブロックか」「この要素は何と呼ぶか」を考え続けなければなりません。
dashboard-card__action-label--activeのような長く具体的な名前を、部品の数だけ考え続けることになります。プログラミングで「最も難しいのは命名だ」とよく言われますが、BEM はその命名を CSS の隅々まで強制します。 - CSS は結局増え続ける: 新しい部品を作るたびに、新しいクラスと新しい CSS ルールが増えます。
card__titleとarticle__titleがほとんど同じスタイルでも、別の名前なので別々に書くことになりがちです。 - HTML と CSS の往復が続く: 見た目を少し変えたいとき、HTML でクラス名を確認し、CSS ファイルを開き、該当箇所を探して直す、という往復が必要です。
つまり BEM は「壊れにくさ」を手に入れた代わりに、「命名コスト」と「CSS が増え続ける問題」は解決できませんでした。
1.5 CSS-in-JS とコンポーネント志向の波
2013 年に React が登場し、Web 開発はコンポーネント志向へと大きく舵を切りました。UI を「ボタン」「カード」「ヘッダー」といった部品(コンポーネント)の組み合わせとしてとらえ、それぞれを独立した単位として扱う考え方です。
コンポーネントは、構造(HTML/JSX)・振る舞い(JavaScript)・見た目(CSS)を 1 つの部品としてまとめたいという欲求を生みました。そこで登場したのが CSS-in-JS です。styled-components(2016 年)や Emotion などが代表例で、JavaScript のファイルの中に CSS を書きます。
const Button = styled.button`
color: white;
background: blue;
padding: 8px 16px;
`;
CSS-in-JS は、CSS のグローバルスコープ問題を解決しました。各コンポーネントに自動でユニークなクラス名が割り振られるため、スタイルが他へ漏れません。「コンポーネントと一緒にスタイルが移動する」という再利用性も得られました。
一方で、CSS-in-JS にも代償がありました。実行時にスタイルを生成するライブラリでは、ページ表示のたびに JavaScript で CSS を組み立てるためパフォーマンスのコストがかかります。また、結局「コンポーネントごとに固有のスタイルを書く」点は BEM と同じで、デザインに一貫性をもたらす仕組み(余白や色を揃える仕掛け)は別途用意する必要がありました。
1.6 「CSS を書かない」という発想の系譜
ここまでの流れ(BEM も CSS-in-JS も「部品ごとに固有の CSS を書く」発想)とは、まったく別の方向から問題に挑む系譜がありました。Atomic CSS / Functional CSS と呼ばれる考え方です。
この発想はシンプルです。「1 つの CSS プロパティだけを担当する、小さなクラスをあらかじめ大量に用意しておき、それらを HTML 側で組み合わせて見た目を作る」というものです。たとえば次のように。
<div class="flex p-4 bg-white rounded shadow">...</div>
flex は display: flex だけ、p-4 は内側の余白だけ、bg-white は背景色だけ、というように、1 クラス 1 役割の小さな部品(これをユーティリティクラス、単一の役割だけを持つ小さなクラス、と呼びます)を組み合わせています。
この系譜には先行者がいました。
- Tachyons(2014 年ごろ): 「ブラウザの中でデザインするための、関数型 CSS ツールキット」を掲げ、小さな単機能クラスを組み合わせてデザインする手法を確立しました。Tachyons 自身が「これは単なる CSS フレームワークではなくデザインシステムだ」と述べている点は重要です。後の Tailwind の思想を先取りしています。
- Basscss・Atomic CSS(Yahoo! の ACSS) など、同時期に複数の試みがありました。
このアプローチは、当時「インラインスタイルと同じではないか」「HTML が汚い」と強い批判を浴びました(この批判は今も Tailwind に向けられ続けています。第28章で正面から扱います)。しかし同時に、「部品ごとに CSS を書かないので、CSS がほとんど増えない」「クラスを組み合わせるだけなので命名で悩まない」という、BEM や CSS-in-JS が解決できなかった問題への明確な答えでもありました。
1.7 この歴史の上に Tailwind CSS がどう位置づくか
ここまでの歴史を整理すると、CSS は次の課題と戦い続けてきたことが分かります。
| 時代 / 手法 | 解決したこと | 残った課題 |
|---|---|---|
| 素の CSS | 構造と見た目の分離 | グローバル・詳細度・カスケードで壊れる |
| BEM など命名規則 | 壊れにくさ | 命名コスト、CSS が増え続ける |
| CSS-in-JS | スコープ・コンポーネント化 | 一貫性は別途必要、実行時コスト |
| Atomic / Functional CSS | CSS が増えない、命名不要 | 「HTML が汚い」批判、開発体験が未成熟 |
Tailwind CSS は、この一番下の Atomic / Functional CSS の系譜に位置します。ただし Tailwind が画期的だったのは、思想を発明したことではありません。Tachyons などの先行者がすでに示していた「ユーティリティを組み合わせる」という考え方に、
- 制約のある統一されたデザインシステム(余白・色・サイズがあらかじめ整ったスケールになっている)
- 必要なクラスだけを自動生成する仕組み(HTML が汚い以前に、まず巨大な CSS にならない工夫。第4章で扱います)
- 優れた開発体験(エディタ補完、レスポンシブやホバーへの対応)
を組み合わせ、実務で本当に使える完成度にまで仕上げた点にあります。
次の第2章では、この Tailwind を作った人物 Adam Wathan が、まさにここで挙げた課題のどれに困り、何を考えて Tailwind を生み出したのかを、本人の言葉(一次情報)から追っていきます。