Style elements based on interaction, sibling/parent state, and color scheme — hover, focus, group, peer, and dark mode.
Why: prefix any utility with hover:, focus:, focus-visible:, or active: to apply it only in that state. These run live in the browser — try hovering, focusing, and clicking the elements below.
<div class="flex flex-col gap-4 p-4">
<button class="rounded-md bg-indigo-600 px-4 py-2 text-sm font-semibold text-white transition hover:bg-indigo-700 active:bg-indigo-800">
Hover and click me
</button>
<input
type="text"
placeholder="Focus me"
class="rounded-md border border-slate-300 px-3 py-2 text-sm outline-hidden focus:border-indigo-500 focus:ring-2 focus:ring-indigo-200"
/>
<a href="#" class="text-sm text-indigo-600 underline-offset-4 hover:underline">
Hover this link to underline it
</a>
</div>Why: add group to a parent and group-hover: / group-focus: to any descendant to style children based on the parent's state. Add peer to an input and peer-checked: / peer-invalid: to a sibling to style it based on that input's state.
<div class="space-y-6 p-4">
<a href="#" class="group block rounded-lg border border-slate-200 p-4">
<p class="font-semibold text-slate-900 group-hover:text-indigo-600">Hover the card</p>
<p class="text-sm text-slate-500 group-hover:text-slate-700">The title color changes via group-hover</p>
</a>
<label class="flex items-center gap-2">
<input type="checkbox" class="peer h-4 w-4" />
<span class="text-sm text-slate-500 peer-checked:font-semibold peer-checked:text-emerald-600">
Check me to style this label via peer-checked
</span>
</label>
</div>Why: prefix a utility with dark: to apply it only in dark mode. This demo enables class-based dark mode with @custom-variant (the v4 way) — edit the code and add class="dark" to the <html> tag (or remove it) to toggle the preview.
<!DOCTYPE html>
<html>
<head>
<style type="text/tailwindcss">
@custom-variant dark (&:where(.dark, .dark *));
</style>
</head>
<body class="bg-white p-6 text-slate-900 dark:bg-slate-900 dark:text-white">
<div class="rounded-lg border border-slate-200 p-4 dark:border-slate-700">
<h2 class="text-lg font-semibold">Card title</h2>
<p class="mt-1 text-sm text-slate-500 dark:text-slate-400">
Add class="dark" to the <html> tag above to switch themes.
</p>
</div>
</body>
</html>Why: has-* styles a parent based on its children — has-checked: matches when a descendant checkbox is checked, no JavaScript required. Common pseudo-classes have named shorthands (has-checked, has-focus); the bracket form has-[selector] is still there for anything else, like has-[img]. data-[state=open]: matches an element's own data-* attribute, useful for components that toggle state via attributes.
<div class="space-y-4 p-4">
<label class="flex cursor-pointer items-center gap-3 rounded-lg border border-slate-200 p-4 has-checked:border-indigo-500 has-checked:bg-indigo-50">
<input type="checkbox" class="h-4 w-4" />
<span class="text-sm font-medium">This card highlights when checked (has-checked)</span>
</label>
<div data-state="open" class="rounded-lg border border-slate-200 p-4 data-[state=open]:border-emerald-500 data-[state=closed]:opacity-50">
<p class="text-sm">data-state="open" → highlighted border</p>
</div>
</div>