タブやアコーディオン、ドロップダウン——「開いているときだけ見た目を変えたい」UIで、JavaScriptからクラスをいちいち付け外ししていませんか? classList.add('active') と remove('active') を書いて、状態管理が散らかる……あの面倒くささです。
Tailwind CSSの data属性バリアント(data-*)を使うと、JavaScriptは data-state="open" のような属性を切り替えるだけで済み、見た目はTailwind側が宣言的に担当してくれます。この記事では、data-[state=open]: の基本から、v4での真偽値data属性、ヘッドレスUIとの相性、group/peer との併用までを解説します。group や peer を先に押さえておくと理解が早いので、未読なら peerとは?groupとの違い もどうぞ。
data属性バリアントの基本:data-[state=open]:
HTMLの data-* 属性は、開発者が自由に付けられる「状態の入れ物」です。Tailwindでは data-[属性=値]: の形で、その属性の値に応じてスタイルを当てられます。
<div data-state="open"
class="h-12 overflow-hidden
data-[state=open]:h-auto">
開いているときだけ高さが auto になる
</div>data-state が "open" のときだけ h-auto が効きます。JavaScriptがやることは、この属性を "open" と "closed" で切り替えるだけ。クラス名を組み立てて付け外しする必要がありません。アコーディオンに使うと、こんな見た目になります。

真偽値のdata属性は角カッコなしで書ける(v4)
「値はいらない、属性が付いているかどうかだけ見たい」というケースも多いですよね(例:data-active が付いていたら強調)。Tailwind v4では、値を持たない真偽値の data-* 属性は角カッコなしで書けます。
<!-- data-active が付いているときだけ太字+色付き -->
<li data-active class="text-slate-600 data-active:font-bold data-active:text-indigo-600">
選択中の項目
</li>値で分岐したいときは data-[state=open]:、属性の有無だけ見たいときは data-active: ——この2つを使い分ければ、たいていの状態表現はカバーできます。
ヘッドレスUIとの相性が抜群
data属性バリアントが真価を発揮するのが、Radix UIやHeadless UIなどの「ヘッドレスUIライブラリ」との組み合わせです。これらのライブラリは、見た目を持たない代わりに、開閉や選択などの状態を data-state="open" や data-disabled といった属性で公開してくれます。
つまり、ライブラリが状態を属性で教えてくれる → Tailwindの data-* でその見た目を当てる、という役割分担が自然に成立します。アクセシビリティやキーボード操作はライブラリに任せ、デザインはTailwindで自由に作る。これが今どきのUI実装の定番パターンです。
<!-- Radixなどが data-state を自動で付与してくれる -->
<button class="px-4 py-2 rounded-lg
data-[state=active]:bg-indigo-500
data-[state=active]:text-white">
タブ
</button>group / peer と組み合わせる
data属性は、group や peer とも組み合わせられます。親の状態で子を変えたいなら group-data-[...]:、兄弟の状態で変えたいなら peer-data-[...]: です。
<!-- 親が data-state="open" のとき、中の矢印アイコンを回転 -->
<div data-state="open" class="group">
<svg class="transition group-data-[state=open]:rotate-180">...</svg>
</div>「アコーディオンを開いたら矢印が 180度 回る」のような演出が、JavaScriptでクラスを足さずに書けます。group の親→子、peer の兄弟→兄弟という関係に data-* を乗せるイメージです。この関係の基礎は peerとは?groupとの違い と groupを入れ子にする方法 で解説しています。
まとめ
Tailwindのdata属性バリアントを振り返ります。
- 値ありは
data-[state=open]:、真偽値はdata-active:(v4は角カッコ不要) - JavaScriptは属性を切り替えるだけ、見た目はTailwindが宣言的に担当
- Radix・Headless UIなどヘッドレスUIと組み合わせると役割分担がきれいに決まる
- 親の状態は
group-data-[...]:、兄弟の状態はpeer-data-[...]:
タブやアコーディオンを作る機会があれば、まず data-[state=open]: を試してみてください。状態管理がぐっとシンプルになります。関連して、peerとgroupの違い、全体像は Tailwind CSSの使い方【初心者向け】 もどうぞ。
スポンサーリンク
