第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章)。
なぜ問題か: 第22章で詳しく見たとおり、Tailwind を導入して捨てたはずの「命名コスト」「CSS の肥大化」「ファイルの往復」が戻ってきます。
判断基準: 重複はまずコンポーネント抽出で解決できないかを考える(第6部)。@apply が正当なのは「コンポーネント化できない外部マークアップ」などの例外だけ。重複は CSS にではなくコンポーネントに畳み込む、が基準です。
27.3 クラス名の動的文字列結合で検出漏れ
何が起きるか: text-${color}-600 のようにクラス名を動的に組み立てると、スタイルが当たりません。
なぜ問題か: 第4章で見たとおり、Tailwind はソースをただのテキストとして走査し、完全なクラス名の文字列だけを拾います。text-${color}-600 は 1 つの文字列として認識されず、CSS が生成されません。これは「たまに動いてたまに動かない」ではなく、原理的に生成されないので、初学者が最もハマる罠です。
判断基準: クラス名はつねに完全な文字列で書く。プロパティで切り替えるなら、完全なクラス名のマッピングを用意する(第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 が生成した Tailwind コードには、本章のアンチパターンがまとめて現れがちです。代表的な崩れ方は次のとおりです。
- 任意の値の乱用(27.1): AI はデザインカンプの値をそのまま
[327px]のように出しがち。 - トークン無視の直値(27.5):
bg-blue-500・p-4を、プロジェクトのトークンを知らずに選ぶ。 - 動的クラス名(27.3): props から
bg-${color}を組み立てるコードを平気で書く。 - レスポンシブ・アクセシビリティの欠落: 見た目は整っていても、
sm:/md:の設計やaria-*・フォーカス可視化(第21章)が抜けがち。
レビュー観点としては、(1) クラスが実在するか、(2) レスポンシブが破綻していないか、(3) アクセシビリティを落としていないか、(4) 色・余白・角丸・影がトークンに沿っているか、(5) text-${color} のような検出漏れがないか、をチェックします。AI の出力は「下書き」として受け取り、本章の基準で整える——これが AI 時代の実務作法です。