Moving away from Tailwind, and learning to structure my CSS

Julia Evans

Tailwindから離れて、CSSの設計を学ぶ

こんにちは!8年前に、Tailwindとの出会いについて興奮しながら書いたことがあります。

当時の私はCSSのコードをどう整理すればいいのかまったくわかっておらず、混沌としたコードの山とTailwindのどちらかを選べと言われれば、喜んでTailwindを選びました。おかげで小さなサイトをたくさん作ることができました!

この1週間ほど、いくつかのサイトをTailwindから、よりセマンティックなHTMLと素のCSSへ移行する作業をしていました。とにかく楽しくて、めちゃくちゃ面白かったので、学んだことをここにまとめてみます!

いつものことですが、私は専業のフロントエンド開発者ではないので、CSSの学習はここ何年も断続的に、少しずつ進めてきたものです。

気づけばTailwindにたくさん教わっていた

CSSの設計について考え始めたとき、最初は少し気後れしました。自分はCSSの設計があまり得意ではないからです。でも、CSSの設計について書かれたブログ記事(たとえばA whole cascade of layersHow I write CSS in 2024など)を読んでいくうちに、いくつか気づいたことがありました。

  1. どんなCSSのコードベースにも、さまざまな要素が詰まっています(レイアウト、フォント、色、共通コンポーネントなど)。
  2. それぞれを管理するための仕組みやガイドラインを持っておくと非常に役立ちます。そうしないと、すぐに混沌としてしまいます。
  3. Tailwindはそのうちのいくつかに対して仕組みを用意してくれていて、しかも私はすでにそれを使い慣れています。気に入っている仕組みを真似してみればいいのかもしれません!

たとえばTailwindには次のようなものがあります。

これから取り上げる仕組み

ここではCSSコードベースのいくつかの側面について、それぞれにどんなルールを設けたいと考えているかを紹介します。一部はTailwindから取り入れたもので、一部はそうではありません。

  1. リセット
  2. コンポーネント
  3. フォントサイズ
  4. ユーティリティクラス
  5. ベース
  6. 余白
  7. レスポンシブデザイン
  8. ビルドシステム

1. リセット

まずはTailwindの「Preflightスタイル」を、tailwind.cssの最初の200行ほどをそのままコピーして持ってきました。

こうして気づいたのですが、いつの間にかTailwindのCSSリセットにすっかり慣れ親しんでいました。たとえばTailwindはすべての要素にbox-sizing: border-boxを指定します(要素の幅にpaddingが含まれるという意味です)。

* { box-sizing: border-box; }

これなしでCSSを書くとなると、かなり慣れ直しが必要になると思います。Tailwindのリセットには、html {line-height: 1.5;}のように、無意識に頼っていて存在すら気づいていないものが他にもたくさんあるはずです。

2. コンポーネント

ここからがCSSの大部分を占めるところです!

ここでの考え方は、CSSを「コンポーネント」単位で整理するというものです。VueやReactのコンポーネントと発想は似ています(といっても、サイトにJavaScriptがまったくなくても構いません)。

基本的な考え方は次のとおりです。

  1. 各「コンポーネント」は一意のクラスを持つ
  2. あるコンポーネントのCSSが、他のコンポーネントのCSSを上書きすることはない
  3. 各コンポーネントは独自のCSSファイルを持つ

そのため、あるコンポーネントのCSSを編集しても、別のコンポーネントが謎の不具合を起こすことはありません。そして実際に変更したいCSSの8割方は各コンポーネントのファイルにまとまっているので、100行程度のコンポーネントを編集するときは、その100行だけを考えればよいのです。私にとっては格段に考えやすくなりました。

たとえば、次のHTMLは.zineという「コンポーネント」です。

<figure class="zine horizontal">
    <img src="whatever.jpg">
</figure>

そしてCSSはネストしたセレクタを使って、こんな感じになります。

.zine {
  ...
  &.horizontal {
    ...
  }
  &.vertical {
    ...
  }
  &:hover {
    ...
  }
}

ウェブコンポーネントや@scopeのように、プログラム的にコンポーネント同士の干渉を防ぐ仕組みはまだ導入していません。ただ、規約を決めてできるだけ守るだけでも、すでに大きな改善に感じられます。

次は、サイト全体の一貫性を保ち、これらのコンポーネントを足並み揃えるための規約についてです!

3. 色

colours.cssには、必要に応じて使える変数がこんなふうにまとめてあります。色は本当に難しいので、今回のリファクタリングで色の使い方を見直すことはせず、そのままにしました。

ここで守ろうとしているルールはひとつだけで、サイトで使う色はすべてこのファイルに列挙するということです。

:root {
  --pink: #fea0c2;
  --pink-light: #F9B9B9;
  --red: #f91a55;
  --orange: rgb(222, 117, 31);
  ...
}

4. フォントサイズ

Tailwindで気に入っていたことのひとつは、フォントサイズを決めたいときに「うーん、大きくしたいな」と思ったらtext-lgと書くだけで済むことでした。もしそれでも小さければxl2xlにすればいい。emなのかpxなのかremなのかをいちいち思い出す必要もありません。

そこで、Tailwindから拝借して、こんなふうに変数を定義しました。

  --size-xs: 0.75rem;
  --line-height-xs: 1rem;

  --size-sm: 0.875rem;
  --line-height-sm: 1.25rem;

あとはフォントサイズを設定したいときに、こんなふうに書けます。Tailwindより少し冗長ですが、今のところはこれで満足しています。

h3 {
  font-size: var(--size-lg);
  line-height: var(--line-height-lg);
}

5. ユーティリティ

ボタンのように、さまざまなコンポーネントにまたがって登場するものがあります。こういったものを「ユーティリティ」と呼んでいます。

Tailwindからいくつかのユーティリティクラスをコピーしてきました(たとえばスクリーンリーダーのユーザーにだけ見せたいもののための.sr-onlyなどです)。

このセクションはかなり小さく、変更する際は慎重になるようにしています。

6. ベース

「ベース」スタイルは、サイト全体に適用する自分で選んだスタイルです。サイト全体に多くのスタイルを強制する自信がないので、このセクションはとても小さく保つ必要があります。今のところ納得できているのは次の2つだけで、<section>の方は今後変えるかもしれません。

/* put a 950px column in the middle of each <section> */
section {
  --inner-width: 950px;
  padding: 3rem max(1rem, (100% - var(--inner-width))/2);
}

a {
  color: var(--orange);
}

ベーススタイルについては、ボトムアップで進めるのが私には一番やりやすそうです。まずはベースにほとんど何も入れない状態から始めて、共通で使いたいものが見つかったらコンポーネントからベースへ移していく形です。

7. 余白

paddingやmarginの管理方法については、まだ完全には固まっていません。ただ、Tailwindでやっていたように、見た目が整うまであちこちに場当たり的にpaddingやmarginを置いていくやり方よりは、もう少し筋の通った方法を目指しています。

今は、できるだけ外側のレイアウトコンポーネントに余白の管理を任せる方向で考えています。たとえば、たくさんの子要素を持つ<section>で、子要素の間に均等に余白を入れたい場合、次のように書くことがあります。

section > *+* {
  margin-top: 1rem;
}

参考にしたブログ記事:

8. レスポンシブデザイン:もっとグリッドを使おう!

Tailwindでレスポンシブデザインをしていたときは、メディアクエリをたくさん使っていました。Tailwindにはmd:text-xlという記法があり、これは「md以上のサイズでtext-xlのスタイルを適用する」という意味です。

今はかなり違うアプローチを試しています。ブレークポイントをそれほど必要としない、より柔軟なCSSグリッドのレイアウトを作るというものです。難しいですが、グリッドで何ができるのかを学ぶのは本当に面白く、これはTailwindでは実現が難しいことの良い例だと思います。

たとえば、auto-fitを使って大きな画面では自動的に2カラム、小さな画面では1カラムに切り替える方法を学んでいるところです。次のようにします。

  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 400px), max-content));
  justify-content: center;

また、grid-template-areasも多用しました。これは素晴らしい機能で、Tailwindでは使えないのではないかと思います。

参考にしたもの:

9. ビルドシステム:esbuild

開発中はビルドシステムは必要ありません。今のCSSには次のような組み込みのimport文があるからです。

@import "reset.css";
@import "typography.css";
@import "colors.css";

さらに次のような組み込みのネストセレクタもあります。

.page {
  h2 { ...}
}

必要であれば、本番用にesbuildでCSSファイルをバンドルすることもできます。こんな感じです。

esbuild style.css --bundle --loader:.svg=dataurl  --loader:.woff2=file --outfile=/tmp/out.css

普段はCSSやJSのビルドシステムを避けているのですが、esbuild(2021年にこちらで書いたことがあります)はウェブ標準に則っていて、しかもGoの静的バイナリなので、使うことに抵抗がありません。

なぜTailwindから離れたのか

なぜTailwindから移行したのかと何人かに聞かれたので、理由になったことをいくつか挙げます。

  • Tailwindは2018年以降、ビルドシステムへの依存度がずっと高くなりました。新しいバージョンのTailwindをビルドシステムなしで使うのは不可能(?)だと思います。そのため私は長年Tailwind v2を使い続けてきました(最近はlitewindというものもあるようです)。
  • 本来Tailwindはビルドシステムと一緒に使うものなのですが、私はあまりそうしてこなかったので、多くのプロジェクトで2.8MB(gzipで270KB)のtailwind.min.cssを抱えることになり、少し間抜けに感じていました。
  • Tailwindを使い始めた頃よりも、CSSがずっと得意になった
  • 結局のところTailwindには制約があります。CSSでちょっと変わったことをやろうとしても、Tailwindでは必ずしも実現できません。その制約はとても役に立つこともあります(この記事の多くはTailwindの制約を自分で再実装する話でもあります!)が、今は自分で取捨選択できるようになりたいと思っています。
  • 同じプロジェクト内で素のCSSとTailwindが混在するサイトができてしまい、メンテナンスが楽しくなかった
  • よりセマンティックなHTMLを書くのはどんな感じなのか、好奇心が湧いた

気になっているCSSの機能

今回の作業を通じて、まだ使ってはいないけれどいつか学んでみたいCSSの機能をたくさん知りました。

Tailwindから離れたもうひとつの理由

この記事ではTailwindから学んだことをたくさん書いてきましたが、それはすべて本当のことです。

ただ、3年前に読んだTailwind and the Femininity of CSSという記事がずっと心に残っています。正直なところ、私も最初はその記事で書かれているような態度でCSSに向き合っていたのかもしれません。

シンプルだと聞いたから、簡単なのだろうと思い込む。でも実際に使ってみるとうまくいかない。自分は賢いはずで、これは簡単なはずなのだから、きっと言語が悪いのだろう、と考えてしまうのです。

でもこの10年で、私はCSSという技術を心から好きになり、尊敬するようになりました。

だから何年か前に、「CSSは難しい」という感想に対して、CSSを軽んじるのではなく、CSSを上達して技術として真剣に向き合うことで応えたいと思ったのです。そう決めてからすべてが変わりました。私を悩ませていた多くのこと(「中央寄せなんて不可能」など)はとっくの昔にCSSで解決されていたこと、そして「中央寄せ」が何を意味するのか自体がそもそも単純ではなく、やり方がたくさんあるのも当然なのだと学びました。CSSが難しいのは、難しい問題を解こうとしているからなのです!

この10〜15年で追加された新しいCSSの機能(この記事でもいくつか触れました!)や、それらがCSSを使いやすくしていることにすっかり感心していますし、時間をかけてCSSのスキルを磨くのは本当に素晴らしい経験でした。

そしてその記事を読んで、TailwindはCSSの専門性を軽んじることに加担しているように感じ、それには与したくないと思ったのです。個人的にはTailwindが役に立ったことは確かですが。とりわけLLMの時代である今、人間の専門性を大切にすることがこれまで以上に重要だと感じています。

影響を受けた、Tailwindを批判するもうひとつのブログ記事はこちらです。

今回はここまで!

wizardzines.comのデザインとCSSを最初に手がけてくれたMelody Starlingさんに感謝します。サイトのクールで楽しいところはすべてMelodyさんのおかげです。

また、今回の作業では(CSS TricksSmashing Magazineをはじめとする)素晴らしいCSS関連のブログ記事をたくさん読みました。この記事の中でもいくつかリンクしていますが、CSSコミュニティのみなさんが実践を惜しみなく共有してくれることに心から感謝しています。

原文は Julia Evans により に公開されました。

この記事は「muse-spark-1.2-contributor」を使用して翻訳されました。