新しい高速エンジン(Oxide)はフルビルドで最大5倍、増分ビルドで100倍以上高速化されました。しかし真価は速度より「CSS をデザインシステムの単一の真実にできる」点にあります。
1. v3 → v4:何が変わったのか
最大の変化は 設定の場所です。v3 は JS の設定ファイルに色やブレークポイントを書きましたが、v4 は CSS の中で完結します。
/* ❌ v3 の書き方(v4 では誤り) */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* ✅ v4 の正しい書き方:これ1行 */
@import "tailwindcss";
@plugin "@tailwindcss/typography"; /* プラグインも CSS で読み込む */
加えて v4 は次を標準装備しました。
- 自動コンテンツ検出。
content配列の手動設定が原則不要になりました。 - container queries が組み込み(プラグイン不要)。
@containerと@sm:などで親要素サイズに応じたスタイルが書けます。 - モダン CSS 基盤。 cascade layers、
@property、color-mix()を活用。@starting-style(出現アニメ)、not-*バリアント、field-sizing、inertなどにも対応。
設定が JS から消えることで、デザイントークンが「CSS という単一の場所」に集約され、デザインと実装の乖離が減ります。
2. @theme:デザイントークンを一元管理する
@theme で定義した変数は、ただの CSS 変数ではなく「どのユーティリティクラスを生成するか」を決めます。たとえば --color-primary を定義すると bg-primary / text-primary / border-primary が自動で使えるようになります。
@theme {
/* 角丸トークン */
--radius-sm: 0.375rem;
--radius: 0.625rem;
--radius-lg: 0.875rem;
/* 影トークン(2層で奥行きを出す) */
--shadow-sm: 0 1px 2px -1px rgb(16 24 40 / 0.06),
0 1px 3px 0 rgb(16 24 40 / 0.05);
}
これで rounded-lg、shadow-sm が「あなたのデザイン定義」で動きます。色やスペーシングを各コンポーネントにベタ書きする代わりに、トークンを参照する——これが ETC(変更が容易)の核心です。仕様変更はトークン1か所の修正で全画面に波及します。
3. ダークモードの落とし穴:@theme inline が切り替えを壊す
ここが本記事で最も価値のある実務知見です。Tailwind v4 でダークモードを CSS 変数で実装するとき、@theme inline を使うとダークモードが効かなくなります。
理由はこうです。@theme inline は変数の値をビルド時にユーティリティへ焼き込みます。つまり bg-background が background-color: hsl(0 0% 100%)(ライトの値)として固定され、ランタイムで .dark クラスが付いても変わりません。
正解は「生のチャンネル値を :root / .dark に置き、@theme(非 inline)でトークンへマッピングする」二段構えです。
@custom-variant dark (&:where(.dark, .dark *));
/* ① 生の HSL チャンネル値を単一の真実として持つ */
:root {
--background: 0 0% 100%;
--foreground: 222 47% 11%;
--primary: 222 47% 11%;
--primary-foreground: 210 20% 98%;
}
.dark {
--background: 222 47% 5%;
--foreground: 210 20% 98%;
--primary: 210 20% 98%;
--primary-foreground: 222 47% 11%;
}
/* ② 非 inline の @theme でトークンへ。これは
`--color-*: hsl(var(--channel))` を「本物の CSS 変数」として出力するため、
var(--channel) がランタイムに解決され、ライト/ダークが正しく切り替わる。 */
@theme {
--color-background: hsl(var(--background));
--color-foreground: hsl(var(--foreground));
--color-primary: hsl(var(--primary));
--color-primary-foreground: hsl(var(--primary-foreground));
}
ポイントは、色を「HSL のチャンネル三つ組(222 47% 11%)」で保持すること。hsl(var(--token) / <alpha>) の形で任意の透明度を後付けできます(例:bg-primary/15)。@theme inline を選ぶと、この「ランタイム解決」が失われダークモードが死ぬ——この一点だけは必ず覚えてください。
4. 流体タイポグラフィ:1つのユーティリティで4属性を束ねる
見出しのサイズをブレークポイントごとに text-2xl md:text-4xl lg:text-6xl と書くのは冗長です。clamp() と @theme の 修飾子付きトークンを使うと、text-display-2xl ひとつで font-size・line-height・letter-spacing・font-weight をまとめて適用できます。
@theme {
--text-display-2xl: clamp(2.75rem, 1.6rem + 5.1vw, 5.5rem);
--text-display-2xl--line-height: 1.08;
--text-display-2xl--letter-spacing: -0.02em;
--text-display-2xl--font-weight: 700;
}
// 1クラスで「滑らかに伸縮する見出し」が完成。改行制御は text-balance を併用
<h1 className="text-display-2xl text-balance">見出し</h1>
数値表示には tabular-nums(等幅数字)を当てると桁が揃い、カウントアップ等でガタつきません。これは小さな差ですが、洗練された UI の積み重ねです。
5. アクセシビリティを CSS に組み込む
「a11y は後で」ではなく、トークン設計の段階で組み込みます。Tailwind v4 でも素の CSS メディアクエリがそのまま使えます。
@layer base {
/* キーボード操作時だけフォーカスリングを見せる */
:focus-visible {
outline: 2px solid hsl(var(--ring));
outline-offset: 2px;
}
/* アニメーションは prefers-reduced-motion を尊重 */
@media (prefers-reduced-motion: reduce) {
*,
::before,
::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
/* スクロールバー出現での横ズレ(CLS)を防ぐ */
html {
scrollbar-gutter: stable;
}
}
/* ハイコントラスト設定では、細い罫線と淡色テキストを濃くする */
@media (prefers-contrast: more) {
:root {
--border: 222 30% 35%;
--muted-foreground: 222 25% 28%;
}
}
/* Windows ハイコントラスト(forced-colors)でもフォーカスを維持 */
@media (forced-colors: active) {
:focus-visible {
outline-color: Highlight;
}
}
色トークンを決める際は コントラスト比 AA(本文 4.5:1、大きな文字 3:1) を満たすこと。淡色テキスト(muted-foreground)はカード上で割り負けやすいので、白背景だけでなく「淡く色の付いた面」でも 4.5:1 を確保する値を選びます。アクセシビリティの全体像はWCAG 2.2 実装ガイドを参照してください。
6. container queries:本当に再利用できるコンポーネント
メディアクエリは「画面幅」で分岐しますが、container queries は「親要素の幅」で分岐します。同じカードをサイドバーにもメイン領域にも置けて、それぞれの幅で最適化されます。これが真の再利用性(ETC)です。
<div className="@container">
{/* 親が広いときだけ横並びに */}
<article className="flex flex-col @md:flex-row @md:gap-6">
<img className="aspect-video @md:w-48" />
<div>...</div>
</article>
</div>
「画面が広い」ではなく「この置き場所が広い」で考えると、コンポーネントが配置文脈から独立し、デザインシステムの部品として強くなります。
7. カスタムユーティリティと @apply の使いどころ
繰り返すパターンはカスタムユーティリティに切り出せます。ただし乱用は禁物です(YAGNI)。
/* 自前ユーティリティ:本当に何度も使うものだけ */
@utility container-tight {
margin-inline: auto;
max-width: 48rem;
padding-inline: 1rem;
}
@apply は「既存のクラスをまとめたい誘惑」に駆られますが、多用するとユーティリティファーストの利点(HTML を見れば分かる)を失います。原則は HTML 側でユーティリティを並べること。@apply はデザインシステムのプリミティブ(ボタン等)に限定し、アプリ画面では避けます。
8. パフォーマンスとコスト効率
| 項目 | v4 での扱い | 効果 |
|---|---|---|
| ビルド速度 | Oxide エンジン(Rust) | フル最大5倍、増分100倍以上 |
| 未使用 CSS | 自動コンテンツ検出+ツリーシェイク | 本番 CSS が小さく、LCP に有利 |
| 配信サイズ | トークン=CSS 変数で重複が減る | キャッシュ効率・転送量を最適化 |
| ランタイム | ビルド時に静的 CSS 化 | JS 実行コストゼロ(INP に有利) |
CSS が小さく静的であることは、そのまま Core Web Vitals の改善につながります(詳細はCore Web Vitals 最適化ガイド)。「速いサイトは安いサイト」でもあり、配信コストの削減にも効きます。
9. テスト容易性・可観測性
- コントラストの自動検査。
@axe-core/playwrightをCIに組み込み、トークン変更でコントラスト不足が混入しないか検出します。トークンを変えた瞬間に全画面の色が変わるからこそ、自動ガードが効きます。 - ビジュアルリグレッション。 スナップショット比較で、トークン変更の影響範囲を可視化します。
- ダーク/ライト両方をテスト。 第3章の設計が壊れていないか、両モードでスクリーンショットを取ります。
10. アンチパターン
- ❌
@theme inlineで色を定義してダークモードが死ぬ。 ランタイム切り替えには非 inline +生 CSS 変数を使う(第3章)。 - ❌ 任意値(
bg-[#1a2b3c])を各所に散らす。 トークン化してbg-primaryに。色の単一の真実が崩れます。 - ❌
:focus { outline: none }でフォーカスを消す。:focus-visibleで見せる。 - ❌
prefers-reduced-motion/forced-colorsを無視する。 CSS 数行で対応できる必須事項です。 - ❌
@applyをアプリ画面で多用する。 プリミティブに限定し、画面はユーティリティで組む。 - ❌ メディアクエリだけで部品を作る。 配置文脈に依存しない部品は container queries で。
11. FAQ(よくある質問)
Q. v3 から v4 へ移行すべき?
A. 新規プロジェクトは v4 一択です。既存は公式の upgrade ツールがありますが、@tailwind ディレクティブの置換、設定の CSS 化、プラグイン読み込みの変更が必要なため、影響範囲を確認してから移行します。
Q. tailwind.config.js はもう使えない?
A. CSS-first が標準ですが、互換のための JS 設定読み込みも可能です。新規は CSS の @theme を推奨します(単一の場所に集約できるため)。
Q. ダークモードが切り替わらない。
A. 最有力の原因は @theme inline での色定義です。生の CSS 変数を :root/.dark に置き、非 inline の @theme でマッピングしてください(第3章)。
Q. shadcn/ui と併用できる? A. 相性は抜群です。shadcn は CSS 変数ベースのテーマを前提としており、本記事のトークン設計とそのまま噛み合います(shadcn/ui 設計ガイド)。
Q. デザイントークンはどこまで細かく分ける?
A. 「意味」で分けます(primary / muted / destructive)。色そのもの(blue-500)ではなく役割で名付けると、リブランドや配色変更が1か所で済みます。
まとめ:CSS をデザインシステムの単一の真実にする
Tailwind v4 の本質は速度ではなく、設計の集約です。色・タイポ・角丸・影・ブレークポイントを @theme に集め、そこからユーティリティが生成され、a11y もコントラストもダークモードも同じ場所で管理する——これが破綻しないデザインシステムの土台になります。
@import+@themeで CSS-first 設定に統一。- 色は生チャンネル+非 inline
@themeでランタイム・ダークモード対応。 - 流体タイポ・container queries で配置文脈に強い部品を作る。
prefers-contrast/forced-colors/reduced-motion/:focus-visibleで a11y を CSS に内蔵。- 静的 CSS の軽さで パフォーマンスとコストを最適化。
デザインの一貫性は、プロダクトの信頼感に直結します。トークンが整っているプロダクトは、見た目だけでなく保守性・拡張性も高い。
デザインシステムの構築や、Tailwind v4 への移行・テーマ設計が必要な場合は、お気軽にご相談ください。 下記の事例では、多言語・多文化のユーザーが使う UI を、一貫したデザインと操作性で設計・実装した過程を紹介しています。