「チェックされたら、別の要素の色を変えたい」「入力エラーのときだけメッセージを赤く出したい」——こういうUI、JavaScriptを書かずにできたら楽だと思いませんか?
Tailwind CSSには group という仕組みがあって、これを知っている方は多いはずです。でも、その兄弟分にあたる peer となると「名前は見たことあるけど違いがよくわからない」という方が多いのではないでしょうか。
group は「親にホバーしたら子の見た目を変える」のが得意ですが、フォームでは少し使いづらい場面があります。そんなときに登場するのが peer です。チェックボックス・ラジオボタン・入力エラー表示など、フォーム部品の状態を別の要素に伝えたいときに活躍します。「なぜ group とは別に peer があるのか」は、ここを押さえると一気に腑に落ちます。
この記事では、peer と group の違いを図で押さえたうえで、peer-checked や peer-invalid を使った実例、よく使うバリアント一覧、そして「peer が効かない!」ときのチェックリストまで、コピペできるコードで解説します。Tailwindの導入がまだの方は RailsにTailwind CSS v4を導入する方法 から、全体像をつかみたい方は Tailwind CSSの使い方【初心者向け】 も合わせてどうぞ。
目次
まず結論:groupは「親→子」、peerは「兄弟→兄弟」
細かい話に入る前に、いちばん大事なところだけ先に言ってしまいます。
group:親の状態(hoverなど)を、その中の子要素のスタイルに反映するpeer:先に書いた兄弟の状態(checkedなど)を、その後ろの兄弟のスタイルに反映する
図にするとこうです。向き(だれの状態を、どこに伝えるか)がまったく違います。

「親をまるごとホバーしたら中身が反応してほしい」なら group、「あるフォーム部品の状態を、その隣の要素に伝えたい」なら peer。この使い分けさえ掴めば、あとは応用です。group をさらに入れ子にして使う方法は Tailwind CSS v3.2で追加された機能を使ってgroupを入れ子にする方法 で詳しく書いているので、group 側を深掘りしたい方はそちらへ。
peerの基本:チェックボックスでラベルの色を変える
いちばんシンプルな例から始めましょう。チェックボックスに peer クラスを付けると、その後ろにある要素から peer-checked: でチェック状態を参照できます。
<input type="checkbox" class="peer" />
<label class="text-slate-500 peer-checked:text-indigo-600">
チェックすると色が変わります
</label>ポイントは2つだけです。
- 状態を「持つ側」(=チェックボックス)に
peerを付ける - 状態に「反応する側」(=ラベル)に
peer-checked:を付ける
これで、チェックを入れた瞬間にラベルの文字色が変わります。JavaScriptは一行も書いていません。checked だけでなく、状態に応じたバリアントがひと通り用意されています。
よく使うpeerバリアント一覧
「peer-checked 以外にどんなのがあるの?」という方向けに、実務で出番の多いものをまとめておきます。基本は peer-(状態): の形で、状態の部分はおなじみのCSS擬似クラスに対応しています。
| クラス | 効くタイミング | よく使う場面 |
|---|---|---|
peer-checked: | チェック/選択されたとき | チェックボックス・ラジオ・トグル |
peer-focus: | フォーカスされたとき | 入力中のラベル強調・フローティングラベル |
peer-hover: | ホバーされたとき | 兄弟をホバーしたときの補足表示 |
peer-invalid: | 入力が不正なとき | バリデーションエラー表示 |
peer-required: | 必須項目のとき | 「必須」マークの表示 |
peer-disabled: | 無効化されているとき | 使えない項目をグレーアウト |
peer-placeholder-shown: | 未入力(プレースホルダー表示中)のとき | 未入力かどうかでの出し分け |
このうち peer-checked: と peer-invalid: が二大巨頭です。次の章で、それぞれ実例を見ていきます。
実例1:チェックボックスを「ボタン風」にする(peer-checked)
実務でよく使うのが、標準のチェックボックスを隠して、見た目をリッチにするパターンです。本物の <input> は sr-only で隠しておき(=画面上は見えないがアクセシビリティは保たれる)、見た目用の <span> を peer-checked: で切り替えます。
<label class="flex items-center gap-3 p-3 rounded-lg border border-slate-200 cursor-pointer">
<input type="checkbox" class="peer sr-only" />
<!-- 見た目のチェックボックス -->
<span class="w-5 h-5 rounded border-2 border-slate-300
peer-checked:bg-indigo-500 peer-checked:border-indigo-500"></span>
<!-- ラベル -->
<span class="text-slate-600
peer-checked:text-indigo-600 peer-checked:font-bold">
メール通知を受け取る
</span>
</label>チェックすると、四角が塗りつぶされ、ラベルも色付き・太字に変わります。実際の見た目はこんなイメージです(左:未チェック/右:チェック時)。

ラジオボタンでも同じ要領です。「選択中のカードだけ枠を強調する」といったカード選択UIも、この応用でJavaScriptなしに作れます。
実例2:フォームのバリデーション表示(peer-invalid)
もうひとつの定番が、入力エラーの表示です。まずはいちばんシンプルな形から覚えましょう。<input> に peer を付けておけば、後ろのエラーメッセージを peer-invalid: で出し分けられます。peer-invalid: は、ブラウザのHTML5バリデーション(type="email" や required など)が「不正」と判断したときに効きます。
STEP1:まずは peer-invalid だけ
<input
type="email"
required
placeholder="you@example.com"
class="peer px-3 py-2 rounded-lg border border-slate-300
focus:border-indigo-500 focus:outline-none" />
<p class="invisible peer-invalid:visible mt-1 text-sm text-red-600">
メールアドレスの形式が正しくありません
</p>普段はメッセージを invisible で隠しておき、入力が不正になった瞬間に peer-invalid:visible で表示する、という流れです。見た目はこうなります。

まずはこれで十分動きます。「peer-invalid:visible で不正なときだけ表示する」——この形をしっかり押さえてください。
STEP2:実務では「入力し始めてから」出す
STEP1のコードには、実は落とし穴があります。required な入力欄は「空っぽ」の時点でもinvalidなので、ページを開いた瞬間から(まだ何も入力していないのに)エラーが出てしまうのです。
そこで実務では「入力し始めてから(=空でなくなってから)だけ出したい」ことが多く、:placeholder-shown(プレースホルダーが見えている=未入力の状態)を組み合わせます。少しコードが複雑になるので、初めての方はSTEP1だけ覚えて、ここは「こういう書き方もある」と眺める程度でOKです。
<p class="invisible mt-1 text-sm text-red-600
peer-[&:not(:placeholder-shown):invalid]:visible">
メールアドレスの形式が正しくありません
</p>peer-[...] は「任意のセレクタで peer の状態を細かく指定する」書き方で、角カッコの中の & が peer 自身(=入力欄)を指します。:not(:placeholder-shown) で「未入力ではない」、:invalid で「不正」——つまり「何か入力されていて、かつ形式が不正」のときだけメッセージが出ます。placeholder 属性を付けておくのを忘れずに。
ハマりポイント①:peerは「先に書いた兄弟」しか参照できない
peer でいちばん多いつまずきが、これです。peer-* は、peer が付いた要素より後ろにある兄弟にしか効きません。これはCSSの仕組み(後続兄弟を選ぶセレクタ)に由来する制約で、Tailwindの都合ではありません。
<!-- ❌ 効かない:label が input より前にある -->
<label class="peer-checked:text-indigo-600">ラベル</label>
<input type="checkbox" class="peer" />
<!-- ✅ peer を先に書く -->
<input type="checkbox" class="peer" />
<label class="peer-checked:text-indigo-600">ラベル</label>「peer-checked: を書いたのに反応しない」ときは、まずHTMLの順番を疑ってください。反応させたい要素は、必ず peer の後ろに置きます。また、両者は同じ親の中の兄弟である必要があります(親をまたいで上の階層には伝わりません)。階層をまたいで親の状態を子に伝えたいなら、それは peer ではなく group の出番です。
ハマりポイント②:peerが複数あるときは「名前」で区別する
同じ階層に peer が2つ以上あると、peer-checked: はどれの状態を見ればいいのか区別できません。そんなときは、group と同じく名前付きpeer(Tailwind v3.2以降)を使います。peer/名前 で名前を付け、peer-checked/名前: で特定のpeerだけを参照します。
<input type="checkbox" class="peer/published" />
<input type="checkbox" class="peer/draft" />
<p class="hidden peer-checked/published:block">公開中です</p>
<p class="hidden peer-checked/draft:block">下書きです</p>これで「publishedにチェックが入ったら『公開中です』、draftなら『下書きです』」と、それぞれ独立して反応させられます。複数のトグルが絡むUIでは、名前を付けておくと混線せずに済みます。
peerが効かないときのチェックリスト
「peer-checked: を書いたのに反応しない!」——peer でつまずいたら、まずこの順に確認してください。だいたいこのどれかに当てはまります。
- ☑ 反応させたい要素が
peerより「後ろ」に書かれているか(前にあると効かない/ハマりポイント①) - ☑ 両者は同じ親の中の兄弟か(親をまたいで上の階層には伝わらない)
- ☑
peerを付けた要素が、本当にその状態になるか(peer-checked:なら相手はcheckbox/radioか) - ☑ 同じ階層に
peerが複数ないか(あるなら名前付きpeer/名前で区別/ハマりポイント②) - ☑ そのクラスがビルドに含まれているか(動的に組み立てたクラス名はTailwindが検出できず生成されないことがある。
contentの対象に入っているか確認)
特に多いのが、いちばん上の「順番」と、いちばん下の「クラスが生成されていない」の2つです。順番は先ほどのハマりポイント①で詳しく触れたとおり、生成まわりは「書いたクラスがそもそもCSSに出ているか」をDevToolsで確認すると早いです。
group-hoverとpeerの違い、結局どっち?
この記事の本丸、group と peer の違いを表で整理します。迷ったら「だれの状態を、どこに伝えたいか」で選んでください。
| 比較 | group | peer |
|---|---|---|
| 状態を持つ側 | 親要素(group) | 兄弟要素(peer) |
| 反応する側 | 中の子要素 | 後ろの兄弟要素 |
| 向き | 親 → 子 | 兄弟 → 兄弟 |
| 得意な場面 | hover系のUI | フォーム系のUI |
| よく使う例 | カード全体をhover→中のボタンや矢印を強調 | チェックボックス/入力欄→ラベルやエラー表示 |
| 代表バリアント | group-hover: group-focus: | peer-checked: peer-invalid: peer-focus: |
覚え方はシンプルです。迷ったら、こう判断してください。
- hover系(カードやボタンをホバーして中身を変える)→
group - フォーム系(チェックや入力エラーで隣を変える)→
peer
もちろん両方を組み合わせることもできます(例:カード全体は group、中のチェックボックスは peer)。レイアウトやカードUIそのものの組み立て方は Tailwind CSSの使い方【初心者向け】 に、group を入れ子にする応用は groupを入れ子にする方法 にまとめているので、合わせて読むとTailwindのstate系がひと通り押さえられます。
まとめ
peer のポイントを振り返ります。
group=親→子、peer=兄弟→兄弟。迷ったらhover系→group、フォーム系→peer- 状態を持つ側に
peer、反応する側にpeer-checked:/peer-invalid:などを付ける peer-invalid:はまず単独で覚え、実務では:placeholder-shownと組み合わせる- 効かないときはチェックリストで確認(特に「順番」と「クラスが生成されているか」)
peerが複数あるときは 名前付きpeer(peer/名前)で区別する
チェックボックスやフォームのちょっとしたインタラクションは、peer を覚えるだけでJavaScriptなしにかなり作れるようになります。まずは「チェックしたらラベルの色が変わる」最小例から手を動かしてみてください。
関連記事として、親の状態を入れ子の子に伝える groupを入れ子にする方法、レイアウトやカードUIの組み立てを一気に学べる Tailwind CSSの使い方【初心者向け】 もどうぞ。
スポンサーリンク
