Responsive & State Variants

Responsive prefixes like md: and lg:, state variants like hover: and focus:, and dark mode with dark:.

Responsive prefixes

Tailwind is mobile-first: an unprefixed utility applies at every screen size, and a breakpoint prefix makes a utility apply only from that breakpoint up. This mirrors the mobile-first min-width media query approach from plain CSS.

Prefix Applies from (default)
(none) All screen sizes
sm: 640px and up
md: 768px and up
lg: 1024px and up
xl: 1280px and up
2xl: 1536px and up
HTML
<div class="text-base md:text-lg lg:text-xl">
    Font size grows as the viewport gets wider
</div>

<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
    <!-- 1 column on mobile, 2 from sm, 4 from lg -->
</div>

<nav class="hidden md:flex gap-6">
    <!-- hidden on mobile, shown as a flex row from md up -->
</nav>

Read md:grid-cols-2 as "apply grid-cols-2 once the viewport is at least the md breakpoint" — exactly equivalent to writing @media (min-width: 768px) { grid-template-columns: repeat(2, 1fr); } by hand, just inline.

State variants: hover:, focus:, disabled:

State variants apply a utility only when the element is in a particular interactive state:

HTML
<button class="bg-blue-500 hover:bg-blue-600 focus:ring-2 focus:ring-blue-300 active:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed text-white px-4 py-2 rounded-md transition">
    Submit
</button>
Variant Applies when
hover: The pointer is over the element
focus: The element has keyboard/click focus
active: The element is being clicked/pressed
disabled: The element has the disabled attribute
focus-within: Any descendant has focus (useful on a form wrapper)
group-hover: A parent marked group is hovered — lets a hover on a card affect a child element

group is a small but powerful pattern for hovering a parent to style a child:

HTML
<div class="group p-4 border rounded-lg hover:border-blue-400">
    <h3 class="text-gray-900">Card title</h3>
    <p class="text-gray-500 group-hover:text-blue-500">
        This text changes color when the *card* (not just this text) is hovered.
    </p>
</div>

Combining responsive and state variants

Variants stack, and can be combined in any order the class name expresses left to right — a common pattern is a responsive prefix plus a state variant on the same utility:

HTML
<button class="bg-blue-500 md:hover:bg-blue-700">
    Only applies the hover color from md breakpoint up
</button>

Dark mode: dark:

Tailwind ships a dark: variant that applies when dark mode is active. By default it follows the operating system's prefers-color-scheme, but it's commonly switched to a manual toggle using the class strategy:

Javascript
// tailwind.config.js
module.exports = {
    darkMode: 'class', // instead of the default 'media'
    // ...
};
HTML
<body class="bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
    <div class="bg-gray-50 dark:bg-gray-800 p-6 rounded-lg">
        <h2 class="text-gray-900 dark:text-white">Card title</h2>
        <p class="text-gray-600 dark:text-gray-400">Card body text.</p>
    </div>
</body>

With darkMode: 'class', dark styles only apply while an ancestor (usually <html>) has the class dark — toggled by a small script:

Javascript
document.documentElement.classList.toggle('dark');

With the default darkMode: 'media', the same dark: classes apply automatically based on the visitor's OS-level preference, with no toggle needed — but also no way for the user to override it independently of their OS setting.

Common mistakes

  • Using lg: when md: (or vice versa) was intended — always check a design against actual breakpoint widths rather than guessing which prefix "sounds right."
  • Forgetting dark: variants entirely on background/text color pairs, resulting in unreadable (e.g. dark text on a dark background) combinations for users with dark mode enabled.
  • Trying to use group-hover: without adding the group class to the actual ancestor — the child variant does nothing unless the correct parent carries group.
  • Assuming responsive prefixes are max-width (desktop-first) — Tailwind's default breakpoints are all min-width, so md:text-lg means "at md and wider," not "at md and narrower."