Chuyển đến nội dung chính
友田 陽大
Frontend
Tailwind CSS
フロントエンド
アクセシビリティ
Next.js
アーキテクチャ設計
パフォーマンス

Tailwind CSS v4 実践ガイド【2026年版】— CSS-first 設計・デザイントークン・ダークモード・a11y を本番品質で

Tailwind CSS v4 の CSS-first 設定(@import / @theme / @custom-variant)を本番品質で使いこなす完全ガイド。デザイントークンの単一管理、ランタイムで切り替わるダークモード設計の落とし穴、流体タイポgrafi、container queries、prefers-contrast / forced-colors などアクセシビリティ対応まで実コードで解説します。

Published
Last updated
Updated
Reading time
9 min read
Author
友田 陽大
Chia sẻ

新しい高速エンジン(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、@propertycolor-mix() を活用。@starting-style(出現アニメ)、not-* バリアント、field-sizinginert などにも対応。

設定が 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-lgshadow-sm が「あなたのデザイン定義」で動きます。色やスペーシングを各コンポーネントにベタ書きする代わりに、トークンを参照する——これが ETC(変更が容易)の核心です。仕様変更はトークン1か所の修正で全画面に波及します。


3. ダークモードの落とし穴:@theme inline が切り替えを壊す

ここが本記事で最も価値のある実務知見です。Tailwind v4 でダークモードを CSS 変数で実装するとき、@theme inline を使うとダークモードが効かなくなります。

理由はこうです。@theme inline は変数の値をビルド時にユーティリティへ焼き込みます。つまり bg-backgroundbackground-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 で。

まとめ:CSS をデザインシステムの単一の真実にする

Tailwind v4 の本質は速度ではなく、設計の集約です。色・タイポ・角丸・影・ブレークポイントを @theme に集め、そこからユーティリティが生成され、a11y もコントラストもダークモードも同じ場所で管理する——これが破綻しないデザインシステムの土台になります。

  1. @import + @themeCSS-first 設定に統一。
  2. 色は生チャンネル+非 inline @theme でランタイム・ダークモード対応。
  3. 流体タイポ・container queries で配置文脈に強い部品を作る。
  4. prefers-contrast / forced-colors / reduced-motion / :focus-visiblea11y を CSS に内蔵
  5. 静的 CSS の軽さで パフォーマンスとコストを最適化

デザインの一貫性は、プロダクトの信頼感に直結します。トークンが整っているプロダクトは、見た目だけでなく保守性・拡張性も高い。

デザインシステムの構築や、Tailwind v4 への移行・テーマ設計が必要な場合は、お気軽にご相談ください。 下記の事例では、多言語・多文化のユーザーが使う UI を、一貫したデザインと操作性で設計・実装した過程を紹介しています。

Câu hỏi thường gặp

v3 から v4 へ移行すべき?
新規プロジェクトは v4 一択です。既存は公式の upgrade ツールがありますが、@tailwind ディレクティブの置換、設定の CSS 化、プラグイン読み込みの変更が必要なため、影響範囲を確認してから移行します。
tailwind.config.js はもう使えない?
CSS-first が標準ですが、互換のための JS 設定読み込みも可能です。新規は CSS の @theme を推奨します(単一の場所に集約できるため)。
ダークモードが切り替わらない。
最有力の原因は @theme inline での色定義です。生の CSS 変数を :root/.dark に置き、非 inline の @theme でマッピングしてください。
shadcn/ui と併用できる?
相性は抜群です。shadcn は CSS 変数ベースのテーマを前提としており、本記事のトークン設計とそのまま噛み合います(shadcn/ui 設計ガイド)。
デザイントークンはどこまで細かく分ける?
「意味」で分けます(primary / muted / destructive)。色そのもの(blue-500)ではなく役割で名付けると、リブランドや配色変更が1か所で済みます。
友田

友田 陽大

Người phát triển sản phẩm đoạt Giải thưởng Bộ trưởng (METI). Với TypeScript + Python + AWS, tôi một mình triển khai trọn gói SaaS, chuyển đổi số công nghiệp và AI tạo sinh (RAG) sẵn sàng cho production — từ phân tích yêu cầu đến hạ tầng và vận hành.

Got a challenge?

From design to implementation and operations — solo × generative AI

Implementation like this article's, end to end from requirements to production. Start with a free 30-minute technical consult and tell me about your situation.

Nhận cả dự án trọn gói lẫn vai trò cố vấn kỹ thuật. Hãy bắt đầu bằng buổi tư vấn miễn phí 30 phút.

最短ルート:カレンダーから直接予約

相談内容が固まっている方は、フォーム送信よりその場で日程を確定する方がスムーズです。下記から空き時間をお選びください。

  • 30分のオンライン無料相談
  • Google Meet / Zoom / Microsoft Teams
  • NDA 商談前締結可・無理な営業はいたしません
無料相談の空き枠を予約する

Cũng đáng đọc