サイトアイコンmaita tomoya dev io

Tailwind CSS

スタイリングとフレームワーク

Tailwind CSS

Tailwind CSSとは

Tailwind CSSはユーティリティファーストCSSフレームワーク。従来のCSSフレームワーク(Bootstrapなど)が「ボタンはこういう見た目」と完成品を提供するのに対し、Tailwind CSSは「文字を赤くする」「余白を4pxにする」といった小さなユーティリティクラスを組み合わせてデザインを構築するアプローチを取る。

たとえるなら、Bootstrapは「完成品の家具セット」を買うようなもので、Tailwind CSSは「木材、ネジ、塗料を自由に組み合わせてオリジナル家具を作る」ようなもの。自由度は高いが、基本的なパーツ(ユーティリティクラス)は全て用意されているので、ゼロから作る必要はない。

2017年にAdam Wathanによって開発され、現在はWebフロントエンド開発で最も人気のあるCSSフレームワークの一つ。React、Vue、Next.jsなどのモダンフレームワークとの相性が非常に良い。


従来のCSS vs Tailwind CSS

アプローチの違い

従来のCSS(BEMなど)の書き方:

<!-- HTML -->
<div class="card">
  <h2 class="card__title">タイトル</h2>
  <p class="card__description">説明文です。</p>
  <button class="card__button">詳細を見る</button>
</div>
/* CSS */
.card {
  background-color: white;
  border-radius: 8px;
  padding: 24px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.card__title {
  font-size: 1.25rem;
  font-weight: bold;
  color: #1a202c;
  margin-bottom: 8px;
}

.card__description {
  color: #718096;
  margin-bottom: 16px;
}

.card__button {
  background-color: #3b82f6;
  color: white;
  padding: 8px 16px;
  border-radius: 4px;
  border: none;
  cursor: pointer;
}

Tailwind CSSの書き方:

<div class="bg-white rounded-lg p-6 shadow-md">
  <h2 class="text-xl font-bold text-gray-900 mb-2">タイトル</h2>
  <p class="text-gray-500 mb-4">説明文です。</p>
  <button class="bg-blue-500 text-white px-4 py-2 rounded cursor-pointer">詳細を見る</button>
</div>

CSSファイルが不要で、HTMLだけで完結する。これがTailwind CSSの最大の特徴。

メリット・デメリット比較表

観点従来のCSSTailwind CSS
CSSファイルの管理CSSファイルが肥大化しやすいCSSファイルをほぼ書かない
クラス名の命名BEMなどの命名規則が必要命名不要(ユーティリティクラスを使う)
HTML側のクラスシンプルクラスが長くなりがち
デザインの一貫性開発者に依存デザイントークンで統一される
学習コストCSS自体の知識が必要CSSの知識 + ユーティリティクラスの暗記
カスタマイズ性自由度が高いconfig設定で柔軟に対応可能
開発速度CSSファイルとHTMLの行き来が必要HTMLだけで完結するため速い
ファイルサイズ使わないCSSも残りがちビルド時に未使用クラスを自動削除
コンポーネントとの相性普通React/Vueなどと非常に相性が良い
チーム開発スタイルの衝突リスクありユーティリティなので衝突しにくい

Tailwind CSSが特に向いているケース:

  • React、Vue、Svelteなどコンポーネントベースの開発
  • プロトタイピングやMVPの素早い開発
  • デザインシステムを統一したいチーム開発

従来のCSSが向いているケース:

  • 既存プロジェクトの保守
  • CSSの基礎を学ぶ段階
  • 極めて複雑なアニメーションが多いプロジェクト

導入方法

Tailwind CSSの導入にはいくつかの方法がある。プロジェクトの規模や用途に応じて選択する。

方法1: CDN(学習・プロトタイプ向け)

最も簡単な方法。HTMLファイルに1行追加するだけで使える。ただし、本番環境での利用は推奨されない(カスタマイズが制限される、ファイルサイズが大きい)。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Tailwind CSS テスト</title>
    <!-- Tailwind CSS CDN -->
    <script src="https://cdn.tailwindcss.com"></script>
  </head>
  <body class="bg-gray-100 min-h-screen">
    <div class="container mx-auto p-8">
      <h1 class="text-3xl font-bold text-blue-600">Hello Tailwind!</h1>
      <p class="mt-4 text-gray-700">CDNで簡単に試せます。</p>
    </div>
  </body>
</html>

方法2: npm install(実務向け・推奨)

Viteを使ったプロジェクトでの導入手順を示す。現在の実務で最も一般的な方法。

# 新しいViteプロジェクトを作成
npm create vite@latest my-project -- --template react
cd my-project

# Tailwind CSSとその依存パッケージをインストール
npm install -D tailwindcss @tailwindcss/vite

vite.config.tsにプラグインを追加:

// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
  plugins: [react(), tailwindcss()],
})

CSSファイルにTailwindのインポートを追加:

/* src/index.css */
@import 'tailwindcss';

これでTailwind CSSが使えるようになる。

方法3: PostCSS設定(既存プロジェクトへの導入、Tailwind CSS v3系)

既存のプロジェクトにTailwind CSSを追加する場合はPostCSS経由で導入する。なお、この設定方法はTailwind CSS v3系のもの。v4では設定方法が大きく変わっているため、新規プロジェクトでは方法2のViteプラグイン方式を推奨する。

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init

postcss.config.jsを作成:

// postcss.config.js
export default {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}

tailwind.config.jsでコンテンツのパスを設定:

// tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
  content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
  theme: {
    extend: {},
  },
  plugins: [],
}
導入方法用途カスタマイズビルド最適化
CDN学習、プロトタイプ制限ありなし
npm + Vite新規プロジェクト(推奨)全て可能あり
PostCSS既存プロジェクトへの追加全て可能あり

基本的なユーティリティクラス

Tailwind CSSの核心はユーティリティクラス。CSSのプロパティ1つに対して1つのクラスが対応する。以下に主要なカテゴリごとにまとめる。

色(Color)

Tailwindの色は{プロパティ}-{色名}-{濃さ}の形式で指定する。濃さは50(薄い)から950(濃い)まで。

<!-- テキストの色 -->
<p class="text-red-500">赤いテキスト</p>
<p class="text-blue-700">濃い青のテキスト</p>
<p class="text-gray-400">薄いグレーのテキスト</p>

<!-- 背景色 -->
<div class="bg-green-100">薄い緑の背景</div>
<div class="bg-yellow-500">黄色の背景</div>

<!-- ボーダーの色 -->
<div class="border border-purple-300">紫のボーダー</div>

主要な色の一覧:

色名用途の例クラス例
slate落ち着いたグレー系text-slate-700, bg-slate-100
gray標準的なグレーtext-gray-500, bg-gray-200
redエラー、警告text-red-500, bg-red-100
orange注意、アクセントtext-orange-500, bg-orange-100
yellow警告、ハイライトtext-yellow-500, bg-yellow-100
green成功、確認text-green-500, bg-green-100
blueリンク、情報、プライマリtext-blue-500, bg-blue-100
indigoアクセントtext-indigo-500, bg-indigo-100
purple装飾、ブランドtext-purple-500, bg-purple-100
pink装飾、フェミニンtext-pink-500, bg-pink-100

濃さの段階:

印象使用例
50非常に薄い背景色に使う(bg-blue-50
100-200薄い背景色、ホバー時の背景
300-400やや薄いボーダー、無効状態のテキスト
500標準アイコン、ボタン背景
600-700やや濃い本文テキスト、ボタンホバー
800-900濃い見出し、重要なテキスト
950非常に濃い最も目立つテキスト

サイズ(Width / Height)

<!-- 幅 -->
<div class="w-full">横幅100%</div>
<div class="w-1/2">横幅50%</div>
<div class="w-64">横幅256px(16rem)</div>
<div class="w-screen">画面幅いっぱい</div>

<!-- 高さ -->
<div class="h-screen">画面の高さいっぱい</div>
<div class="h-64">高さ256px</div>
<div class="h-full">親要素の高さいっぱい</div>

<!-- 最大・最小幅 -->
<div class="max-w-md">最大幅448px</div>
<div class="min-h-screen">最低でも画面の高さ</div>

主なサイズ値:

クラスピクセル換算
w-000px
w-10.25rem4px
w-20.5rem8px
w-41rem16px
w-82rem32px
w-164rem64px
w-328rem128px
w-6416rem256px
w-9624rem384px
w-1/250%-
w-1/333.333%-
w-2/366.667%-
w-full100%-
w-screen100vw-

覚え方のコツ: 数字は基本的に「値 x 4px = ピクセル」と覚える。w-4なら4 x 4px = 16px。

余白(Padding / Margin)

Tailwindの余白は{種類}{方向}-{サイズ}の形式。

<!-- パディング(内側の余白) -->
<div class="p-4">全方向に16pxの余白</div>
<div class="px-4">左右に16pxの余白</div>
<div class="py-2">上下に8pxの余白</div>
<div class="pt-8">上だけに32pxの余白</div>
<div class="pb-4">下だけに16pxの余白</div>

<!-- マージン(外側の余白) -->
<div class="m-4">全方向に16pxの外側余白</div>
<div class="mx-auto">左右中央揃え</div>
<div class="mt-8">上に32pxの外側余白</div>
<div class="mb-4">下に16pxの外側余白</div>

<!-- 負のマージン -->
<div class="-mt-4">上に-16pxのマージン</div>

方向の記号:

記号意味対応するCSSプロパティ
なし(p-4全方向padding: 1rem
xpx-4左右padding-left: 1rem; padding-right: 1rem
ypy-4上下padding-top: 1rem; padding-bottom: 1rem
tpt-4padding-top: 1rem
rpr-4padding-right: 1rem
bpb-4padding-bottom: 1rem
lpl-4padding-left: 1rem

ボーダー(Border)

<!-- 基本のボーダー -->
<div class="border">1pxの実線ボーダー</div>
<div class="border-2">2pxのボーダー</div>
<div class="border-4">4pxのボーダー</div>

<!-- ボーダーの色 -->
<div class="border border-gray-300">グレーのボーダー</div>
<div class="border-2 border-blue-500">青い2pxのボーダー</div>

<!-- 角丸 -->
<div class="rounded">小さな角丸(4px)</div>
<div class="rounded-md">中くらいの角丸(6px)</div>
<div class="rounded-lg">大きな角丸(8px)</div>
<div class="rounded-xl">さらに大きな角丸(12px)</div>
<div class="rounded-full">完全な円</div>

<!-- 特定の方向の角丸 -->
<div class="rounded-t-lg">上だけ角丸</div>
<div class="rounded-b-lg">下だけ角丸</div>

<!-- ボーダーの方向 -->
<div class="border-b">下だけボーダー</div>
<div class="border-t-2 border-blue-500">上に青い2pxボーダー</div>

テキスト(Typography)

<!-- フォントサイズ -->
<p class="text-xs">12px - 極小テキスト</p>
<p class="text-sm">14px - 小さいテキスト</p>
<p class="text-base">16px - 標準テキスト</p>
<p class="text-lg">18px - やや大きいテキスト</p>
<p class="text-xl">20px - 大きいテキスト</p>
<p class="text-2xl">24px - 見出し向け</p>
<p class="text-3xl">30px - 大見出し</p>
<p class="text-4xl">36px - 特大見出し</p>

<!-- 太さ -->
<p class="font-thin">細い(100)</p>
<p class="font-normal">標準(400)</p>
<p class="font-medium">やや太い(500)</p>
<p class="font-semibold">半太字(600)</p>
<p class="font-bold">太字(700)</p>

<!-- 文字揃え -->
<p class="text-left">左揃え</p>
<p class="text-center">中央揃え</p>
<p class="text-right">右揃え</p>

<!-- 行間 -->
<p class="leading-tight">狭い行間</p>
<p class="leading-normal">標準の行間</p>
<p class="leading-relaxed">広い行間</p>

<!-- その他 -->
<p class="underline">下線付き</p>
<p class="line-through">取り消し線</p>
<p class="uppercase">大文字変換</p>
<p class="truncate">長いテキストを省略記号で...(overflow: hidden + text-overflow: ellipsis)</p>

シャドウ(Box Shadow)

<div class="shadow-sm">小さな影</div>
<div class="shadow">標準的な影</div>
<div class="shadow-md">中くらいの影</div>
<div class="shadow-lg">大きな影</div>
<div class="shadow-xl">特大の影</div>
<div class="shadow-2xl">最大の影</div>
<div class="shadow-none">影なし</div>

透明度(Opacity)

<div class="opacity-100">不透明(100%)</div>
<div class="opacity-75">やや透明(75%)</div>
<div class="opacity-50">半透明(50%)</div>
<div class="opacity-25">かなり透明(25%)</div>
<div class="opacity-0">完全に透明(0%)</div>

レスポンシブデザイン

Tailwind CSSのレスポンシブデザインはモバイルファースト。何もプレフィックスをつけないクラスがモバイル用で、ブレイクポイントのプレフィックスをつけると「その画面幅以上で適用」される。

ブレイクポイント一覧

プレフィックス最小幅対象デバイスの目安CSSの値
なし0pxモバイル(全サイズ)-
sm:640px大きめスマホ@media (min-width: 640px)
md:768pxタブレット@media (min-width: 768px)
lg:1024pxノートPC@media (min-width: 1024px)
xl:1280pxデスクトップ@media (min-width: 1280px)
2xl:1536px大画面デスクトップ@media (min-width: 1536px)

モバイルファーストの考え方

重要な概念: Tailwindでは「小さい画面のスタイルをデフォルトで書き、画面が大きくなるにつれて上書きしていく」。

<!-- モバイルファーストの例 -->
<div class="text-sm md:text-base lg:text-lg xl:text-xl">
  <!--
    モバイル: text-sm(14px)
    768px以上: text-base(16px)
    1024px以上: text-lg(18px)
    1280px以上: text-xl(20px)
  -->
  レスポンシブなテキスト
</div>

実践例: レスポンシブカードグリッド

<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 p-4">
  <!--
    モバイル: 1列
    640px以上: 2列
    1024px以上: 3列
    1280px以上: 4列
  -->
  <div class="bg-white rounded-lg shadow-md p-6">
    <h3 class="text-lg font-bold mb-2">カード1</h3>
    <p class="text-gray-600">カードの説明文です。</p>
  </div>
  <div class="bg-white rounded-lg shadow-md p-6">
    <h3 class="text-lg font-bold mb-2">カード2</h3>
    <p class="text-gray-600">カードの説明文です。</p>
  </div>
  <div class="bg-white rounded-lg shadow-md p-6">
    <h3 class="text-lg font-bold mb-2">カード3</h3>
    <p class="text-gray-600">カードの説明文です。</p>
  </div>
  <div class="bg-white rounded-lg shadow-md p-6">
    <h3 class="text-lg font-bold mb-2">カード4</h3>
    <p class="text-gray-600">カードの説明文です。</p>
  </div>
</div>

実践例: レスポンシブレイアウト

<!-- モバイルでは縦並び、PCでは横並び -->
<div class="flex flex-col md:flex-row gap-4">
  <!-- サイドバー: モバイルでは上に、PCでは左に -->
  <aside class="w-full md:w-64 bg-gray-800 text-white p-4 rounded-lg">
    <h2 class="text-xl font-bold mb-4">メニュー</h2>
    <nav>
      <ul class="space-y-2">
        <li><a href="#" class="hover:text-blue-300">ホーム</a></li>
        <li><a href="#" class="hover:text-blue-300">記事一覧</a></li>
        <li><a href="#" class="hover:text-blue-300">お問い合わせ</a></li>
      </ul>
    </nav>
  </aside>

  <!-- メインコンテンツ -->
  <main class="flex-1 bg-white p-6 rounded-lg shadow">
    <h1 class="text-2xl font-bold mb-4">メインコンテンツ</h1>
    <p class="text-gray-700 leading-relaxed">
      ここにメインの内容が入ります。 画面幅が768px以上ではサイドバーと横並びになります。
    </p>
  </main>
</div>

Flexboxユーティリティ

Flexboxはレイアウトを組むための強力なCSS機能。Tailwindではクラスを追加するだけで使える。

基本

<!-- 横並び(デフォルト) -->
<div class="flex">
  <div>要素1</div>
  <div>要素2</div>
  <div>要素3</div>
</div>

<!-- 縦並び -->
<div class="flex flex-col">
  <div>要素1</div>
  <div>要素2</div>
  <div>要素3</div>
</div>

<!-- 折り返しあり -->
<div class="flex flex-wrap">
  <div>要素1</div>
  <div>要素2</div>
  <div>要素3</div>
</div>

配置

<!-- 水平方向の配置 -->
<div class="flex justify-start">左寄せ(デフォルト)</div>
<div class="flex justify-center">中央寄せ</div>
<div class="flex justify-end">右寄せ</div>
<div class="flex justify-between">両端揃え(間に均等スペース)</div>
<div class="flex justify-around">均等配置(両端にもスペース)</div>
<div class="flex justify-evenly">完全均等配置</div>

<!-- 垂直方向の配置 -->
<div class="flex items-start">上揃え</div>
<div class="flex items-center">中央揃え</div>
<div class="flex items-end">下揃え</div>
<div class="flex items-stretch">高さを揃える(デフォルト)</div>

<!-- 上下左右ど真ん中に配置(超よく使う) -->
<div class="flex items-center justify-center h-screen">
  <div>画面の真ん中に表示</div>
</div>

主なFlexboxクラス一覧

クラスCSSプロパティ説明
flexdisplay: flexFlexコンテナにする
inline-flexdisplay: inline-flexインラインFlexコンテナ
flex-rowflex-direction: row横並び(デフォルト)
flex-colflex-direction: column縦並び
flex-wrapflex-wrap: wrap折り返しあり
flex-nowrapflex-wrap: nowrap折り返しなし
justify-startjustify-content: flex-start主軸の開始位置に配置
justify-centerjustify-content: center主軸の中央に配置
justify-endjustify-content: flex-end主軸の終了位置に配置
justify-betweenjustify-content: space-between均等配置(両端に寄せる)
items-startalign-items: flex-start交差軸の開始位置
items-centeralign-items: center交差軸の中央
items-endalign-items: flex-end交差軸の終了位置
items-stretchalign-items: stretch高さを揃える
flex-1flex: 1 1 0%残りのスペースを埋める
flex-noneflex: none伸縮しない
gap-4gap: 1rem要素間の余白
order-1order: 1並び順を変更

実践例: ヘッダーのレイアウト

<header class="flex items-center justify-between px-6 py-4 bg-white shadow">
  <!-- ロゴ -->
  <div class="text-xl font-bold text-blue-600">MyApp</div>

  <!-- ナビゲーション -->
  <nav class="hidden md:flex gap-6">
    <a href="#" class="text-gray-700 hover:text-blue-600">ホーム</a>
    <a href="#" class="text-gray-700 hover:text-blue-600">機能</a>
    <a href="#" class="text-gray-700 hover:text-blue-600">料金</a>
    <a href="#" class="text-gray-700 hover:text-blue-600">お問い合わせ</a>
  </nav>

  <!-- ログインボタン -->
  <button class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700">ログイン</button>
</header>

Gridユーティリティ

CSS Gridは2次元レイアウト(行と列を同時に制御)に最適。Tailwindでは直感的にGridが使える。

基本

<!-- 3列グリッド -->
<div class="grid grid-cols-3 gap-4">
  <div class="bg-blue-100 p-4">1</div>
  <div class="bg-blue-200 p-4">2</div>
  <div class="bg-blue-300 p-4">3</div>
  <div class="bg-blue-100 p-4">4</div>
  <div class="bg-blue-200 p-4">5</div>
  <div class="bg-blue-300 p-4">6</div>
</div>

<!-- 列幅の指定 -->
<div class="grid grid-cols-4 gap-4">
  <div class="col-span-1 bg-gray-200 p-4">サイドバー</div>
  <div class="col-span-3 bg-white p-4">メインコンテンツ(3列分)</div>
</div>

<!-- 自動フィット -->
<div class="grid grid-cols-[repeat(auto-fit,minmax(250px,1fr))] gap-4">
  <!-- 最小250px、最大1frで自動的に列数が変わる -->
  <div class="bg-blue-100 p-4">アイテム1</div>
  <div class="bg-blue-100 p-4">アイテム2</div>
  <div class="bg-blue-100 p-4">アイテム3</div>
  <div class="bg-blue-100 p-4">アイテム4</div>
</div>

主なGridクラス一覧

クラス説明
gridGridコンテナにする
grid-cols-1 ~ grid-cols-12列数を指定
grid-rows-1 ~ grid-rows-6行数を指定
col-span-1 ~ col-span-12要素が占める列数
row-span-1 ~ row-span-6要素が占める行数
gap-1 ~ gap-96グリッド間の余白
gap-x-4列方向の余白
gap-y-4行方向の余白
col-start-1開始列を指定
col-end-4終了列を指定

FlexboxとGridの使い分け

場面推奨理由
1行のナビゲーションFlexbox1次元の横並び
ヘッダーのロゴとメニュー配置Flexbox左右の配置
カードの一覧表示Grid均等な2次元配置
ダッシュボードのレイアウトGrid複雑な行列制御
ボタングループFlexbox簡単な横並び
フォームのラベルと入力欄Grid列幅を揃えたい場合

ダークモード対応

Tailwind CSSではdark:プレフィックスを使うだけでダークモード対応ができる。

基本的な使い方

<!-- ライトモードでは白背景・黒文字、ダークモードでは暗い背景・白文字 -->
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
  <h1 class="text-2xl font-bold text-blue-600 dark:text-blue-400">ダークモード対応のタイトル</h1>
  <p class="text-gray-600 dark:text-gray-300">ユーザーのOS設定に応じて自動的に切り替わります。</p>
</div>

ダークモードの切り替え方法

Tailwind CSSには2つのダークモード戦略がある。

方法1: メディアクエリ(デフォルト)

OSのダークモード設定に自動的に追従する。

/* tailwind.config.jsで特に設定不要(デフォルト) */

方法2: セレクタ戦略(手動切り替え)

JavaScriptでダークモードを切り替えたい場合に使う。

// tailwind.config.js
export default {
  darkMode: 'selector', // v3.4.1以降
  // ...
}
<!-- HTMLのルート要素にdarkクラスを追加・削除で切り替え -->
<html class="dark">
  <body class="bg-white dark:bg-gray-900">
    <!-- ダークモードが適用される -->
  </body>
</html>
// JavaScriptでの切り替え例
const toggleDarkMode = () => {
  document.documentElement.classList.toggle('dark')
}

実践例: ダークモード対応のカード

<div class="min-h-screen bg-gray-100 dark:bg-gray-900 p-8">
  <div
    class="max-w-md mx-auto bg-white dark:bg-gray-800 rounded-xl shadow-lg
              dark:shadow-gray-900/50 p-6"
  >
    <h2 class="text-xl font-bold text-gray-900 dark:text-white mb-2">記事のタイトル</h2>
    <p class="text-gray-600 dark:text-gray-300 mb-4">
      この記事ではダークモード対応の方法を解説します。
      OSの設定に応じて自動的にテーマが切り替わります。
    </p>
    <div class="flex items-center gap-2">
      <span class="text-sm text-gray-500 dark:text-gray-400">2026年3月28日</span>
      <span
        class="bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200
                   text-xs px-2 py-1 rounded-full"
      >
        チュートリアル
      </span>
    </div>
  </div>
</div>

状態バリアント(hover / focus等)

Tailwindでは、要素の状態に応じたスタイル変更をhover:focus:などのプレフィックスで簡単に実現できる。

主なバリアント一覧

プレフィックスいつ適用されるかよく使う場面
hover:マウスを乗せた時ボタン、リンク、カード
focus:フォーカスが当たった時入力欄、ボタン
active:クリック中ボタン
focus-within:子要素にフォーカスがある時フォームグループ
focus-visible:キーボードフォーカス時アクセシビリティ対応
disabled:disabled属性がある時無効化されたボタン
first:最初の子要素リストの最初の項目
last:最後の子要素リストの最後の項目
odd:奇数番目の子要素テーブルの縞模様
even:偶数番目の子要素テーブルの縞模様
group-hover:親要素にhoverした時カード内のアイコン変化
peer-checked:兄弟要素がチェックされた時カスタムトグル
placeholder:プレースホルダーテキスト入力欄のプレースホルダー色

実践例

<!-- ボタンの状態変化 -->
<button
  class="bg-blue-600 text-white px-6 py-3 rounded-lg
               hover:bg-blue-700
               active:bg-blue-800
               focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
               disabled:bg-gray-400 disabled:cursor-not-allowed
               transition-colors duration-200"
>
  送信する
</button>

<!-- 入力欄 -->
<input
  type="text"
  class="w-full px-4 py-2 border border-gray-300 rounded-lg
              focus:border-blue-500 focus:ring-2 focus:ring-blue-200 focus:outline-none
              placeholder:text-gray-400
              transition-all duration-200"
  placeholder="メールアドレスを入力"
/>

<!-- テーブルの縞模様 -->
<table class="w-full">
  <tbody>
    <tr class="odd:bg-gray-50 even:bg-white hover:bg-blue-50">
      <td class="p-3">行1</td>
    </tr>
    <tr class="odd:bg-gray-50 even:bg-white hover:bg-blue-50">
      <td class="p-3">行2</td>
    </tr>
    <tr class="odd:bg-gray-50 even:bg-white hover:bg-blue-50">
      <td class="p-3">行3</td>
    </tr>
  </tbody>
</table>

group-hoverの使い方

親要素にhoverした時に、子要素のスタイルを変えたい場合に使う。

<!-- 親要素に group クラスを付ける -->
<a
  href="#"
  class="group block bg-white rounded-lg p-6 shadow
                   hover:shadow-lg transition-shadow duration-200"
>
  <h3
    class="text-lg font-bold text-gray-900
             group-hover:text-blue-600 transition-colors duration-200"
  >
    記事タイトル
  </h3>
  <p class="text-gray-500 group-hover:text-gray-700 transition-colors duration-200">
    カードにhoverすると、タイトルの色も変わります。
  </p>
  <span
    class="text-blue-500 group-hover:translate-x-2
               inline-block transition-transform duration-200"
  >
    続きを読む →
  </span>
</a>

カスタマイズ(tailwind.config.js)

Tailwindの大きな強みは高いカスタマイズ性。tailwind.config.jsでプロジェクト独自のデザインシステムを構築できる。

テーマの拡張

// tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
  content: ['./src/**/*.{js,ts,jsx,tsx}'],
  theme: {
    extend: {
      // 独自の色を追加
      colors: {
        primary: {
          50: '#eff6ff',
          100: '#dbeafe',
          200: '#bfdbfe',
          300: '#93c5fd',
          400: '#60a5fa',
          500: '#3b82f6',
          600: '#2563eb',
          700: '#1d4ed8',
          800: '#1e40af',
          900: '#1e3a8a',
        },
        brand: {
          green: '#00C853',
          dark: '#1A1A2E',
        },
      },

      // 独自のフォントファミリー
      fontFamily: {
        sans: ['Noto Sans JP', 'sans-serif'],
        mono: ['JetBrains Mono', 'monospace'],
      },

      // 独自のスペーシング
      spacing: {
        128: '32rem',
        144: '36rem',
      },

      // 独自のブレイクポイント
      screens: {
        '3xl': '1920px',
      },

      // 独自のアニメーション
      animation: {
        'fade-in': 'fadeIn 0.5s ease-in-out',
        'slide-up': 'slideUp 0.3s ease-out',
      },
      keyframes: {
        fadeIn: {
          '0%': { opacity: '0' },
          '100%': { opacity: '1' },
        },
        slideUp: {
          '0%': { transform: 'translateY(20px)', opacity: '0' },
          '100%': { transform: 'translateY(0)', opacity: '1' },
        },
      },
    },
  },
  plugins: [],
}

使い方:

<!-- 独自の色を使う -->
<button class="bg-primary-500 hover:bg-primary-600 text-white">プライマリボタン</button>
<div class="bg-brand-dark text-white">ブランドカラー</div>

<!-- 独自のアニメーション -->
<div class="animate-fade-in">フェードイン表示</div>
<div class="animate-slide-up">スライドアップ表示</div>

extendとoverrideの違い

// tailwind.config.js
export default {
  theme: {
    // extend: 既存の値に追加(推奨)
    extend: {
      colors: {
        brand: '#FF6B00', // 既存の色はそのまま、brandだけ追加
      },
    },

    // 直接指定: 既存の値を完全に上書き(注意!)
    // colors: {
    //   brand: '#FF6B00', // これだけになり、blue, red等が使えなくなる
    // },
  },
}

重要: 基本的にはextendの中に書くこと。extendを使わないと既存のユーティリティが全て使えなくなる。


@apply(ユーティリティクラスの抽出)

@applyを使うと、よく使うユーティリティクラスの組み合わせをカスタムCSSクラスにまとめられる。

基本的な使い方

/* src/index.css */
@import 'tailwindcss';

/* ボタンの共通スタイルを定義 */
.btn {
  @apply px-4 py-2 rounded-lg font-medium transition-colors duration-200;
}

.btn-primary {
  @apply btn bg-blue-600 text-white hover:bg-blue-700;
}

.btn-secondary {
  @apply btn bg-gray-200 text-gray-800 hover:bg-gray-300;
}

.btn-danger {
  @apply btn bg-red-600 text-white hover:bg-red-700;
}

/* 入力欄の共通スタイル */
.input {
  @apply w-full px-4 py-2 border border-gray-300 rounded-lg
         focus:border-blue-500 focus:ring-2 focus:ring-blue-200 focus:outline-none
         transition-all duration-200;
}
<!-- 使い方 -->
<button class="btn-primary">保存</button>
<button class="btn-secondary">キャンセル</button>
<button class="btn-danger">削除</button>
<input type="text" class="input" placeholder="入力してください" />

@applyを使いすぎてはいけない理由

@applyは便利だが、多用するとTailwind CSSのメリットが失われる。

状況推奨理由
React/Vueコンポーネントでの再利用コンポーネント化JSXでクラスを直接書ける
HTMLだけのプロジェクトで同じスタイルを何度も使う@applyコンポーネントが使えないため
非常に長いクラスの組み合わせ@applyを検討可読性を優先する場合
ほぼ全てのスタイルを@applyで書くやめるべき従来のCSSを書くのと変わらなくなる

Reactを使っている場合、@applyよりもコンポーネント化で対応するのが推奨:

// Button.jsx -- コンポーネントで再利用する方が良い
const Button = ({ variant = 'primary', children, ...props }) => {
  const styles = {
    primary: 'bg-blue-600 text-white hover:bg-blue-700',
    secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
    danger: 'bg-red-600 text-white hover:bg-red-700',
  }

  return (
    <button
      className={`px-4 py-2 rounded-lg font-medium transition-colors duration-200 ${styles[variant]}`}
      {...props}
    >
      {children}
    </button>
  )
}

よく使うレイアウトパターン

パターン1: ナビゲーションバー

<nav class="bg-white shadow-md">
  <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
    <div class="flex items-center justify-between h-16">
      <!-- ロゴ -->
      <div class="flex-shrink-0">
        <a href="/" class="text-2xl font-bold text-blue-600"> TechBlog </a>
      </div>

      <!-- デスクトップナビ -->
      <div class="hidden md:flex items-center gap-8">
        <a
          href="#"
          class="text-gray-700 hover:text-blue-600 font-medium
                          transition-colors duration-200"
        >
          ホーム
        </a>
        <a
          href="#"
          class="text-gray-700 hover:text-blue-600 font-medium
                          transition-colors duration-200"
        >
          記事
        </a>
        <a
          href="#"
          class="text-gray-700 hover:text-blue-600 font-medium
                          transition-colors duration-200"
        >
          カテゴリ
        </a>
        <a
          href="#"
          class="text-gray-700 hover:text-blue-600 font-medium
                          transition-colors duration-200"
        >
          お問い合わせ
        </a>
      </div>

      <!-- ユーザーメニュー -->
      <div class="hidden md:flex items-center gap-4">
        <button class="text-gray-600 hover:text-gray-900">
          <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path
              stroke-linecap="round"
              stroke-linejoin="round"
              stroke-width="2"
              d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
            />
          </svg>
        </button>
        <button
          class="bg-blue-600 text-white px-4 py-2 rounded-lg
                       hover:bg-blue-700 transition-colors duration-200"
        >
          ログイン
        </button>
      </div>

      <!-- モバイルメニューボタン -->
      <div class="md:hidden">
        <button class="text-gray-600 hover:text-gray-900 p-2">
          <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path
              stroke-linecap="round"
              stroke-linejoin="round"
              stroke-width="2"
              d="M4 6h16M4 12h16M4 18h16"
            />
          </svg>
        </button>
      </div>
    </div>
  </div>
</nav>

パターン2: カードコンポーネント

<div
  class="max-w-sm bg-white rounded-xl shadow-md overflow-hidden
            hover:shadow-xl transition-shadow duration-300"
>
  <!-- カード画像 -->
  <div class="relative">
    <img
      src="https://via.placeholder.com/400x200"
      alt="カード画像"
      class="w-full h-48 object-cover"
    />
    <span
      class="absolute top-3 right-3 bg-blue-600 text-white text-xs
                 font-medium px-3 py-1 rounded-full"
    >
      新着
    </span>
  </div>

  <!-- カード本文 -->
  <div class="p-6">
    <div class="flex items-center gap-2 mb-3">
      <span class="text-xs font-medium text-blue-600 bg-blue-50 px-2 py-1 rounded">
        プログラミング
      </span>
      <span class="text-xs text-gray-400">2026年3月28日</span>
    </div>

    <h3
      class="text-lg font-bold text-gray-900 mb-2
               line-clamp-2 hover:text-blue-600 transition-colors"
    >
      Tailwind CSSで美しいUIを構築する方法
    </h3>

    <p class="text-gray-600 text-sm leading-relaxed mb-4 line-clamp-3">
      Tailwind CSSを使って、レスポンシブでアクセシブルなUIを 効率的に構築する方法を解説します。
    </p>

    <!-- フッター -->
    <div class="flex items-center justify-between pt-4 border-t border-gray-100">
      <div class="flex items-center gap-2">
        <div class="w-8 h-8 bg-gray-300 rounded-full"></div>
        <span class="text-sm text-gray-700">山田太郎</span>
      </div>
      <a href="#" class="text-sm text-blue-600 hover:text-blue-800 font-medium"> 続きを読む → </a>
    </div>
  </div>
</div>

パターン3: お問い合わせフォーム

<div class="max-w-lg mx-auto bg-white rounded-2xl shadow-lg p-8">
  <h2 class="text-2xl font-bold text-gray-900 mb-2">お問い合わせ</h2>
  <p class="text-gray-500 mb-8">お気軽にご連絡ください。</p>

  <form class="space-y-6">
    <!-- 名前(2列) -->
    <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
      <div>
        <label class="block text-sm font-medium text-gray-700 mb-1">姓</label>
        <input
          type="text"
          class="w-full px-4 py-2.5 border border-gray-300 rounded-lg
                      focus:border-blue-500 focus:ring-2 focus:ring-blue-200
                      focus:outline-none transition-all duration-200
                      placeholder:text-gray-400"
          placeholder="山田"
        />
      </div>
      <div>
        <label class="block text-sm font-medium text-gray-700 mb-1">名</label>
        <input
          type="text"
          class="w-full px-4 py-2.5 border border-gray-300 rounded-lg
                      focus:border-blue-500 focus:ring-2 focus:ring-blue-200
                      focus:outline-none transition-all duration-200
                      placeholder:text-gray-400"
          placeholder="太郎"
        />
      </div>
    </div>

    <!-- メール -->
    <div>
      <label class="block text-sm font-medium text-gray-700 mb-1"> メールアドレス </label>
      <input
        type="email"
        class="w-full px-4 py-2.5 border border-gray-300 rounded-lg
                    focus:border-blue-500 focus:ring-2 focus:ring-blue-200
                    focus:outline-none transition-all duration-200
                    placeholder:text-gray-400"
        placeholder="example@mail.com"
      />
    </div>

    <!-- お問い合わせ種類 -->
    <div>
      <label class="block text-sm font-medium text-gray-700 mb-1"> お問い合わせ種類 </label>
      <select
        class="w-full px-4 py-2.5 border border-gray-300 rounded-lg
                     focus:border-blue-500 focus:ring-2 focus:ring-blue-200
                     focus:outline-none transition-all duration-200
                     text-gray-700"
      >
        <option value="">選択してください</option>
        <option value="general">一般的なお問い合わせ</option>
        <option value="support">技術サポート</option>
        <option value="billing">料金について</option>
        <option value="other">その他</option>
      </select>
    </div>

    <!-- メッセージ -->
    <div>
      <label class="block text-sm font-medium text-gray-700 mb-1"> メッセージ </label>
      <textarea
        rows="5"
        class="w-full px-4 py-2.5 border border-gray-300 rounded-lg
                       focus:border-blue-500 focus:ring-2 focus:ring-blue-200
                       focus:outline-none transition-all duration-200
                       placeholder:text-gray-400 resize-none"
        placeholder="お問い合わせ内容を入力してください"
      ></textarea>
    </div>

    <!-- 送信ボタン -->
    <button
      type="submit"
      class="w-full bg-blue-600 text-white py-3 rounded-lg font-medium
                   hover:bg-blue-700 active:bg-blue-800
                   focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
                   transition-colors duration-200"
    >
      送信する
    </button>
  </form>
</div>

パターン4: フッター

<footer class="bg-gray-900 text-gray-300">
  <div class="max-w-7xl mx-auto px-4 py-12">
    <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
      <!-- ブランド情報 -->
      <div>
        <h3 class="text-white text-xl font-bold mb-4">TechBlog</h3>
        <p class="text-sm leading-relaxed mb-4">
          初心者からミドルエンジニアまで、実践的な技術情報を発信するブログです。
        </p>
      </div>

      <!-- リンク1 -->
      <div>
        <h4 class="text-white font-semibold mb-4">コンテンツ</h4>
        <ul class="space-y-2 text-sm">
          <li><a href="#" class="hover:text-white transition-colors">最新記事</a></li>
          <li><a href="#" class="hover:text-white transition-colors">チュートリアル</a></li>
          <li><a href="#" class="hover:text-white transition-colors">ハンズオン</a></li>
          <li><a href="#" class="hover:text-white transition-colors">書籍レビュー</a></li>
        </ul>
      </div>

      <!-- リンク2 -->
      <div>
        <h4 class="text-white font-semibold mb-4">カテゴリ</h4>
        <ul class="space-y-2 text-sm">
          <li><a href="#" class="hover:text-white transition-colors">フロントエンド</a></li>
          <li><a href="#" class="hover:text-white transition-colors">バックエンド</a></li>
          <li><a href="#" class="hover:text-white transition-colors">インフラ</a></li>
          <li><a href="#" class="hover:text-white transition-colors">AI/ML</a></li>
        </ul>
      </div>

      <!-- リンク3 -->
      <div>
        <h4 class="text-white font-semibold mb-4">関連リンク</h4>
        <ul class="space-y-2 text-sm">
          <li><a href="#" class="hover:text-white transition-colors">プライバシーポリシー</a></li>
          <li><a href="#" class="hover:text-white transition-colors">利用規約</a></li>
          <li><a href="#" class="hover:text-white transition-colors">お問い合わせ</a></li>
          <li><a href="#" class="hover:text-white transition-colors">運営者情報</a></li>
        </ul>
      </div>
    </div>

    <!-- コピーライト -->
    <div class="border-t border-gray-800 mt-8 pt-8 text-center text-sm">
      <p>&copy; 2026 TechBlog. All rights reserved.</p>
    </div>
  </div>
</footer>

Tailwind CSSとコンポーネントライブラリ

Tailwind CSSをベースにした人気のコンポーネントライブラリを紹介する。これらを使うことで、開発速度をさらに向上できる。

daisyUI

Tailwind CSS上に構築されたコンポーネントライブラリ。Bootstrapのようにクラス名を付けるだけでスタイルが適用される。

npm install daisyui
// tailwind.config.js
export default {
  plugins: [require('daisyui')],
  daisyui: {
    themes: ['light', 'dark', 'cupcake'],
  },
}
<!-- daisyUIのボタン -->
<button class="btn btn-primary">プライマリ</button>
<button class="btn btn-secondary">セカンダリ</button>
<button class="btn btn-accent">アクセント</button>

<!-- daisyUIのカード -->
<div class="card w-96 bg-base-100 shadow-xl">
  <div class="card-body">
    <h2 class="card-title">カードタイトル</h2>
    <p>カードの本文です。</p>
    <div class="card-actions justify-end">
      <button class="btn btn-primary">詳細</button>
    </div>
  </div>
</div>

shadcn/ui

コンポーネントのソースコードを自分のプロジェクトにコピーして使う方式のライブラリ。React + Tailwind CSS + Radix UIの組み合わせ。カスタマイズ性が非常に高い。

# 初期設定
npx shadcn@latest init

# 使いたいコンポーネントを追加
npx shadcn@latest add button
npx shadcn@latest add card
npx shadcn@latest add input
// Reactでの使用例
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'

export default function Page() {
  return (
    <Card>
      <CardHeader>
        <CardTitle>タイトル</CardTitle>
      </CardHeader>
      <CardContent>
        <p>コンテンツ</p>
        <Button variant="default">クリック</Button>
      </CardContent>
    </Card>
  )
}

ライブラリ比較表

特徴daisyUIshadcn/uiHeadless UI
対応フレームワーク全て(HTML/CSS)ReactReact, Vue
カスタマイズ性
コンポーネント数多い多い少ない(基本のみ)
導入の簡単さ非常に簡単やや手間中程度
バンドルサイズ小さい小さい(必要な分だけ)小さい
アクセシビリティ標準的Radix UIベースで優秀WAI-ARIA準拠
学習コスト低い中程度中程度
TypeScriptサポートあり優秀あり
適した場面プロトタイプ、個人開発本番アプリ独自デザインが必要な場合

パフォーマンス

未使用クラスの自動削除

Tailwind CSSは全てのユーティリティクラスを生成すると非常に大きなCSSファイルになる。しかし、ビルド時に実際に使用しているクラスだけを抽出して、未使用のクラスを自動的に削除する仕組みがある。

この仕組みが正しく動くためには、tailwind.config.jscontent設定が正確である必要がある:

// tailwind.config.js
export default {
  content: [
    './index.html',
    './src/**/*.{js,ts,jsx,tsx}', // ソースコードのパスを正確に指定
  ],
  // ...
}

やってはいけないこと(クラス名の動的生成)

Tailwindはビルド時にファイルを静的に解析するため、動的にクラス名を組み立てるとビルドから漏れてしまう。

// NG: 動的に文字列を結合してクラス名を作る
const color = 'blue'
const size = '500'
;<div className={`text-${color}-${size}`}>テキスト</div>
// → ビルド時に "text-blue-500" を認識できず、CSSが適用されない

// OK: 完全なクラス名を使う
const colorClass = 'text-blue-500'
;<div className={colorClass}>テキスト</div>

// OK: 条件分岐で完全なクラス名を使う
const textColor = isError ? 'text-red-500' : 'text-blue-500'
;<div className={textColor}>テキスト</div>

// OK: オブジェクトで管理する
const colorMap = {
  primary: 'text-blue-500',
  success: 'text-green-500',
  danger: 'text-red-500',
}
;<div className={colorMap[variant]}>テキスト</div>

ビルドサイズの比較

状態CSSファイルサイズの目安
開発時(全クラス含む)約3-4MB
ビルド後(未使用クラス削除済み)約10-30KB
gzip圧縮後約5-10KB

ビルド後のサイズは非常に小さく、パフォーマンスの問題にはならない。

clsxやtwMergeの活用

複数のクラスを条件付きで結合する場合、clsxtailwind-mergeを使うとコードがすっきりする。

npm install clsx tailwind-merge
// lib/utils.ts
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'

// クラス名を安全にマージするユーティリティ関数
export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}
import { cn } from '@/lib/utils'

// 使用例
;<button
  className={cn(
    'px-4 py-2 rounded-lg font-medium', // 基本スタイル
    isActive && 'bg-blue-600 text-white', // アクティブ時
    isDisabled && 'opacity-50 cursor-not-allowed', // 無効時
    className // 外部から渡されたクラス
  )}
>
  {children}
</button>

tailwind-mergeが重要な理由: 同じCSSプロパティに対して複数のクラスが指定された場合、後から指定したクラスが優先されるように解決してくれる。

import { twMerge } from 'tailwind-merge'

twMerge('px-4 px-8') // → 'px-8'(後のものが優先)
twMerge('text-red-500 text-blue-500') // → 'text-blue-500'

実践演習

以下の課題に取り組むことで、Tailwind CSSの理解が深まる。

演習1: プロフィールカードを作成

以下の要件でプロフィールカードを作ってみよう。

  • 丸いアバター画像
  • 名前(太字、大きめ)
  • 肩書き(グレー、小さめ)
  • 自己紹介文
  • SNSリンクボタン3つ(横並び)
  • ホバーエフェクト付き
  • レスポンシブ対応

演習2: 価格比較テーブルを作成

  • 3つのプラン(無料、プロ、ビジネス)
  • 各プランの機能一覧
  • おすすめプランを目立たせる
  • ダークモード対応

演習3: レスポンシブダッシュボード

  • サイドバー(モバイルでは非表示)
  • ヘッダーバー
  • 統計カード4つ(モバイル1列、タブレット2列、PC4列)
  • メインコンテンツエリア

参考リンク