Tailwind CSS インタラクティブ状態:hover/focus/active 状態プレフィックスとカーソルスタイル

状態プレフィックスの概要

Tailwindは状態プレフィックスを使用して、疑似クラスCSSを記述することなく、異なる状態の要素にスタイルを適用します。

プレフィックス CSS 疑似クラス 説明
hover: :hover マウスホバー
ocus: :focus フォーカス状態
ctive: :active マウス押下
disabled: :disabled 無効状態
isited: :visited 訪問済みリンク
irst: :first-child 最初の子要素
last: :last-child 最後の子要素
odd: :nth-child(odd) 奇数の子要素
even: :nth-child(even) 偶数の子要素

`html

状態プレフィックスデモ

hover 状態

<!-- focus 状態 -->
<div class="bg-white rounded-lg shadow p-6">
  <h3 class="font-semibold mb-4">focus 状態</h3>
  <div class="space-y-4">
    <input type="text" placeholder="入力欄をクリックしてフォーカス効果を確認"
           class="w-full p-3 border border-gray-300 rounded-lg focus:border-blue-500 focus:ring-2 focus:ring-blue-500/50 focus:outline-none transition-all">
    <input type="text" placeholder="カスタムフォーカスシャドウ"
           class="w-full p-3 border border-gray-300 rounded-lg focus:shadow-lg focus:shadow-purple-500/30 focus:border-purple-500 focus:outline-none transition-all">
  </div>
</div>

<!-- active 状態 -->
<div class="bg-white rounded-lg shadow p-6">
  <h3 class="font-semibold mb-4">active 状態</h3>
  <div class="flex gap-4">
    <button class="px-6 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600 active:bg-blue-800 active:scale-95 transition-all">
      押下で暗く・縮小
    </button>
    <button class="px-6 py-3 bg-white border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 active:bg-gray-100 active:scale-95 transition-all">
      押下エフェクト
    </button>
  </div>
</div>

<!-- disabled 状態 -->
<div class="bg-white rounded-lg shadow p-6">
  <h3 class="font-semibold mb-4">disabled 状態</h3>
  <div class="flex gap-4">
    <button class="px-6 py-3 bg-blue-500 text-white rounded-lg disabled:opacity-50 disabled:cursor-not-allowed">
      有効なボタン
    </button>
    <button disabled class="px-6 py-3 bg-blue-500 text-white rounded-lg disabled:opacity-50 disabled:cursor-not-allowed">
      無効なボタン
    </button>
    <input type="text" placeholder="有効な入力欄"
           class="p-3 border border-gray-300 rounded-lg disabled:bg-gray-100 disabled:cursor-not-allowed">
    <input type="text" disabled placeholder="無効な入力欄"
           class="p-3 border border-gray-300 rounded-lg disabled:bg-gray-100 disabled:cursor-not-allowed">
  </div>
</div>

<!-- 訪問済みリンク -->
<div class="bg-white rounded-lg shadow p-6">
  <h3 class="font-semibold mb-4">訪問済みリンク</h3>
  <div class="flex gap-4">
    <a href="#visited1" class="text-blue-600 hover:text-blue-800 visited:text-purple-600">
      クリック後に紫色に
    </a>
    <a href="#visited2" class="text-green-600 hover:text-green-800 visited:text-gray-400">
      クリック後に灰色に
    </a>
  </div>
</div>
`

従来のCSSとTailwindの比較 従来の方法では utton:hover { background-color: ... } のような疑似クラスルールを記述する必要がありますが、Tailwindでは hover:bg-blue-700 のようなプレフィックスで直接宣言できます。

💡 ポイント: 状態プレフィックスは組み合わせて使用できます。例えば hover:focus:ring-2 とすると、ホバーかつフォーカス時にスタイルを適用できます。

構造的疑似クラス(first/last/odd/even)

構造的疑似クラスプレフィックスは、特定の位置にある子要素を選択するために使用します。

`html

構造的疑似クラスデモ

first/last

  • 最初のアイテム(first:bg-blue-50)
  • 2番目のアイテム
  • 3番目のアイテム
  • 最後のアイテム(last:bg-green-50)
<!-- odd/even -->
<div class="bg-white rounded-lg shadow p-6">
  <h3 class="font-semibold mb-4">odd/even</h3>
  <ul class="space-y-0">
    <li class="p-3 odd:bg-white even:bg-gray-50 border-b">行 1(odd:bg-white)</li>
    <li class="p-3 odd:bg-white even:bg-gray-50 border-b">行 2(even:bg-gray-50)</li>
    <li class="p-3 odd:bg-white even:bg-gray-50 border-b">行 3(odd:bg-white)</li>
    <li class="p-3 odd:bg-white even:bg-gray-50 border-b">行 4(even:bg-gray-50)</li>
    <li class="p-3 odd:bg-white even:bg-gray-50 border-b">行 5(odd:bg-white)</li>
    <li class="p-3 odd:bg-white even:bg-gray-50">行 6(even:bg-gray-50)</li>
  </ul>
</div>

<!-- テーブル例 -->
<div class="bg-white rounded-lg shadow p-6">
  <h3 class="font-semibold mb-4">テーブルのしま模様</h3>
  <table class="w-full">
    <thead>
      <tr class="bg-gray-100">
        <th class="p-3 text-left">名前</th>
        <th class="p-3 text-left">メール</th>
        <th class="p-3 text-left">役割</th>
      </tr>
    </thead>
    <tbody>
      <tr class="odd:bg-white even:bg-gray-50 hover:bg-blue-50 transition-colors">
        <td class="p-3">山田</td>
        <td class="p-3">yamada@example.com</td>
        <td class="p-3">管理者</td>
      </tr>
      <tr class="odd:bg-white even:bg-gray-50 hover:bg-blue-50 transition-colors">
        <td class="p-3">鈴木</td>
        <td class="p-3">suzuki@example.com</td>
        <td class="p-3">編集者</td>
      </tr>
      <tr class="odd:bg-white even:bg-gray-50 hover:bg-blue-50 transition-colors">
        <td class="p-3">佐藤</td>
        <td class="p-3">sato@example.com</td>
        <td class="p-3">ユーザー</td>
      </tr>
      <tr class="odd:bg-white even:bg-gray-50 hover:bg-blue-50 transition-colors">
        <td class="p-3">田中</td>
        <td class="p-3">tanaka@example.com</td>
        <td class="p-3">ユーザー</td>
      </tr>
    </tbody>
  </table>
</div>
`

従来のCSSとTailwindの比較 従来の方法では li:first-child { ... } や r:nth-child(even) { ... } のようなセレクターを記述する必要がありますが、Tailwindでは irst:bg-blue-50 や even:bg-gray-50 のようなプレフィックスで直接宣言できます。

group-hover と peer

group-hover: と peer は、親子要素間または兄弟要素間のインタラクティブ状態を処理するために使用します。

`html

group-hover と peer デモ

group-hover 親子ホバー

🎨

デザインツール

親要素にホバーして効果を確認

パフォーマンス

親要素にホバーして効果を確認

🔒

セキュリティ

親要素にホバーして効果を確認

<!-- group 組み合わせ使用 -->
<div class="bg-white rounded-lg shadow p-6">
  <h3 class="font-semibold mb-4">group 組み合わせ使用</h3>
  <div class="space-y-4">
    <div class="group flex items-center gap-4 p-4 bg-gray-50 rounded-lg hover:bg-blue-50 transition-colors cursor-pointer">
      <div class="w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center group-hover:bg-blue-500 transition-colors">
        <svg class="w-5 h-5 text-blue-500 group-hover:text-white transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
        </svg>
      </div>
      <div class="flex-1">
        <h4 class="font-medium group-hover:text-blue-600 transition-colors">メニュー項目 1</h4>
        <p class="text-sm text-gray-500 group-hover:text-blue-400 transition-colors">説明テキスト</p>
      </div>
      <svg class="w-5 h-5 text-gray-400 group-hover:text-blue-500 group-hover:translate-x-1 transition-all" fill="none" stroke="currentColor" viewBox="0 0 24 24">
        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
      </svg>
    </div>

    <div class="group flex items-center gap-4 p-4 bg-gray-50 rounded-lg hover:bg-green-50 transition-colors cursor-pointer">
      <div class="w-10 h-10 bg-green-100 rounded-full flex items-center justify-center group-hover:bg-green-500 transition-colors">
        <svg class="w-5 h-5 text-green-500 group-hover:text-white transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
        </svg>
      </div>
      <div class="flex-1">
        <h4 class="font-medium group-hover:text-green-600 transition-colors">メニュー項目 2</h4>
        <p class="text-sm text-gray-500 group-hover:text-green-400 transition-colors">説明テキスト</p>
      </div>
      <svg class="w-5 h-5 text-gray-400 group-hover:text-green-500 group-hover:translate-x-1 transition-all" fill="none" stroke="currentColor" viewBox="0 0 24 24">
        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
      </svg>
    </div>
  </div>
</div>

<!-- peer 兄弟要素 -->
<div class="bg-white rounded-lg shadow p-6">
  <h3 class="font-semibold mb-4">peer 兄弟要素</h3>
  <div class="space-y-4">
    <div>
      <input type="email" id="email" placeholder=" "
             class="peer w-full p-3 pt-5 border border-gray-300 rounded-lg focus:border-blue-500 focus:ring-2 focus:ring-blue-500/50 focus:outline-none transition-all">
      <label for="email"
             class="absolute left-3 top-1 text-gray-400 text-sm transition-all peer-placeholder-shown:top-3.5 peer-placeholder-shown:text-base peer-focus:top-1 peer-focus:text-sm peer-focus:text-blue-500">
        メールアドレス
      </label>
    </div>
    <div class="relative">
      <input type="password" id="password" placeholder=" "
             class="peer w-full p-3 pt-5 border border-gray-300 rounded-lg focus:border-purple-500 focus:ring-2 focus:ring-purple-500/50 focus:outline-none transition-all">
      <label for="password"
             class="absolute left-3 top-1 text-gray-400 text-sm transition-all peer-placeholder-shown:top-3.5 peer-placeholder-shown:text-base peer-focus:top-1 peer-focus:text-sm peer-focus:text-purple-500">
        パスワード
      </label>
    </div>
    <div class="flex items-center gap-3">
      <input type="checkbox" id="agree" class="peer sr-only">
      <label for="agree"
             class="w-5 h-5 border-2 border-gray-300 rounded peer-checked:bg-blue-500 peer-checked:border-blue-500 cursor-pointer transition-colors flex items-center justify-center">
        <svg class="w-3 h-3 text-white opacity-0 peer-checked:opacity-100" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M5 13l4 4L19 7"></path>
        </svg>
      </label>
      <span class="text-gray-700">利用規約に同意します</span>
    </div>
  </div>
</div>
`
💡 ポイント: 親要素に group クラスを追加し、子要素に group-hover: プレフィックスを使用します。前の兄弟要素に peer クラスを追加し、後の兄弟要素に peer-focus: などのプレフィックスを使用します。

カーソルスタイル(cursor-*)

cursor-* クラスは、マウスが要素の上にホバーしたときのカーソルスタイルを制御します。

クラス CSS 値 説明
cursor-auto cursor: auto 自動(デフォルト)
cursor-default cursor: default デフォルト矢印
cursor-pointer cursor: pointer 指のポインター
cursor-wait cursor: wait 待機
cursor-text cursor: text テキスト選択
cursor-move cursor: move 移動
cursor-not-allowed cursor: not-allowed 禁止
cursor-grab cursor: grab 掴む
cursor-grabbing cursor: grabbing 掴んでいる
cursor-crosshair cursor: crosshair 十字カーソル
cursor-zoom-in cursor: zoom-in ズームイン
cursor-zoom-out cursor: zoom-out ズームアウト

`html

カーソルスタイルデモ

カーソルスタイル

👆
cursor-auto
👆
cursor-default
👆
cursor-pointer
cursor-wait
📝
cursor-text
cursor-move
🚫
cursor-not-allowed
cursor-grab
cursor-grabbing
cursor-crosshair
🔍
cursor-zoom-in
🔍
cursor-zoom-out
`

従来のCSSとTailwindの比較 従来の方法では cursor: pointer のようなCSSプロパティを記述する必要がありますが、Tailwindでは cursor-pointer のようなクラス名で直接宣言できます。

選択制御(select-*)

select-* クラスは、ユーザーがテキストを選択できるかどうかを制御します。

クラス CSS 値 説明
select-none user-select: none 選択不可
select-text user-select: text 選択可能(デフォルト)
select-all user-select: all クリックで全選択
select-auto user-select: auto 自動

`html

選択制御デモ

選択制御

select-none:選択不可

このテキストは選択できません。ボタンやラベルなどのインタラクティブ要素に適しています。

select-text:選択可能(デフォルト)

このテキストは選択できます。本文コンテンツに適しています。

select-all:クリックで全選択

const apiKey = "abc123";

<!-- 実践的応用 -->
<div class="bg-white rounded-lg shadow p-6">
  <h3 class="font-semibold mb-4">実践的応用</h3>
  <div class="space-y-4">
    <!-- コードブロック -->
    <div class="bg-gray-900 text-green-400 p-4 rounded-lg font-mono text-sm select-all">
      npm install tailwindcss
    </div>
    <!-- ボタンテキストは選択不可 -->
    <div class="flex gap-4">
      <button class="px-6 py-3 bg-blue-500 text-white rounded-lg select-none cursor-pointer hover:bg-blue-600">
        ボタンテキストは選択不可
      </button>
      <button class="px-6 py-3 bg-green-500 text-white rounded-lg select-none cursor-pointer hover:bg-green-600">
        送信確認
      </button>
    </div>
  </div>
</div>
`
💡 ポイント: select-none はボタンやラベルなど、テキスト選択が不要な要素によく使用されます。select-all はコードブロックやコマンドラインなど、ワンクリックコピーが望ましい箇所に使用されます。

リサイズ制御(resize-*)

esize-* クラスは、要素をリサイズできるかどうかを制御します。

クラス CSS 値 説明
esize-none
esize: none リサイズ不可
esize-y
esize: vertical 垂直方向のみリサイズ
esize-x
esize: horizontal 水平方向のみリサイズ
esize
esize: both リサイズ可能

`html

リサイズ制御デモ

リサイズ制御

resize-none:リサイズ不可

resize-y:垂直方向のみリサイズ

resize-x:水平方向のみリサイズ

resize:リサイズ可能

`

従来のCSSとTailwindの比較 従来の方法では esize: vertical のようなCSSプロパティを記述する必要がありますが、Tailwindでは esize-y のようなクラス名で直接宣言できます。

スクロール動作(scroll-*)

scroll-* クラスは、スクロール動作を制御します。

クラス CSS 値 説明
scroll-auto scroll-behavior: auto 自動(デフォルト)
scroll-smooth scroll-behavior: smooth スムーズスクロール
snap-start scroll-snap-align: start 開始位置にスナップ
snap-center scroll-snap-align: center 中央にスナップ
snap-end scroll-snap-align: end 終了位置にスナップ
snap-none scroll-snap-type: none スナップなし
snap-x scroll-snap-type: x mandatory 水平スナップ
snap-y scroll-snap-type: y mandatory 垂直スナップ

`html

スクロール動作デモ

スムーズスクロール

セクション 1

セクション 2

セクション 3

<!-- スクロールスナップ -->
<div class="bg-white rounded-lg shadow p-6">
  <h3 class="font-semibold mb-4">スクロールスナップ</h3>
  <div class="flex overflow-x-auto snap-x snap-mandatory gap-4 pb-4">
    <div class="snap-center flex-shrink-0 w-64 h-40 bg-blue-100 rounded-lg flex items-center justify-center">
      <span class="text-blue-700 font-semibold">カード 1</span>
    </div>
    <div class="snap-center flex-shrink-0 w-64 h-40 bg-green-100 rounded-lg flex items-center justify-center">
      <span class="text-green-700 font-semibold">カード 2</span>
    </div>
    <div class="snap-center flex-shrink-0 w-64 h-40 bg-purple-100 rounded-lg flex items-center justify-center">
      <span class="text-purple-700 font-semibold">カード 3</span>
    </div>
    <div class="snap-center flex-shrink-0 w-64 h-40 bg-orange-100 rounded-lg flex items-center justify-center">
      <span class="text-orange-700 font-semibold">カード 4</span>
    </div>
    <div class="snap-center flex-shrink-0 w-64 h-40 bg-red-100 rounded-lg flex items-center justify-center">
      <span class="text-red-700 font-semibold">カード 5</span>
    </div>
  </div>
</div>

<!-- スクロールバー非表示 -->
<div class="bg-white rounded-lg shadow p-6">
  <h3 class="font-semibold mb-4">スクロールバー非表示</h3>
  <div class="flex overflow-x-auto scrollbar-hide gap-4 pb-4">
    <div class="flex-shrink-0 w-48 h-32 bg-gradient-to-br from-blue-400 to-blue-600 rounded-lg flex items-center justify-center text-white">
      スクロールバー非表示
    </div>
    <div class="flex-shrink-0 w-48 h-32 bg-gradient-to-br from-green-400 to-green-600 rounded-lg flex items-center justify-center text-white">
      scrollbar-hide を使用
    </div>
    <div class="flex-shrink-0 w-48 h-32 bg-gradient-to-br from-purple-400 to-purple-600 rounded-lg flex items-center justify-center text-white">
      水平スクロール可能
    </div>
    <div class="flex-shrink-0 w-48 h-32 bg-gradient-to-br from-orange-400 to-orange-600 rounded-lg flex items-center justify-center text-white">
      続けてスクロール
    </div>
  </div>
</div>
`
💡 ポイント: 要素に scroll-smooth を追加すると、ページ全体でスムーズスクロールが有効になります。snap-x snap-mandatory と snap-center を組み合わせるとカルーセル効果が作成できます。

❓ よくある質問

Q group-hover と hover の違いは何ですか?
A hover: は要素自身のホバー状態です。group-hover: は親要素(group クラスを持つ)がホバーされたときに子要素にスタイルを適用します。親子要素間の連動したホバー効果に最適です。
Q peer はどのように機能しますか?
A 要素に peer クラスを追加し、その後の兄弟要素に peer-focus: や peer-valid: などのプレフィックスを使用してスタイルを適用します。フローティングラベルやフォームバリデーションによく使用されます。
Q disabled:opacity-50 が動作しないのはなぜですか?
A disabled: プレフィックスが実際の disabled 属性と一緒に使用されていることを確認してください。Tailwindは .disabled:opacity-50:disabled セレクターを生成するため、要素に disabled 属性が必要です。
Q スクロールスナップを実装するにはどうすればよいですか?
A スクロールコンテナに snap-x snap-mandatory(水平)または snap-y snap-mandatory(垂直)を追加し、子要素に snap-center または snap-start を追加してください。

📖 まとめ

esize-* で要素のリサイズ、scroll-* でスクロール動作を制御

📝 課題

  1. ⭐ hover:、 ocus:、ctive:、disabled: 状態プレフィックスを使用して、完全なインタラクティブ状態スタイリングを持つボタンコンポーネントを作成してください

  2. ⭐⭐ group と group-hover: を使用して、アイコン、テキスト、矢印が連動して変化するナビゲーションメニューを作成してください

  3. ⭐⭐⭐ peer と peer-focus: を使用してフローティングラベル効果を実装し、 ocus: と invalid: 状態を組み合わせてフォームバリデーションスタイリングを実装するフォームを作成してください

100%