Overview

Tailwind’s responsive variants are mobile-first: sm: means “at 40rem and above,” not “only on small screens.” Build the mobile layout as the base, then layer larger breakpoints on top. The parent reference is tailwind; for the container query mechanism see css-container-queries.

Build mobile, layer up

Every utility without a breakpoint prefix is the mobile style. Breakpoint variants add rules at larger sizes. The pattern is additive, not overriding.

<div class="flex flex-col gap-4 md:flex-row md:gap-8">
  <aside class="w-full md:w-64">Sidebar</aside>
  <main class="w-full md:flex-1">Content</main>
</div>

The mobile layout stacks vertically with a small gap. At md (48rem and above), it becomes a row with a wider gap and a fixed sidebar. No reset is needed because flex-col is the baseline and md:flex-row adds to it.

Know the five breakpoints

Tailwind v4 ships these defaults:

VariantMin-width
sm:40rem (640px)
md:48rem (768px)
lg:64rem (1024px)
xl:80rem (1280px)
2xl:96rem (1536px)

Override or extend them in @theme:

@theme {
  --breakpoint-sm: 36rem;
  --breakpoint-3xl: 112rem;
}

Stick to the defaults unless the design system specifies different values. Inventing extra breakpoints multiplies the class variants each component must handle.

Prefer container queries over viewport queries for components

Viewport breakpoints work for page shells. Components placed in variable-width slots, like a card in a sidebar versus a card in a three-column grid, should use container queries.

<div class="@container">
  <div class="flex flex-col @md:flex-row gap-4">
    <img class="w-full @md:w-32 rounded-md" />
    <div>Details</div>
  </div>
</div>

The @md: prefix comes from @tailwindcss/container-queries. The component adapts to its container width, not the viewport. The same markup renders correctly in a 240px sidebar and an 800px main column. For the underlying CSS, see css-container-queries.

Reserve viewport breakpoints for page-level layout

Viewport breakpoints are the right choice for page-level layout decisions that depend on the screen: hiding a sidebar below a certain width, switching from a single to a multi-column page shell, adjusting the main content max-width.

<body class="grid grid-cols-1 lg:grid-cols-[240px_1fr]">
  <nav class="hidden lg:block">Navigation</nav>
  <main class="px-4 lg:px-8">Content</main>
</body>

The sidebar column only makes sense at lg and above because it needs enough screen real estate to coexist with the main content. This is a viewport concern, not a component concern.

Use max-*: variants for top-down overrides

v4 adds max-sm:, max-md:, max-lg:, max-xl:, and max-2xl: as “at or below” breakpoint variants. Use these sparingly; they fight the mobile-first flow.

<div class="p-8 max-sm:p-4">

The rule reads: 32px padding normally, but 16px when below sm. This is fine for small adjustments. For major layout changes, restructure to be mobile-first instead of adding max-width overrides.

Avoid breakpoint proliferation on a single element

An element with text-sm md:text-base lg:text-lg xl:text-xl is a signal that the type scale needs a fluid value instead.

<!-- Before: four breakpoint variants -->
<h1 class="text-2xl md:text-3xl lg:text-4xl xl:text-5xl">Title</h1>
 
<!-- After: fluid type with clamp, one class -->
<h1 class="text-fluid-heading">Title</h1>
@theme {
  --text-fluid-heading: clamp(1.5rem, 1rem + 3vw, 3rem);
}

Fluid type via clamp() removes the need for step-wise breakpoints on typography. Add a token for each fluid scale step and use it everywhere.

Test on real container sizes, not just breakpoints

Most bugs in responsive layouts appear at widths between breakpoints or in components placed in unexpected container widths. Test at intermediate widths by dragging the browser window, not only at the exact breakpoint values.

For container queries, inspect the container’s computed width in DevTools to confirm it matches your expectation. A container inside a CSS grid track may be narrower than the viewport even on a wide screen.