← All posts

Responsive design wire frame using media queries

· CSS, Responsive

First appeared on mobifreaks.com · Updated

In February 2012 we started redesigning Mobifreaks, and we wanted it responsive. Since nothing at Mobifreaks happened without a freebie, I built a responsive wireframe with media queries and shared it.

What media queries are

Media queries are conditions that apply CSS based on the media type, screen width, height, orientation, resolution and more. We already used media types to load a separate print stylesheet:

<link rel="stylesheet" href="screen.css" media="screen">
<link rel="stylesheet" href="print.css" media="print">

That costs two HTTP requests. With @media blocks, both can live in one file:

@media screen { h1 { font-size: 2em; } }
@media print  { h1 { font-size: 1em; } }

The 2012 layout

A wrapper with a header, a content column, a sidebar and a footer. The desktop version used fixed pixel widths:

.wrap { width: 1200px; padding: 40px; margin: 0 auto; }
.col1 { width: 820px; float: left; }
.col2 { width: 300px; float: left; margin-left: 40px; }
.clear { clear: both; }

Then one media query per screen size, seven in total (1280, 1024, 800, 640, 480, 320 and 220 px), each resetting every width:

@media all and (max-width: 1280px) and (min-width: 1024px) {
  .wrap { width: 984px; padding: 20px; }
  .col1 { width: 624px; }
  .col2 { width: 300px; margin-left: 20px; }
}
@media all and (max-width: 800px) and (min-width: 640px) {
  .wrap { width: 600px; padding: 20px; }
  .col1, .col2 { width: 580px; float: none; margin: 0 0 20px 0; }
}
/* ...and five more like these */

It worked, and it taught me media queries. But it only looked right at seven widths; anything in between jumped to the next fixed size. The viewport tag in it also had maximum-scale=1, which I now recommend against.

The same layout today

Instead of fixed widths per device, let the layout stretch, and add one breakpoint where the design actually needs it.

<div class="wrap">
  <header>Header</header>
  <main>Content</main>
  <aside>Sidebar</aside>
  <footer>Footer</footer>
</div>
.wrap {
  display: grid;
  gap: clamp(1rem, 3vw, 2.5rem);
  max-width: 75rem;
  margin-inline: auto;
  padding: clamp(1rem, 3vw, 2.5rem);
}

@media (width >= 50rem) {
  .wrap {
    grid-template-columns: 1fr 18.75rem;
    grid-template-areas:
      "header header"
      "main   aside"
      "footer footer";
  }
  header { grid-area: header; }
  main   { grid-area: main; }
  aside  { grid-area: aside; }
  footer { grid-area: footer; }
}
Live demo. Drag the bottom-right corner to resize it. Open in a new tab
  • One breakpoint instead of seven. Below 50rem everything stacks; above it the sidebar sits beside the content. In between, the content column just grows.
  • clamp() scales gaps and padding smoothly with the screen width.
  • max-width instead of width lets the wrapper shrink on small screens without a media query.
  • No floats or clearing divs. Grid handles the columns.
  • (width >= 50rem) is the newer range syntax, supported in all current browsers.
  • No transitions on width. I used to animate the resize, which looked fun while dragging a window but nobody else ever saw it.

For components that live in different places, like a card that sits in both the main column and the sidebar, look at container queries (@container). They respond to the size of the container instead of the screen.