Skip to main content
Back to Blog
CSSResponsive DesignWeb DevelopmentFrontendMedia QueriesTailwind CSS

Responsive Design Patterns: CSS Media Queries and Modern Techniques

Master responsive web design with CSS media queries, container queries, fluid typography, and modern layout patterns for all screen sizes.

6 min read

Responsive design has evolved far beyond simple breakpoints. Modern CSS gives us powerful tools for creating layouts that adapt naturally to any screen size. Here's how to build truly responsive interfaces using current best practices.

The Foundation: Mobile-First Approach

Start with mobile styles and layer on complexity for larger screens:

/* Base styles for mobile */
.card {
  padding: 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

/* Tablet and up */
@media (min-width: 768px) {
  .card {
    padding: 1.5rem;
    flex-direction: row;
    gap: 1.5rem;
  }
}

/* Desktop and up */
@media (min-width: 1024px) {
  .card {
    padding: 2rem;
    gap: 2rem;
  }
}

Standard Breakpoints

/* Common breakpoints */
:root {
  --breakpoint-sm: 640px;   /* Small devices */
  --breakpoint-md: 768px;   /* Tablets */
  --breakpoint-lg: 1024px;  /* Laptops */
  --breakpoint-xl: 1280px;  /* Desktops */
  --breakpoint-2xl: 1536px; /* Large screens */
}

/* Using with media queries */
@media (min-width: 768px) { /* md */ }
@media (min-width: 1024px) { /* lg */ }

Fluid Typography

Stop jumping between fixed font sizes. Use fluid scaling:

/* Fluid typography using clamp() */
h1 {
  /* Minimum 2rem, scales with viewport, maximum 4rem */
  font-size: clamp(2rem, 5vw + 1rem, 4rem);
}

h2 {
  font-size: clamp(1.5rem, 3vw + 0.75rem, 2.5rem);
}

p {
  font-size: clamp(1rem, 1.5vw + 0.5rem, 1.25rem);
  line-height: 1.6;
}

/* Fluid spacing to match */
.section {
  padding: clamp(2rem, 5vw, 6rem) clamp(1rem, 3vw, 3rem);
}

Modular Scale with CSS Custom Properties

:root {
  --text-base: clamp(1rem, 0.5vw + 0.875rem, 1.125rem);
  --text-scale: 1.25;

  --text-sm: calc(var(--text-base) / var(--text-scale));
  --text-lg: calc(var(--text-base) * var(--text-scale));
  --text-xl: calc(var(--text-lg) * var(--text-scale));
  --text-2xl: calc(var(--text-xl) * var(--text-scale));
  --text-3xl: calc(var(--text-2xl) * var(--text-scale));
}

Container Queries: The Game Changer

Media queries respond to viewport size. Container queries respond to parent container size:

/* Define a container */
.card-container {
  container-type: inline-size;
  container-name: card;
}

/* Style based on container width */
@container card (min-width: 400px) {
  .card {
    display: grid;
    grid-template-columns: 200px 1fr;
    gap: 1.5rem;
  }
}

@container card (min-width: 600px) {
  .card {
    grid-template-columns: 250px 1fr;
    padding: 2rem;
  }

  .card-title {
    font-size: 1.5rem;
  }
}

Practical Container Query Example

<div class="sidebar">
  <div class="card-container">
    <article class="card">
      <img src="/thumbnail.jpg" alt="Article thumbnail" />
      <div class="card-content">
        <h3 class="card-title">Article Title</h3>
        <p class="card-excerpt">Brief description...</p>
      </div>
    </article>
  </div>
</div>
.card-container {
  container-type: inline-size;
}

.card {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.card img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: 0.5rem;
}

/* When container is wide enough, go horizontal */
@container (min-width: 350px) {
  .card {
    flex-direction: row;
  }

  .card img {
    width: 120px;
    aspect-ratio: 1;
  }
}

Modern Layout Patterns

The Holy Grail with CSS Grid

.layout {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: auto 1fr auto;
  min-height: 100vh;
  grid-template-areas:
    "header"
    "main"
    "footer";
}

@media (min-width: 1024px) {
  .layout {
    grid-template-columns: 250px 1fr 200px;
    grid-template-areas:
      "header header header"
      "nav main aside"
      "footer footer footer";
  }
}

.header { grid-area: header; }
.nav { grid-area: nav; }
.main { grid-area: main; }
.aside { grid-area: aside; }
.footer { grid-area: footer; }

Auto-Fit Grid Cards

/* Cards that automatically adapt to available space */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
  gap: 1.5rem;
}

Flexbox Responsive Navigation

.nav {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: center;
}

@media (min-width: 768px) {
  .nav {
    justify-content: space-between;
  }
}

.nav-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  justify-content: center;
}

Responsive Images

Art Direction with Picture Element

<picture>
  <!-- Wide screens: landscape hero -->
  <source
    media="(min-width: 1024px)"
    srcset="/hero-wide.webp"
  />
  <!-- Tablets: square crop -->
  <source
    media="(min-width: 640px)"
    srcset="/hero-square.webp"
  />
  <!-- Mobile: portrait crop -->
  <img
    src="/hero-portrait.webp"
    alt="Hero image"
    loading="lazy"
  />
</picture>

Responsive Background Images

.hero {
  background-image: url('/hero-mobile.webp');
  background-size: cover;
  background-position: center;
}

@media (min-width: 768px) {
  .hero {
    background-image: url('/hero-tablet.webp');
  }
}

@media (min-width: 1200px) {
  .hero {
    background-image: url('/hero-desktop.webp');
  }
}

/* Or use image-set for format selection */
.hero {
  background-image: image-set(
    url('/hero.avif') type('image/avif'),
    url('/hero.webp') type('image/webp'),
    url('/hero.jpg') type('image/jpeg')
  );
}

Responsive Tables

Tables are notoriously difficult on mobile. Here's a pattern that works:

/* Card-style table on mobile */
@media (max-width: 767px) {
  table, thead, tbody, tr, th, td {
    display: block;
  }

  thead {
    position: absolute;
    left: -9999px;
  }

  tr {
    margin-bottom: 1rem;
    border: 1px solid #e5e7eb;
    border-radius: 0.5rem;
    padding: 1rem;
  }

  td {
    display: flex;
    justify-content: space-between;
    padding: 0.5rem 0;
    border-bottom: 1px solid #f3f4f6;
  }

  td::before {
    content: attr(data-label);
    font-weight: 600;
  }

  td:last-child {
    border-bottom: none;
  }
}
<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Status</th>
      <th>Amount</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td data-label="Name">John Doe</td>
      <td data-label="Status">Active</td>
      <td data-label="Amount">$250.00</td>
    </tr>
  </tbody>
</table>

Tailwind CSS Responsive Patterns

If you're using Tailwind, responsive design is built-in:

// Responsive card component
export function Card({ title, description, image }) {
  return (
    <article className="
      flex flex-col gap-4
      md:flex-row md:gap-6
      lg:gap-8
      p-4 md:p-6 lg:p-8
      rounded-lg shadow-md
    ">
      <img
        src={image}
        alt=""
        className="
          w-full aspect-video object-cover rounded
          md:w-48 md:aspect-square
          lg:w-64
        "
      />
      <div className="flex-1">
        <h3 className="
          text-lg font-semibold
          md:text-xl
          lg:text-2xl
        ">
          {title}
        </h3>
        <p className="
          mt-2 text-gray-600
          text-sm md:text-base
          line-clamp-3 md:line-clamp-none
        ">
          {description}
        </p>
      </div>
    </article>
  );
}

Container Queries in Tailwind

// With @tailwindcss/container-queries plugin
<div className="@container">
  <div className="
    flex flex-col gap-4
    @md:flex-row @md:gap-6
    @lg:gap-8
  ">
    {/* Content adapts to container, not viewport */}
  </div>
</div>

Testing Responsive Designs

DevTools Device Emulation

// Test across common device sizes
const devices = [
  { name: 'iPhone SE', width: 375, height: 667 },
  { name: 'iPhone 14', width: 390, height: 844 },
  { name: 'iPad', width: 768, height: 1024 },
  { name: 'MacBook', width: 1440, height: 900 },
];

Viewport Testing Checklist

  • Test at each breakpoint boundary (767px, 768px)
  • Test portrait and landscape orientations
  • Test with long content (text overflow, scroll)
  • Test with minimal content (empty states)
  • Verify touch targets are 44px minimum

Conclusion

Modern responsive design combines fluid values, container awareness, and smart layout patterns. Start mobile-first, use clamp() for fluid sizing, embrace container queries for component-based responsiveness, and test across real devices.

For more CSS techniques, explore my CSS Grid vs Flexbox guide and Tailwind CSS tips.