メインコンテンツまでスキップ

状態バリアント

この章では、Tailwind CSS で要素の状態に応じたスタイリングを学びます。

ホバー・フォーカス・アクティブ

hover

マウスホバー時のスタイルを指定します。

<button class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-sm">
ホバーで色が変わる
</button>

<a href="#" class="text-blue-500 hover:text-blue-700 hover:underline">
ホバーで下線表示
</a>
備考

v4 では hover スタイルは @media (hover: hover) でラップされ、ホバー非対応デバイス (タッチ操作のみ) では適用されません。v3 までのタップ発火に依存している場合は @custom-variant hover (&:hover); で旧挙動に戻せます。

focus

フォーカス時のスタイルを指定します。

<input
type="text"
class="border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-500 focus:outline-hidden
px-4 py-2 rounded-sm"
placeholder="フォーカスで枠が青くなる"
/>

<button class="bg-blue-500 focus:ring-4 focus:ring-blue-300 text-white px-4 py-2 rounded-sm">
フォーカスリング
</button>

focus-visible

キーボード操作時のフォーカスのみに適用されます。

<button class="focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:outline-hidden
px-4 py-2 rounded-sm">
キーボードフォーカス時のみリング表示
</button>

focus-within

子要素がフォーカスされた時に親要素に適用されます。

<div class="border border-gray-300 focus-within:border-blue-500 focus-within:ring-1 focus-within:ring-blue-500 p-4 rounded-sm">
<label class="block text-sm mb-1">ラベル</label>
<input type="text" class="w-full focus:outline-hidden" />
</div>

active

クリック中のスタイルを指定します。

<button class="bg-blue-500 hover:bg-blue-600 active:bg-blue-700 text-white px-4 py-2 rounded-sm">
クリック中に色が変わる
</button>

フォーム状態

disabled

<button class="bg-blue-500 disabled:bg-gray-300 disabled:cursor-not-allowed text-white px-4 py-2 rounded-sm" disabled>
無効なボタン
</button>

<input
type="text"
class="border disabled:bg-gray-100 disabled:text-gray-500 disabled:cursor-not-allowed px-4 py-2 rounded-sm"
disabled
value="無効な入力"
/>

required

<input
type="text"
class="border required:border-red-500 px-4 py-2 rounded-sm"
required
/>

invalid

<input
type="email"
class="border invalid:border-red-500 invalid:text-red-500 focus:invalid:ring-red-500 px-4 py-2 rounded-sm"
value="invalid-email"
/>

valid

<input
type="email"
class="border valid:border-green-500 px-4 py-2 rounded-sm"
value="valid@email.com"
/>

checked

<label class="flex items-center gap-2">
<input
type="checkbox"
class="w-5 h-5 appearance-none rounded-sm border border-gray-300 checked:bg-blue-500 checked:border-transparent"
/>
チェックボックス
</label>

indeterminate

<input
type="checkbox"
id="some-checkbox"
class="appearance-none indeterminate:bg-gray-300"
/>

<script>
document.getElementById('some-checkbox').indeterminate = true;
</script>

indeterminate 状態は HTML 属性では設定できず、JavaScript からのみ設定できます。

placeholder-shown

<input
type="text"
class="border placeholder-shown:border-gray-300 not-placeholder-shown:border-blue-500 px-4 py-2 rounded-sm"
placeholder="プレースホルダー"
/>

read-only

<input
type="text"
class="border read-only:bg-gray-100 read-only:cursor-not-allowed px-4 py-2 rounded-sm"
readonly
value="読み取り専用"
/>

グループ

親要素の状態に基づいて子要素をスタイリングします。

group-hover

<div class="group p-4 bg-white rounded-lg shadow-sm hover:bg-blue-500 cursor-pointer">
<h3 class="font-bold group-hover:text-white">タイトル</h3>
<p class="text-gray-600 group-hover:text-blue-100">説明文</p>
</div>

group-focus

<button class="group px-4 py-2 rounded-sm border focus:ring-2">
<span class="group-focus:text-blue-500">フォーカス時に青くなる</span>
</button>

名前付きグループ

複数のグループを区別できます。

<div class="group/card p-4 bg-white rounded-lg shadow-sm hover:shadow-lg">
<div class="group/title">
<h3 class="font-bold group-hover/card:text-blue-500">タイトル</h3>
<button class="opacity-0 group-hover/title:opacity-100">編集</button>
</div>
<p class="group-hover/card:text-gray-700">説明文</p>
</div>

ピア (兄弟要素)

兄弟要素の状態に基づいてスタイリングします。

peer

<div>
<input type="checkbox" id="toggle" class="peer sr-only" />
<label for="toggle" class="cursor-pointer">トグル</label>
<div class="hidden peer-checked:block mt-2 p-4 bg-gray-100 rounded-sm">
チェック時に表示されるコンテンツ
</div>
</div>

フォームバリデーション

<div>
<input
type="email"
class="peer border px-4 py-2 rounded-sm invalid:border-red-500"
placeholder="メールアドレス"
required
/>
<p class="hidden peer-invalid:block text-red-500 text-sm mt-1">
有効なメールアドレスを入力してください
</p>
</div>

名前付きピア

<div>
<input type="text" class="peer/name" placeholder="名前" />
<input type="email" class="peer/email" placeholder="メール" />
<p class="hidden peer-invalid/email:block text-red-500">
メールが無効です
</p>
</div>

構造ベースの擬似クラス

first / last

<ul>
<li class="py-2 border-b first:pt-0 last:pb-0 last:border-b-0">項目 1</li>
<li class="py-2 border-b first:pt-0 last:pb-0 last:border-b-0">項目 2</li>
<li class="py-2 border-b first:pt-0 last:pb-0 last:border-b-0">項目 3</li>
</ul>

odd / even

<table>
<tbody>
<tr class="odd:bg-gray-50 even:bg-white">
<td class="p-4">行 1</td>
</tr>
<tr class="odd:bg-gray-50 even:bg-white">
<td class="p-4">行 2</td>
</tr>
<tr class="odd:bg-gray-50 even:bg-white">
<td class="p-4">行 3</td>
</tr>
</tbody>
</table>

first-of-type / last-of-type

<div>
<p class="first-of-type:font-bold">最初の p 要素</p>
<p>2 番目の p 要素</p>
<p class="last-of-type:text-gray-500">最後の p 要素</p>
</div>

only-child

<ul>
<li class="only:text-center">唯一の子要素の場合のみ中央揃え</li>
</ul>

empty

<div class="p-4 empty:hidden">
<!-- 空の場合は非表示 -->
</div>

before / after

擬似要素 ::before::after のスタイリング。

<span class="before:content-['*'] before:text-red-500 before:mr-1">
必須項目
</span>

<a href="#" class="relative after:absolute after:bottom-0 after:left-0 after:w-full after:h-0.5 after:bg-blue-500 after:scale-x-0 hover:after:scale-x-100 after:transition-transform">
ホバーで下線アニメーション
</a>

コンテンツなしの擬似要素

<div class="relative before:content-[''] before:block before:absolute before:inset-0 before:bg-linear-to-r before:from-blue-500 before:to-purple-500 before:opacity-50">
グラデーションオーバーレイ
</div>

has

v3.4 で追加された has-* バリアント。CSS の :has() セレクタを Tailwind のユーティリティとして使用できます。v4 では has-checked: のような名前付き形式も利用できます。

<!-- 子要素にチェックされた入力がある場合 -->
<div class="has-[:checked]:bg-blue-50 has-[:checked]:border-blue-500 border p-4 rounded-sm">
<label class="flex items-center gap-2">
<input type="checkbox" />
オプション
</label>
</div>

<!-- 特定の子要素を持つ場合 -->
<div class="has-[img]:p-0 p-4">
<img src="..." />
</div>

バリアントの組み合わせ

複数のバリアントを組み合わせることができます。

<!-- レスポンシブ + ホバー -->
<button class="bg-blue-500 md:hover:bg-blue-600 px-4 py-2 rounded-sm">
md 以上でのみホバー効果
</button>

<!-- グループ + フォーカス -->
<div class="group">
<button class="group-focus:ring-2">ボタン</button>
</div>

<!-- ダークモード + ホバー -->
<button class="bg-white dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700">
ダークモード対応ホバー
</button>

実践例

インタラクティブなカード

<div class="group relative bg-white rounded-xl shadow-md hover:shadow-xl transition-shadow overflow-hidden">
<div class="absolute inset-0 bg-linear-to-r from-blue-500 to-purple-500 opacity-0 group-hover:opacity-10 transition-opacity"></div>
<img src="..." class="w-full h-48 object-cover group-hover:scale-105 transition-transform duration-300" />
<div class="p-6">
<h3 class="font-bold text-xl group-hover:text-blue-500 transition-colors">タイトル</h3>
<p class="mt-2 text-gray-600">説明文...</p>
<button class="mt-4 px-4 py-2 bg-blue-500 text-white rounded-sm opacity-0 group-hover:opacity-100 translate-y-2 group-hover:translate-y-0 transition-all">
詳細を見る
</button>
</div>
</div>

フローティングラベル入力

<div class="relative">
<input
type="text"
id="email"
class="peer w-full px-4 py-3 border rounded-lg placeholder-transparent focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="メールアドレス"
/>
<label
for="email"
class="absolute left-4 top-3 text-gray-500 transition-all
peer-placeholder-shown:top-3 peer-placeholder-shown:text-base
peer-focus:-top-2.5 peer-focus:text-sm peer-focus:text-blue-500
-top-2.5 text-sm bg-white px-1"
>
メールアドレス
</label>
</div>

アコーディオン

<div class="border rounded-lg">
<div>
<input type="checkbox" id="accordion1" class="peer sr-only" />
<label for="accordion1" class="flex justify-between items-center p-4 cursor-pointer hover:bg-gray-50 peer-checked:[&_svg]:rotate-180">
<span class="font-medium">セクション 1</span>
<svg class="w-5 h-5 transition-transform">...</svg>
</label>
<div class="hidden peer-checked:block px-4 pb-4">
アコーディオンの内容...
</div>
</div>
</div>

まとめ

  • hover: / focus: / active: で基本的なインタラクション
  • disabled: / invalid: / checked: でフォーム状態
  • group-* で親要素の状態に基づくスタイリング
  • peer-* で兄弟要素の状態に基づくスタイリング
  • first: / last: / odd: / even: で位置ベースのスタイリング
  • before: / after: で擬似要素
  • has-* で子要素の状態に基づくスタイリング
  • バリアントは組み合わせ可能

次に読む