TailwindCSS JS不要で状態管理ができるpeerプロパティについて

TailwindCSS JS不要で状態管理ができるpeerプロパティについて

投稿日:2026年6月17日 / 更新日:2026年6月18日

こんにちは、nhです。

6月に入り、梅雨らしい天気の日も増えてきましたね。
外出の予定が立てづらい時期ですが、その分まとまった時間を使ってフロントエンドの知識を深めるには良い季節かもしれません。

今回は、TailwindCSSの「peer」プロパティについてご紹介します。
peerは、フォーム要素などの状態に応じて別の要素のスタイルを変更できる機能です。
通常CSSでは兄弟セレクタ(`~`)などを利用して実現しますが、TailwindCSSではpeerpeer-*を利用することで、HTML上で直感的に状態管理を行うことができます。
特にフォーム周りの実装では活躍する機会が多く、JavaScriptを書かずに実現できるUIも少なくありません。

それでは、具体的な使い方を見ていきましょう。

1. チェックボックスの状態に応じてラベルの見た目を変更する

まずは最も基本的な使い方です。
チェックボックスにpeerを付与し、その状態をラベル側で参照します。

<input
  type="checkbox"
  id="agree"
  class="peer hidden"
/>

<label
  for="agree"
  class="cursor-pointer rounded border px-4 py-2
         peer-checked:bg-blue-500
         peer-checked:text-white"
>
  利用規約に同意する
</label>

チェック状態に応じてラベルの見た目を変更できます。
カスタムチェックボックスやカード選択UIなどでよく利用されるパターンです。

2. フォームバリデーションのエラーメッセージを表示する

入力値の検証状態に応じてメッセージを表示することもできます。

<input
  type="email"
  class="peer border p-2"
  placeholder="メールアドレス"
  required
/>

<p class="hidden text-sm text-red-500 peer-invalid:block">
  正しいメールアドレスを入力してください。
</p>

入力内容が不正な場合のみエラーメッセージが表示されます。
簡単なバリデーションであれば、JavaScriptを使わずに実装できます。

3. フローティングラベルを実装する

最近のフォームUIでよく見かけるフローティングラベルも、`peer`を利用して実装できます。

<div class="relative">
  <input
    type="text"
    placeholder=" "
    class="peer w-full border p-4"
  />

  <label
    class="absolute left-4 top-4 transition-all
           peer-focus:top-1
           peer-focus:text-xs
           peer-[:not(:placeholder-shown)]:top-1
           peer-[:not(:placeholder-shown)]:text-xs"
  >
    お名前
  </label>
</div>

フォーカス時や入力済みの状態に応じてラベルが移動するため、より洗練されたフォームUIを実現できます。
※Tailwind CSS v4系を前提にする場合は、`peer-[:not(:placeholder-shown)]` のようなArbitrary Variantが利用できます。

4. よく使うpeerバリエーション一覧

peerにはさまざまな状態を監視するためのバリエーションが用意されています。
実務でよく利用するものをまとめてみました。

クラス発火条件主な用途
peer-focus:*要素がフォーカスされた時入力補助
peer-hover:*要素にホバーした時補足情報表示
peer-disabled:*要素が無効化された時操作制御
peer-checked:*チェック状態の時チェックボックス、ラジオボタン
peer-invalid:*入力値が不正な時バリデーション

peer-focus

<input
  type="text"
  class="peer border p-2"
/>

<p class="peer-focus:text-blue-500">
  お名前を入力してください。
</p>

入力欄にフォーカスした際に説明文の色を変更できます。

peer-hover

<button class="peer">
  ヘルプ
</button>

<p class="hidden peer-hover:block">
  詳細な説明を表示しています。
</p>

ホバー時に補足情報を表示できます。

peer-disabled

<input
  type="text"
  disabled
  class="peer border p-2"
/>

<p class="peer-disabled:text-red-500">
  この項目は編集できません。
</p>

無効化状態に応じてメッセージの見た目を変更できます。

peer-checked

<input
  type="checkbox"
  class="peer"
/>

<label class="peer-checked:text-green-500">
  プランを選択
</label>

チェック状態を利用してUIを切り替えられます。

peer-invalid

<input
  type="email"
  required
  class="peer"
/>

<p class="hidden peer-invalid:block text-red-500">
  正しいメールアドレスを入力してください。
</p>

入力値が不正な場合のみエラーメッセージを表示できます。

5. peerとgroupの違い

`peer`とよく比較される機能として`group`があります。

peer

兄弟要素の状態を監視する場合に使用します。

<input class="peer" />

<p class="peer-focus:text-blue-500">
  説明文
</p>

group

親要素の状態を監視する場合に使用します。

<div class="group">
  <h3 class="group-hover:text-blue-500">
    タイトル
  </h3>
</div>

使い分け

利用シーン推奨
フォーム入力状態を参照したいpeer
チェックボックスの状態を参照したいpeer
親要素のホバー状態を参照したいgroup
カード全体のホバー演出を行いたいgroup

6. まとめ

TailwindCSSのpeerは、ある要素の状態を基準に別要素のスタイルを変更できる便利な機能です。

特にフォーム周りでは、

  • チェック状態の切り替え
  • バリデーション表示
  • フローティングラベル
  • フォーカス時の補助表示

など、多くのUIをJavaScriptなしで実装できます。

また、groupとの違いを理解しておくことで、よりシンプルで保守しやすいコードを書くことができます。

フォーム実装やインタラクティブなUIを作成する際は、ぜひ活用してみてください。

参考元:
http://Hover, focus, and other states

最後に、今回のサンプルコードを掲載します。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>TailwindCSS peer サンプル集</title>
  <script src="https://cdn.tailwindcss.com"></script>
</head>

<body class="bg-slate-100 py-10 px-4">
  <div class="mx-auto max-w-5xl">
    <header class="mb-10">
      <h1 class="text-4xl font-bold mb-2">
        TailwindCSS peer プロパティ サンプル集
      </h1>
      <p class="text-slate-600">
        ブログ記事で紹介した peer の実装例をまとめています。
      </p>
    </header>

    <div class="space-y-8">
      <!-- 1 -->
      <section class="rounded-xl bg-white p-6 shadow">
        <h2 class="mb-4 text-2xl font-bold">
          1. peer-checked
        </h2>
        <input
          id="agree"
          type="checkbox"
          class="peer hidden"
        >
        <label
          for="agree"
          class="inline-block cursor-pointer rounded-lg border border-slate-300 px-5 py-3 transition-all
									peer-checked:bg-blue-500
									peer-checked:text-white
									peer-checked:border-blue-500"
        >
          利用規約に同意する
        </label>
        <p class="mt-3 text-sm text-slate-500">
          チェック状態に応じてラベルの見た目が変化します。
        </p>
      </section>

      <!-- 2 -->
      <section class="rounded-xl bg-white p-6 shadow">
        <h2 class="mb-4 text-2xl font-bold">
          2. peer-invalid
        </h2>
        <div class="max-w-md">
          <input
            type="email"
            required
            placeholder="メールアドレス"
            class="peer w-full rounded-lg border border-slate-300 p-3"
          >
          <p class="mt-2 hidden text-sm text-red-500 peer-invalid:block">
            正しいメールアドレスを入力してください。
          </p>
        </div>
        <p class="mt-3 text-sm text-slate-500">
          メールアドレス形式でない場合にエラーメッセージが表示されます。
        </p>
      </section>

      <!-- 3 -->
      <section class="rounded-xl bg-white p-6 shadow">
        <h2 class="mb-4 text-2xl font-bold">
          3. フローティングラベル
        </h2>
        <div class="relative max-w-md">
          <input
            id="name"
            type="text"
            placeholder=" "
            class="peer w-full rounded-lg border border-slate-300 px-4 pt-6 pb-2"
          >
          <label
            for="name"
            class="absolute left-3 top-4 bg-white px-1 text-slate-500 transition-all duration-200
										peer-focus:top-0
										peer-focus:text-xs
										peer-focus:text-blue-500
										peer-[:not(:placeholder-shown)]:top-0
										peer-[:not(:placeholder-shown)]:text-xs"
          >
            お名前
          </label>
        </div>
        <p class="mt-3 text-sm text-slate-500">
          フォーカス時や入力済み状態でラベルが上へ移動します。
        </p>
      </section>

      <!-- 4 -->
      <section class="rounded-xl bg-white p-6 shadow">
        <h2 class="mb-4 text-2xl font-bold">
          4. peer-focus
        </h2>
        <div class="max-w-md">
          <input
            type="text"
            placeholder="お名前"
            class="peer w-full rounded-lg border border-slate-300 p-3"
          >
          <p class="mt-2 text-sm text-slate-500 transition-colors peer-focus:text-blue-500">
            お名前を入力してください。
          </p>
        </div>
        <p class="mt-3 text-sm text-slate-500">
          入力欄にフォーカスすると説明文の色が変わります。
        </p>
      </section>

      <!-- 5 -->
      <section class="rounded-xl bg-white p-6 shadow">
        <h2 class="mb-4 text-2xl font-bold">
          5. peer-hover
        </h2>
        <div>
          <button
            class="peer rounded-lg bg-slate-800 px-5 py-3 text-white"
          >
            ヘルプ
          </button>
          <p class="mt-2 hidden rounded bg-slate-100 p-3 text-sm text-slate-600 peer-hover:block">
            詳細な説明を表示しています。
          </p>
        </div>
        <p class="mt-3 text-sm text-slate-500">
          ボタンにホバーすると補足説明が表示されます。
        </p>
      </section>

      <!-- 6 -->
      <section class="rounded-xl bg-white p-6 shadow">
        <h2 class="mb-4 text-2xl font-bold">
          6. peer-disabled
        </h2>
        <div class="max-w-md">
          <input
            type="text"
            disabled
            value="編集不可"
            class="peer w-full rounded-lg border border-slate-300 bg-slate-100 p-3"
          >
          <p class="mt-2 text-sm text-slate-500 peer-disabled:text-red-500">
            この項目は現在編集できません。
          </p>
        </div>
        <p class="mt-3 text-sm text-slate-500">
          disabled状態に応じてメッセージの色を変更できます。
        </p>
      </section>

      <!-- 7 -->
      <section class="rounded-xl bg-white p-6 shadow">
        <h2 class="mb-6 text-2xl font-bold">
          7. peer と group の違い
        </h2>
        <div class="grid gap-6 md:grid-cols-2">
          <!-- peer -->
          <div class="rounded-lg border p-5">
            <h3 class="mb-3 text-lg font-semibold">
              peer
            </h3>
            <input
              type="text"
              placeholder="フォーカスしてください"
              class="peer w-full rounded border p-3"
            >
            <p class="mt-2 text-slate-500 peer-focus:text-blue-500">
              フォーカスすると色が変わります
            </p>
            <p class="mt-4 text-sm text-slate-400">
              兄弟要素の状態を監視
            </p>
          </div>

          <!-- group -->
          <div class="group rounded-lg border p-5 transition">
            <h3 class="mb-3 text-lg font-semibold group-hover:text-blue-500">
              group
            </h3>
            <p class="text-slate-500 group-hover:text-blue-500">
              カード全体にホバーしてください
            </p>
            <p class="mt-4 text-sm text-slate-400">
              親要素の状態を監視
            </p>
          </div>
        </div>
      </section>
    </div>

  </div>

</body>
</html>

この記事を書いた人
nh
nh
株式会社ウェブネーションのデザインチームに所属していますNYです。WEBサイトのデザイン作成やWEBサイト作成、WEBサイトの修正などWEBデザイン全般を担当してます。日々、WEBデザインについて勉強しています。