はじめに
TailwindCSSを使っていると、「なぜクラス名を書くだけでスタイルが適用されるのか」「どうやって必要なCSSだけが生成されるのか」と疑問に思うことがあるかもしれません。
この記事では、TailwindCSSがビルド時にどのような処理を行い、最終的なCSSファイルを生成しているのか、その仕組みを詳しく解説します。
対象読者
- TailwindCSSを使い始めた方
- TailwindCSSの内部動作に興味がある方
- ビルドプロセスの仕組みを理解したい方
TailwindCSSとは
TailwindCSSは、ユーティリティファーストのCSSフレームワークです。bg-blue-500やtext-whiteといった小さなクラスを組み合わせてスタイリングを行います。
ユーティリティファーストCSSとは
従来のCSSとの違い
従来のCSSでは、コンポーネントごとにクラス名を定義してスタイルを記述していました。
従来の方式:
/* CSS */
.button-primary {
background-color: #3b82f6;
color: white;
padding: 0.5rem 1rem;
border-radius: 0.25rem;
}
<!-- HTML -->
<button class="button-primary">クリック</button>
TailwindCSSの方式:
<!-- HTML -->
<button class="bg-blue-500 text-white px-4 py-2 rounded">
クリック
</button>
CSS側で独自のクラスを定義する必要がなく、HTMLやJSX内でスタイルが完結します。
ユーティリティクラスの考え方
ユーティリティクラスは、一つの目的に特化した小さなクラスです:
-
bg-blue-500:背景色を青にする -
text-white:文字色を白にする -
px-4:左右のパディングを1remにする -
py-2:上下のパディングを0.5remにする
これらを組み合わせることで、複雑なデザインを実現します。
メリットとデメリット
メリット:
- CSSファイルを別途編集する必要がない
- クラス名を考える必要がない
- コンポーネントとスタイルが同じ場所にある
- 使用していないスタイルは生成されない
デメリット:
- HTMLのクラス属性が長くなる
- Tailwindの記法を覚える必要がある
- カスタムデザインには追加設定が必要な場合がある
TailwindCSSの動作フロー全体像
TailwindCSSは、ビルド時に以下のステップで動作します。
各ステップの概要
- コードを書く:HTMLやJSXでTailwindのクラスを指定
- ビルド開始:ViteやWebpackなどのビルドツールが起動
- ファイルスキャン:指定されたファイル内のクラス名を検出
- CSS生成:検出したクラスに対応するCSSのみを生成
- 出力:最小限のCSSファイルを作成
この仕組みにより、プロジェクトで実際に使用しているクラスのCSSだけが含まれるため、ファイルサイズを小さく保てます。
ステップ1:コードを書く
まず、HTMLやJSXでTailwindのクラスを使ってスタイルを指定します。
具体的なコード例
function Card() {
return (
<div className="bg-white rounded-lg shadow-md p-6">
<h2 className="text-2xl font-bold text-gray-800 mb-4">
カードタイトル
</h2>
<p className="text-gray-600">
これはカードの説明文です。
</p>
<button className="mt-4 bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded">
詳細を見る
</button>
</div>
);
}
このコードには、以下のようなTailwindクラスが含まれています:
-
bg-white、bg-blue-500:背景色 -
rounded-lg、rounded:角丸 -
shadow-md:影 -
p-6、px-4、py-2:パディング -
text-2xl、text-gray-800:テキストサイズと色 -
font-bold:太字 -
hover:bg-blue-600:ホバー時の背景色
これらのクラスが、次のステップでスキャンされます。
ステップ2:ファイルスキャン
ビルド時に、TailwindCSSは指定されたファイルをスキャンして、使用されているクラスを検出します。
どのファイルがスキャン対象か
TailwindCSSは、設定に基づいて特定のファイルをスキャンします。デフォルトでは以下のようなファイルが対象です:
// デフォルトのスキャン対象(概念的な表現)
content: [
'./index.html',
'./src/**/*.{js,jsx,ts,tsx,vue}',
]
主なスキャン対象:
- HTMLファイル
- JavaScriptファイル
- JSX/TSXファイル
- Vueコンポーネント
クラスの検出方法
TailwindCSSは、正規表現を使ってファイル内の文字列パターンをマッチングします。具体的には、Tailwindのクラス命名規則に従う文字列を検出しています。
検出される場所:
-
className属性 -
class属性 - テンプレートリテラル内の文字列
- 通常の文字列リテラル
検出されるパターンと検出されないパターン
✅ 検出されるパターン
// 1. 直接指定
<div className="bg-red-500">
// 2. 文字列として定義
const classes = "text-blue-600 font-bold"
<div className={classes}>
// 3. テンプレートリテラル(静的な部分)
<div className={`bg-green-500 text-white`}>
// 4. 三項演算子(両方とも検出される)
<div className={isActive ? "bg-blue-500" : "bg-gray-500"}>
❌ 検出されないパターン
// 1. 動的に生成されるクラス名
const color = "blue"
<div className={`bg-${color}-500`}> // bg-blue-500は検出されない
// 2. 変数を使った組み立て
const bgClass = "bg-" + color + "-500"
<div className={bgClass}>
// 3. オブジェクトのプロパティから取得
const styles = { background: "bg-red-500" }
<div className={styles.background}>
なぜ検出されないのか:
TailwindCSSはビルド時にファイルをスキャンします。この時点では、変数の値が何になるかは確定していません。そのため、動的に生成されるクラス名は検出できないのです。
推奨される回避方法:
// ❌ 動的生成(検出されない)
<div className={`bg-${color}-500`}>
// ✅ 完全なクラス名を使う
<div className={color === "blue" ? "bg-blue-500" : "bg-red-500"}>
// ✅ 必要なクラスを事前に列挙
const colorClasses = {
blue: "bg-blue-500",
red: "bg-red-500",
green: "bg-green-500",
}
<div className={colorClasses[color]}>
ステップ3:CSS生成
スキャンで検出したクラスに対応するCSSを生成します。
検出したクラスからCSSを生成する仕組み
TailwindCSSは、内部に各クラスとCSSの対応表を持っています。検出したクラスに対して、対応するCSSを生成する処理が実行されます。
実際に生成されるCSSの例
先ほどのCardコンポーネントで使用していたクラスから、以下のようなCSSが生成されます:
.bg-white {
background-color: rgb(255 255 255);
}
.bg-blue-500 {
background-color: rgb(59 130 246);
}
.rounded-lg {
border-radius: 0.5rem;
}
.shadow-md {
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
}
.p-6 {
padding: 1.5rem;
}
.text-2xl {
font-size: 1.5rem;
line-height: 2rem;
}
.font-bold {
font-weight: 700;
}
.text-gray-800 {
color: rgb(31 41 55);
}
.hover\:bg-blue-600:hover {
background-color: rgb(37 99 235);
}
/* その他、使用されているクラスのCSSが続く... */
必要なものだけを生成する利点
この仕組みにより、以下のメリットがあります:
ファイルサイズの最小化:
- 使用していないクラスのCSSは一切含まれない
- 本番環境のCSSファイルが非常に小さくなる
- ページの読み込み速度が向上
保守性の向上:
- 不要なCSSが蓄積しない
- コンポーネントを削除すれば、そのCSSも自動的に削除される
- CSS管理の手間が減る
JITモードの登場と進化
TailwindCSSのCSS生成方式は、バージョンアップとともに進化してきました。
従来の方式(v2以前)
v2以前では、PurgeCSS(後にPurge機能として統合)を使った方式が採用されていました。
処理の流れ:
- まず、TailwindCSSが提供するすべてのクラスのCSSを生成
- ビルド時にファイルをスキャン
- 使用されていないクラスのCSSを削除
- 最終的なCSSファイルを出力
問題点:
- 開発時のCSSファイルサイズが数MBと非常に大きい
- ブラウザでの読み込みが遅い
- ビルド時間が長い
JIT方式(v3以降)
v3以降では、JIT(Just-In-Time)モードがデフォルトになりました。
処理の流れ:
- ファイルをスキャンして使用中のクラスを検出
- 検出したクラスのCSSだけを即座に生成
- 最終的なCSSファイルを出力
改善点:
- 開発時も本番環境も同じ小さなCSSファイル
- ビルド速度が大幅に向上
- 任意の値を使ったクラスも動的に生成可能
JITモードの利点
JITモードでは、以下のような任意の値も使えるようになりました:
// 任意の値を使ったクラス
<div className="top-[117px]">
<div className="bg-[#1da1f2]">
<div className="text-[14px]">
これらのクラスは、従来の方式では事前定義されていないため使用できませんでしたが、JITモードでは検出時に動的に生成されるため使用可能です。
TailwindCSS v4での変更点
v4では、さらなる高速化と使いやすさの向上が図られています。
Rust製エンジンによる高速化
TailwindCSSのコア処理がRustで書き直されました。Rustは高速で安全な言語として知られており、以下のような効果があります:
- スキャン処理の高速化
- CSS生成速度の向上
- 大規模プロジェクトでのパフォーマンス改善
PostCSSプラグインの統合
v4では、@tailwindcss/postcssという単一のプラグインで完結するようになりました。
v3以前:
// tailwind.config.js
module.exports = {
content: ['./src/**/*.{html,js}'],
theme: {
extend: {},
},
plugins: [],
}
// postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
v4:
// postcss.config.js
export default {
plugins: {
'@tailwindcss/postcss': {},
},
}
設定がシンプルになり、導入のハードルが下がりました。
設定ファイルの簡素化
v4では、多くの設定がオプショナルになりました:
-
contentの指定が不要(自動検出) - デフォルト設定で多くのケースに対応
- 必要な場合のみカスタマイズ
これにより、初心者でも扱いやすくなっています。
まとめ
TailwindCSSの動作原理を整理します。
動作の仕組み
- スキャン:ビルド時に指定されたファイル内の文字列をスキャン
- 検出:Tailwindクラスのパターンにマッチする文字列を抽出
- 生成:検出したクラスに対応するCSSのみを生成
- 出力:最小限のCSSファイルを作成
この仕組みがもたらすメリット
開発体験の向上:
- CSSファイルを別途編集する必要がない
- スタイルとマークアップが同じ場所にある
- クラス名を考える必要がない
パフォーマンスの最適化:
- 使用しているCSSのみが含まれる
- ファイルサイズが最小限に抑えられる
- ページの読み込み速度が向上
保守性の向上:
- 不要なCSSが蓄積しない
- コンポーネントを削除すればCSSも自動的に削除される
- プロジェクトの肥大化を防げる
TailwindCSSの仕組みを理解することで、より効果的に活用できるようになります。特に動的クラスの扱いなど、注意点を把握しておくことで、予期しない動作を避けられるでしょう。