第27章 Tailwind CSS のアンチパターン
この章は「禁止集」ではありません。挙げる項目は、どれも「絶対に使うな」というものではなく、「なぜ問題になりやすいか、いつなら許容できるか、どう判断するか」を示すものです。アンチパターンの多くは、道具自体が悪いのではなく、使いどころを誤ったときに痛むものです。判断基準として読んでください。
27.1 任意の値([...])の乱用
何が起きるか: p-[13px]・text-[#1a1a1a]・w-[327px] のような任意の値(第4章)が画面中に散らばると、第3章で得た「スケールという制約」が崩れ、デザインの一貫性が失われます。
なぜ問題か: 任意の値が多い場合、チームの採用スケールに沿っているかを見直す必要があります。数値が生成できるかとは別の判断です。1 つ 2 つなら問題ありませんが、増えてくると「テーマが整っていない」「デザインがスケールに沿っていない」という、より根本的な問題の兆候です。
判断基準: 「この値は本当に唯一無二か?」と問います。p-4 や p-5 で代用できないか。何度も使う値なら、[...] で散らすのではなく、テーマ変数として名前を付ける(第5章)。単発・例外的なら可、繰り返すならテーマへ、が基準です。
27.2 @apply でユーティリティを CSS に逃がしすぎる
何が起きるか: HTML をきれいに見せたいあまり、@apply で何でも CSS クラスにまとめてしまう(第22章)。
なぜ問題か: 独自クラスが増えるほど命名や CSS の管理が必要になり、見た目を調べるためのファイル移動も増えます。構造の重複まで解決できるかは別なので、何を共通化するかを第22章の基準で判断します。
判断基準: 構造や振る舞いも重複するなら、まずコンポーネント抽出を検討します。見た目だけを共有する小さな部品なら、@layer components のカスタムクラスも選択肢です。@apply の使用自体ではなく、命名や変更箇所が無用に増えていないかで判断します。
27.3 クラス名の動的文字列結合で検出漏れ
何が起きるか: text-${color}-600 のようにクラス名を動的に組み立てると、その式から必要な CSS を検出できません。
なぜ問題か: 第4章で見たとおり、Tailwind はソースをただのテキストとして走査し、完全なクラス名の文字列だけを拾います。text-${color}-600 は 1 つの文字列として認識されず、CSS が生成されません。補間式だけからは生成できません。ただし、別ファイルに完全なクラス名があったり、watch 中に既出ルールが残っていたりすると、偶然動く場合があります。クリーンビルドでも確認し、別の箇所の記述に依存させないようにします。
判断基準: クラス名はつねに完全な文字列で書く。プロパティで切り替えるなら、完全なクラス名のマッピングを用意する(第25章)。
// NG
<div className={`text-${color}-600`} />
// OK
const C = { red: 'text-red-600', blue: 'text-blue-600' }
<div className={C[color]} />
これは判断の余地が小さい、ほぼ絶対のルールです。
27.4 「巨大コンポーネント 1 個」化(抽出粒度の誤り)
何が起きるか: 重複を嫌うあまり、何でも 1 つの巨大なコンポーネントに詰め込み、無数の props で分岐させる。
なぜ問題か: コンポーネント化は良いことですが、粒度を誤ると逆効果です。1 つの <Card> が 30 個の props を持ち、内部が条件分岐だらけになると、もはや誰も理解・修正できません。これは「重複を避ける」ことに囚われすぎた結果です。
判断基準: 「少しの重複は、誤った抽象化よりマシ」という原則を思い出します。無理に共通化せず、似て非なるものは別コンポーネントに分ける勇気を持つ。バリアントは CVA(第23章)で整理できる範囲に留める、が基準です。
27.5 デザイントークンを無視した直値の散乱
何が起きるか: text-[#1a1a1a]・bg-[#fafafa] のような直値の色・サイズが、テーマを無視して散らばる。
なぜ問題か: 27.1 の色版です。直値で書くと、ブランド変更やダークモード対応(第18章)のときに全置換が必要になり、保守不能に近づきます。
判断基準: 色・余白・フォントなど、デザインの基幹となる値はデザイントークンに集約する(第5章・第26章)。とくに色は、ダークモードのことを考えると直値を避けるべき筆頭です。
27.6 既存 CSS との二重管理
何が起きるか: Tailwind を導入したのに、従来の CSS ファイルも併存し、同じ要素が両方で別々にスタイリングされる。
なぜ問題か: 「この余白は Tailwind のクラス? それとも CSS ファイル?」と、見るべき場所が 2 つになります。第1章で見た「どこで効いているか分からない」状態に逆戻りします。
判断基準: 段階的移行(第24章)は問題ありませんが、「この画面は Tailwind に寄せる」と単位を決めて移行し、中途半端な二重管理を長期間放置しない。新規分は Tailwind、と方針を統一する、が基準です。
27.7 アンチパターンの矯正手順
すでにこれらのアンチパターンに陥っている場合の、現実的な直し方です。
- 検出漏れ(27.3)から直す: スタイルが当たっていないバグなので最優先。動的クラス名を完全な文字列に置き換える。
- 直値・任意値をテーマに寄せる(27.1, 27.5): 繰り返し使われている直値を洗い出し、デザイントークンに昇格させる。
- 重複をコンポーネント化(27.2, 27.4): 痛い箇所から段階的に抽出する。一度に全部やろうとしない。
完璧を目指さず、痛いところから順に直すのが、挫折しないコツです。
27.8 AI 生成 Tailwind のよくある崩れ方
AI の出力も、人が書いたコードと同じ基準で検査します。以下は特定モデルの傾向を断定するものではなく、出力をレビューするときの確認項目です。入力する規約は第26章、依頼例は付録Eを参照してください。
- 任意の値の乱用(27.1):
[327px]などの値が、チームで採用した寸法規約に沿っているか。 - プロジェクトのトークンとの不一致:
bg-blue-500・p-4も標準のテーマトークン由来です。ただし、採用済みのbg-primaryや余白規約を無視して選んでいないかを確認します。これはbg-[#123456]のような CSS の直値とは別の問題です。 - 動的クラス名(27.3): props から
bg-${color}を組み立てていないか。 - レスポンシブ・アクセシビリティの欠落: 見た目は整っていても、
sm:/md:の設計やaria-*・フォーカス可視化(第21章)が抜けていないか。
レビュー観点としては、(1) クラスが実在するか、(2) レスポンシブが破綻していないか、(3) アクセシビリティを落としていないか、(4) 色・余白・角丸・影がトークンに沿っているか、(5) text-${color} のような検出漏れがないか、をチェックします。AI の出力は「下書き」として受け取り、本章の基準で整える——これが AI 時代の実務作法です。