Responsive Web Design Patterns: Practical CSS Layout Examples

Responsive design is easier to maintain when the layout reacts to its content instead of being tuned for a list of device models. A few reusable CSS patterns can make a page work from a narrow phone viewport to a wide desktop without maintaining separate versions.

This guide builds those patterns with ordinary HTML and CSS: a readable page frame, an auto-fitting card grid, flexible navigation, component-level container queries, and a table that remains usable when it needs horizontal scrolling. The examples use no CSS framework. For background on the approach, see our guide to why responsive design matters and the responsive CSS frameworks guide.

Start with a flexible page frame

Include a viewport declaration so the browser lays out the page at the device's CSS-pixel width. Keep zoom available; do not add user-scalable=no or a restrictive maximum-scale.

<meta name="viewport" content="width=device-width, initial-scale=1">

<main class="page">
  <h1>Field notes</h1>
  <p class="intro">Short updates from the people building the product.</p>

  <section class="stories" aria-label="Recent field notes">
    <article class="story">
      <h2>Choosing a first customer interview</h2>
      <p>Start with one question you need to answer.</p>
    </article>
    <article class="story">
      <h2>Making a release easier to review</h2>
      <p>Keep the change small and describe how to verify it.</p>
    </article>
  </section>
</main>

Give the page a fluid width, but cap the line length so paragraphs do not stretch across a large monitor:

*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: system-ui, sans-serif;
}

.page {
  inline-size: min(100% - 2rem, 72rem);
  margin-inline: auto;
  padding-block: clamp(1.5rem, 4vw, 4rem);
}

.intro {
  max-inline-size: 65ch;
  font-size: clamp(1rem, 0.95rem + 0.45vw, 1.25rem);
  line-height: 1.6;
}

The min() expression keeps a one-rem gutter on each side while preventing the page from growing beyond its maximum. clamp() lets vertical spacing and type grow within explicit limits instead of scaling without bounds. Keep body text readable at the lower end of the range, and check it at 200% zoom as well as at the default size. MDN documents responsive design fundamentals and the CSS clamp() function.

Let a card grid create its own columns

Use CSS Grid when cards should move to another row as space runs out. auto-fit and minmax() let the available width, rather than a device-specific breakpoint, determine how many columns fit.

.stories {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
  gap: clamp(1rem, 2.5vw, 1.5rem);
}

.story {
  min-inline-size: 0;
  padding: 1.25rem;
  border: 1px solid #b8c2cc;
  border-radius: 0.5rem;
}

.story h2 {
  margin-block-start: 0;
  overflow-wrap: anywhere;
}

The inner min(100%, 17rem) prevents a card's minimum width from forcing overflow when its container is narrower than 17rem. min-inline-size: 0 also lets grid items shrink instead of allowing long content to widen the whole page. The MDN Grid guide explains tracks and gaps; its auto-placement guide covers how auto-fit places items.

Use a content-based minimum: if the cards become cramped, lower the minimum or let them stack. Do not add a breakpoint just because a particular phone has a certain width. Add one when the content no longer has enough room to remain clear.

Allow navigation to wrap before hiding links

A row of links can wrap naturally with Flexbox. This is often a simpler first step than replacing navigation with a menu button that needs JavaScript and keyboard behavior.

<nav aria-label="Main navigation">
  <ul class="nav-links">
    <li><a href="/responsive-css-frameworks/">CSS guides</a></li>
    <li><a href="/responsive-table/">Tables</a></li>
    <li><a href="/a-developers-guide-to-testing-website-accessibility/">Accessibility</a></li>
  </ul>
</nav>
.nav-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.25rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.nav-links a {
  display: inline-block;
  padding-block: 0.5rem;
}

.nav-links a:focus-visible {
  outline: 3px solid currentColor;
  outline-offset: 3px;
}

Wrapping keeps every link available without a separate interaction. If the navigation still needs too much room, simplify its contents or implement a disclosure control with a real button, an accurate expanded state, and keyboard support. Do not hide links visually while leaving them in the tab order, or make a CSS-only hamburger that cannot be opened by keyboard.

Use container queries for reusable components

A component can appear in a wide main column, a narrow sidebar, or a dialog. A viewport media query cannot tell those contexts apart. A container query lets a component respond to the width of its own parent.

<section class="story-list" aria-label="Product updates">
  <article class="story-row">
    <div class="story-mark" aria-hidden="true">01</div>
    <div>
      <h2>Ship a smaller first release</h2>
      <p>Validate one important workflow before expanding the scope.</p>
    </div>
  </article>
</section>
.story-list {
  container-type: inline-size;
}

.story-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.75rem;
  padding-block: 1rem;
  border-block-end: 1px solid #b8c2cc;
}

.story-mark {
  font-weight: 700;
}

@container (min-width: 36rem) {
  .story-row {
    grid-template-columns: 3rem minmax(0, 1fr);
    gap: 1.25rem;
  }
}

The single-column style is the baseline. In a wide container, the query adds a marker column. If a browser does not support container queries, the content remains in that simpler layout. Check the target browser versions in the MDN container-query guide before depending on this enhancement.

Keep a wide data table readable

Some data is easier to compare in a table than in stacked cards. Instead of squeezing every column or removing information on small screens, allow the table to scroll inside its own named region.

<div class="table-scroll" role="region" aria-labelledby="plan-caption" tabindex="0">
  <table>
    <caption id="plan-caption">Example plan limits</caption>
    <thead>
      <tr>
        <th scope="col">Plan</th>
        <th scope="col">Monthly requests</th>
        <th scope="col">Team access</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th scope="row">Starter</th>
        <td>1,000</td>
        <td>No</td>
      </tr>
      <tr>
        <th scope="row">Team</th>
        <td>10,000</td>
        <td>Yes</td>
      </tr>
    </tbody>
  </table>
</div>
.table-scroll {
  max-inline-size: 100%;
  overflow: auto;
}

.table-scroll table {
  inline-size: 100%;
  min-inline-size: 36rem;
  border-collapse: collapse;
}

.table-scroll th,
.table-scroll td {
  padding: 0.75rem;
  border: 1px solid #b8c2cc;
  text-align: start;
}

The caption and header scopes make relationships clearer to assistive technology. The named, focusable region gives keyboard users a way to reach and scroll the overflow area. Use this pattern only when the information is genuinely tabular; for a list of independent features, a responsive card layout may be easier to understand. Our older responsive table example shows another treatment, but its fixed styling should be adapted to your own contrast and focus requirements.

Size images without stretching them

For images that should shrink with their content column, set a maximum inline size and preserve their intrinsic aspect ratio:

img,
video {
  max-inline-size: 100%;
  block-size: auto;
}

For a page that serves different image sizes, use HTML srcset and sizes so the browser can choose an appropriate source. MDN's responsive images guide explains the selection rules. Add meaningful alternative text when an image communicates information; use empty alternative text for a purely decorative image.

Add media queries when the content needs them

Fluid sizing and auto-fitting layouts cover many pages without a long list of breakpoints. Use a media query for a genuine change in composition, such as moving a sidebar beside the main content once both columns have enough room:

.article-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 2rem;
}

@media (min-width: 52rem) {
  .article-layout {
    grid-template-columns: minmax(0, 2fr) minmax(14rem, 1fr);
  }
}

The minmax(0, 1fr) track prevents long content from forcing the grid wider than its container. The 52rem value is a starting point for this example, not a universal tablet breakpoint. Resize the page and adjust it where the content becomes crowded. MDN's media-query guide covers viewport and feature queries.

Check reflow, zoom, and keyboard use

Before shipping a responsive page:

  1. Resize the viewport gradually and look for the point where each layout starts to feel cramped.
  2. Test a narrow viewport and 200% text zoom; ensure content is not clipped or hidden.
  3. Navigate links, controls, and any scrollable table with a keyboard, and keep focus visible.
  4. Check that headings, form controls, and table headers remain meaningful when the layout changes.
  5. Test the oldest browser versions your project actually supports. Use progressive enhancement when newer CSS features are optional.

WCAG's Reflow criterion explains the expectation to present ordinary content without two-dimensional scrolling at a narrow equivalent viewport, with exceptions for content that inherently needs two dimensions. Its Resize Text criterion covers enlarging text. These checks are not a substitute for testing with actual users and assistive technologies; our developer's guide to accessibility testing goes deeper.

A practical way to choose patterns

Start with a fluid page frame and a layout that follows the content. Use Grid for two-dimensional card layouts, Flexbox for a row of controls or links, container queries when a component is reused at different widths, and a media query when the page composition genuinely needs to change. Keep wide data inside an intentional scroll region rather than shrinking it until it is unreadable.

These patterns are building blocks, not a promise that every page is accessible or compatible automatically. Keep important content visible, preserve zoom, test keyboard behavior, and check the browser support that matters to your readers.

Sources

Leave a Reply