Skip to main content
Technology & EngineeringTailwind231 lines

Tailwind Responsive Design

Responsive breakpoints, mobile-first design patterns, and adaptive layouts with Tailwind CSS

Quick Summary24 lines
You are an expert in building responsive, mobile-first layouts with Tailwind CSS.

## Key Points

- **Design mobile-first.** Write base styles for the smallest screen, then add breakpoint prefixes for larger sizes. This produces smaller CSS and forces you to prioritize content.
- **Use `max-w-*` and `mx-auto` for content width.** Instead of different padding at every breakpoint, constrain the content area: `<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">`.
- **Test at breakpoint boundaries.** Resize your browser to values just below and above each breakpoint (e.g., 639px and 641px) to catch layout jumps.
- **Use `min-w-0` on flex children** to prevent content from overflowing flex containers, especially with text truncation.
- **Prefer `gap` over margins** in flex and grid layouts for cleaner responsive spacing.
- **Avoid too many breakpoint layers.** If you need different values at every single breakpoint, the design may be over-specified. Two or three breakpoints usually suffice.
- **Thinking breakpoints are max-width.** `md:flex` means "flex at 768px and above," not "flex only at medium screens." This is the most common misunderstanding.
- **Forgetting mobile styles.** Writing `md:flex` without a base `hidden` or `block` means the element is visible at all sizes, not just medium+.
- **Using `w-screen` for full-width sections.** This causes horizontal scroll when a vertical scrollbar is present. Use `w-full` or negative margin techniques instead.
- **Not accounting for touch targets on mobile.** Buttons and links need at least 44x44px tap targets. Use `min-h-[44px] min-w-[44px]` or generous padding.
- **Hardcoding pixel widths with arbitrary values.** Prefer the spacing scale and relative units. Arbitrary values like `w-[347px]` break at different screen sizes.

## Quick Example

```html
<!-- text-sm on mobile, text-base from sm up, text-lg from lg up -->
<p class="text-sm sm:text-base lg:text-lg">Responsive text</p>
```
skilldb get tailwind-skills/Tailwind Responsive DesignFull skill: 231 lines
Paste into your CLAUDE.md or agent config

Responsive Design — Tailwind CSS

You are an expert in building responsive, mobile-first layouts with Tailwind CSS.

Overview

Tailwind uses a mobile-first breakpoint system. Unprefixed utilities apply at all screen sizes, while prefixed utilities (sm:, md:, lg:, xl:, 2xl:) apply at that breakpoint and above. This means you design for mobile first, then layer on changes for larger screens.

Core Philosophy

Mobile-first is not just a technical detail of how Tailwind's breakpoints work — it is a design methodology. By writing base styles for the smallest screen and layering complexity at larger breakpoints, you are forced to prioritize content and simplify interactions. A layout that works well on a 320px screen and progressively enhances for larger viewports is fundamentally more robust than one designed for desktop and squeezed down.

Tailwind's breakpoint system uses min-width media queries exclusively, which means every unprefixed class applies at all sizes and prefixed classes apply from that breakpoint upward. This cascading behavior is the single most misunderstood aspect of Tailwind. When you write md:flex, you are not saying "flex only at medium screens" — you are saying "flex at 768px and every size above it." Once you internalize this, responsive design with Tailwind becomes natural rather than adversarial.

Resist the urge to define different values at every breakpoint. If you need text-sm, sm:text-base, md:text-lg, lg:text-xl, and xl:text-2xl, the design is probably over-specified. Two or three breakpoint layers should handle most responsive needs. Simpler responsive rules are easier to maintain, produce smaller markup, and leave fewer edge cases at in-between viewport widths.

Core Concepts

Default Breakpoints

PrefixMin-widthTypical target
(none)0pxMobile phones
sm:640pxLarge phones / small tablets
md:768pxTablets
lg:1024pxLaptops
xl:1280pxDesktops
2xl:1536pxLarge desktops

Mobile-First Means "Min-Width Up"

<!-- This reads as: -->
<!-- Base (mobile): single column -->
<!-- sm (640px+): two columns -->
<!-- lg (1024px+): three columns -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
  <div>Card</div>
  <div>Card</div>
  <div>Card</div>
</div>

Every class without a prefix is the mobile style. You progressively add larger breakpoint overrides.

How Breakpoints Cascade

Because breakpoints use min-width, a sm: style also applies at md:, lg:, etc. unless overridden:

<!-- text-sm on mobile, text-base from sm up, text-lg from lg up -->
<p class="text-sm sm:text-base lg:text-lg">Responsive text</p>

Implementation Patterns

Responsive Navigation

<nav class="flex items-center justify-between px-4 py-3">
  <a href="/" class="text-xl font-bold">Logo</a>

  <!-- Mobile hamburger button, hidden on md+ -->
  <button class="md:hidden" aria-label="Toggle menu">
    <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
            d="M4 6h16M4 12h16M4 18h16" />
    </svg>
  </button>

  <!-- Desktop links, hidden on mobile -->
  <div class="hidden md:flex md:items-center md:gap-6">
    <a href="/features" class="text-gray-700 hover:text-gray-900">Features</a>
    <a href="/pricing" class="text-gray-700 hover:text-gray-900">Pricing</a>
    <a href="/docs" class="text-gray-700 hover:text-gray-900">Docs</a>
  </div>
</nav>

Responsive Grid Layouts

<!-- Product grid: 1 col mobile, 2 col tablet, 4 col desktop -->
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4">
  <div class="rounded-lg border p-4">Product</div>
  <div class="rounded-lg border p-4">Product</div>
  <div class="rounded-lg border p-4">Product</div>
  <div class="rounded-lg border p-4">Product</div>
</div>

<!-- Sidebar layout: stacked on mobile, sidebar on desktop -->
<div class="flex flex-col lg:flex-row lg:gap-8">
  <aside class="lg:w-64 lg:shrink-0">
    <nav>Sidebar navigation</nav>
  </aside>
  <main class="min-w-0 flex-1">
    Main content
  </main>
</div>

Responsive Spacing and Padding

<!-- Tighter on mobile, more breathing room on desktop -->
<section class="px-4 py-8 sm:px-6 lg:px-8 lg:py-16">
  <div class="mx-auto max-w-7xl">
    <h2 class="text-2xl font-bold sm:text-3xl lg:text-4xl">Section Title</h2>
    <div class="mt-4 sm:mt-6 lg:mt-8">
      Content with responsive top margin
    </div>
  </div>
</section>

Responsive Show/Hide

<!-- Visible only on mobile -->
<div class="block sm:hidden">Mobile only</div>

<!-- Visible only on tablet and up -->
<div class="hidden sm:block">Tablet and desktop</div>

<!-- Visible only on desktop -->
<div class="hidden lg:block">Desktop only</div>

<!-- Visible on mobile and desktop, hidden on tablet -->
<div class="block sm:hidden lg:block">Mobile and desktop</div>

Responsive Typography

<h1 class="text-3xl font-bold leading-tight sm:text-4xl md:text-5xl lg:text-6xl">
  Big Hero Headline
</h1>
<p class="mt-4 text-base text-gray-600 sm:text-lg lg:text-xl lg:leading-relaxed">
  Supporting paragraph that scales with the viewport.
</p>

Container Queries (Tailwind v3.3+)

Container queries respond to a parent's size rather than the viewport:

<div class="@container">
  <div class="flex flex-col @md:flex-row @md:items-center gap-4">
    <img class="w-full @md:w-48 rounded-lg" src="photo.jpg" alt="" />
    <div>
      <h3 class="text-lg font-semibold">Title</h3>
      <p class="text-sm text-gray-600">Description</p>
    </div>
  </div>
</div>

Enable with the @tailwindcss/container-queries plugin or built-in support in v4.

Responsive Images

<img
  class="h-48 w-full object-cover sm:h-64 lg:h-80"
  src="hero.jpg"
  alt="Hero image"
/>

<!-- Responsive aspect ratio -->
<div class="aspect-square sm:aspect-video lg:aspect-[21/9]">
  <img class="h-full w-full object-cover" src="banner.jpg" alt="" />
</div>

Custom Breakpoints

// tailwind.config.js
module.exports = {
  theme: {
    screens: {
      xs: '475px',
      sm: '640px',
      md: '768px',
      lg: '1024px',
      xl: '1280px',
      '2xl': '1536px',
      // Max-width breakpoint (applies below this width)
      'max-md': { max: '767px' },
      // Range breakpoint
      'tablet': { min: '768px', max: '1023px' },
    },
  },
}

Best Practices

  • Design mobile-first. Write base styles for the smallest screen, then add breakpoint prefixes for larger sizes. This produces smaller CSS and forces you to prioritize content.
  • Use max-w-* and mx-auto for content width. Instead of different padding at every breakpoint, constrain the content area: <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">.
  • Test at breakpoint boundaries. Resize your browser to values just below and above each breakpoint (e.g., 639px and 641px) to catch layout jumps.
  • Use min-w-0 on flex children to prevent content from overflowing flex containers, especially with text truncation.
  • Prefer gap over margins in flex and grid layouts for cleaner responsive spacing.
  • Avoid too many breakpoint layers. If you need different values at every single breakpoint, the design may be over-specified. Two or three breakpoints usually suffice.

Common Pitfalls

  • Thinking breakpoints are max-width. md:flex means "flex at 768px and above," not "flex only at medium screens." This is the most common misunderstanding.
  • Forgetting mobile styles. Writing md:flex without a base hidden or block means the element is visible at all sizes, not just medium+.
  • Using w-screen for full-width sections. This causes horizontal scroll when a vertical scrollbar is present. Use w-full or negative margin techniques instead.
  • Not accounting for touch targets on mobile. Buttons and links need at least 44x44px tap targets. Use min-h-[44px] min-w-[44px] or generous padding.
  • Hardcoding pixel widths with arbitrary values. Prefer the spacing scale and relative units. Arbitrary values like w-[347px] break at different screen sizes.

Anti-Patterns

  • Desktop-first thinking. Designing for large screens and then trying to cram the layout into mobile breakpoints produces fragile, overridden code. Always start with the mobile layout as the base and add complexity upward.

  • Using w-screen for full-width sections. 100vw includes the scrollbar width on desktop browsers, causing a horizontal scrollbar. Use w-full (which respects the parent container) or the negative-margin full-bleed technique instead.

  • Breakpoint overload. Specifying a different value at every breakpoint (text-xs sm:text-sm md:text-base lg:text-lg xl:text-xl 2xl:text-2xl) creates fragile, verbose markup. If the design requires this many layers, consider fluid typography with clamp() or question whether the design scale is correct.

  • Hiding desktop markup on mobile with hidden. Using display: none to hide a fully rendered desktop navigation on mobile still ships all that markup and its associated JavaScript to every device. Build truly adaptive components that render only the structure needed for the current context.

  • Ignoring touch target sizes. Buttons and links that are comfortable to click with a mouse are often too small for finger taps. Mobile designs need a minimum 44x44px touch target, enforced through adequate padding or explicit min-h-[44px] min-w-[44px].

Install this skill directly: skilldb add tailwind-skills

Get CLI access →