Stacking Variants: Responsive + Dark + State
Compose multiple conditions on one utility and keep the result readable.
Variants stack. Chain them with colons and the utility applies only when every condition is true, read left to right.
md:dark:hover:bg-slate-700That means: from the md breakpoint up, in dark mode, on hover — use this background. Any one condition false, the utility sits out.
Dark mode in v4
By default dark: follows the operating system via prefers-color-scheme. Want a manual toggle instead? Register a class-based variant once in CSS:
@custom-variant dark (&:where(.dark, .dark *));Now adding class="dark" to any ancestor switches every dark: utility beneath it.
The powerful newer variants
has-[...]:— style a parent based on a descendant (has-[:checked]:bg-cyan-50).not-*— negate a variant (not-first:border-t).*:— target direct children (*:rounded-lg).
Keep it scannable
Stacking order does not change specificity, but it changes how fast a teammate parses the class. Pick one order — breakpoint, then theme, then state — and hold to it everywhere.
Example
When to use it
- A developer applies dark:md:text-white to show white text only on medium screens in dark mode without writing any media-query CSS.
- A button uses group-hover:focus:ring-2 so the focus ring only appears when the button is focused and its parent card is hovered.
- A complex form input combines hover:dark:border-indigo-400 so the border changes color only in dark mode on hover.
More examples
Dark mode + responsive stack
dark:md:text-xl applies a larger font only at medium breakpoints when in dark mode, while each prefix condition is independent for other combinations.
<p class="text-gray-700
dark:text-gray-300
md:text-lg
dark:md:text-xl">
Text adapts to mode and breakpoint independently.
</p>State + responsive stack
Breakpoint prefixes before hover: change the hover color at each viewport size, showing how state variants compose with responsive prefixes.
<button
class="bg-gray-100 px-4 py-2 rounded
hover:bg-gray-200
md:hover:bg-indigo-100
lg:hover:bg-indigo-200 lg:hover:text-indigo-800"
>
Multi-context hover
</button>Triple variant: responsive + dark + state
md:dark:focus:ring-indigo-500 stacks three conditions β breakpoint, color scheme, and input state β into a single utility for fine-grained control.
<input
type="text"
class="border rounded px-3 py-2 w-full
focus:ring-2 focus:ring-blue-500
dark:focus:ring-indigo-400
md:dark:focus:ring-offset-2 md:dark:focus:ring-indigo-500"
/>
Discussion