Concise Media Queries with CSS Grid

Stephen Lindberg

abstract illustration of grid layout

Media queries are commonly used to control responsive layouts on websites. Organizing layouts this way is intuitive: On a wide desktop display, we want to present information in columns, and as screen width diminishes below a threshold, we stack elements vertically. With modern CSS, solutions to this problem have become easier than in the past. No longer must we use kludgey rules like display: table; to achieve the layout of our dreams. CSS modules like flexbox and several clever frameworks have made grids easy to achieve with minimal code, but with CSS grid we can write our grid rules once, and achieve the desired layout at any screen size with a single rule, and without any framework.

As an example, let’s take a common layout for a user profile. In a profile we have a user name, avatar, and short biography. Our markup might look something like this:

<div class="user-profile">

  <span class="user-profile__username">
    Rob Ott
  </span>

  <img src="avatar.jpg" class="user-profile__avatar"/>

  <p class="user-profile__bio">
    made of metal
    likes animals
    hates water skiing
  </p>

</div>

How do we flex it?

I’ve seen media queries of all varieties trying to solve the same problem. A common method is to describe a media query which sets the rule display: flex; on a container above a certain width:

@media (min-width: 700px) {
  .user-profile {
    display: flex;
    ...
  }
}

This relies on the initial flex-direction of a flexbox being row and the initial value of flex-wrap being nowrap. When the children are block elements, they will naturally stack vertically when the display: flex; rule no longer applies. Alternatively, we could write a query to swap the value of flex-direction for row or column.

The drawback to the flexbox solution is that in order to achieve complex layout rules with blocks arranged along 2 axes, such as an element spanning 2 rows, we must:

  1. nest elements
  2. write queries to ensure margins and gutters (white space between rows and columns) remain equal,
  3. trip over the order rules of child elements to correctly organize them.

What a headache! Media queries will quickly get out of hand if we take this approach for any complex layout.

Enter grid-template-areas

CSS grid definitely has the advantage when it comes to quickly organizing layouts. Even simple layouts require minimal effort with CSS grid compared to flexbox. With the grid-template-areas property, we can write responsive layouts with a single rule inside a media query. That’s because grid-template-areas defines a visual grid system on both axes at the once. Take a look:

.grid {
  grid-template-areas:
    'avatar name'
    'bio    bio';
}

This rule tells the container that there are three areas: “name”, “avatar”, and “bio”, arranged in a pattern with the avatar and username side by side in the first row, and the bio section in a second row spanning both columns. The magic of this rule is that the number of columns is inferred by the property values. Each name separated by one or more spaces defines a column (and each row must define the same number of columns). For clarity, I broke up the rows onto separate lines to visualize the result of the grid. Our child elements simply need to tell the grid which area they appear in, and the container does the rest:

.user-profile__username {
  grid-area: name;
}

.user-profile__avatar {
  grid-area: avatar;
}

.user-profile__bio {
  grid-area: bio;
}

illustration of profile grid layout

Now a simple media query to rearrange the grid template will handle our responsive layout:

@media (max-width: 700px) {
  .grid {
    grid-template-areas:
      'name'
      'avatar'
      'bio';
  }
}

illustration of stacked layout with name on top

And now our children will stack vertically in a viewport up to 700px!

Keep accessibility in mind when reordering elements

When reordering elements visually, it’s important to ensure that the document structure is ordered logically for accessibility. Here are some thoughts on maintaining accessibility when working with grid.

Now, what if we want to show the user’s avatar first, with their name underneath? we can simply reorder the areas with the same property:

@media (max-width: 700px) {
  .grid {
    grid-template-areas:
      'avatar'
      'name'
      'bio';
  }
}

illustration of stacked layout with avatar on top

Furthermore, we can rearrange the grid areas in any arbitrary layout with that single rule, and because the CSS grid system handles gutters with a flick of the grid-gap rule, we don’t have to worry about any conditional margins on our child elements. So the next time you tackle a layout with complex responsive behavior, choose CSS grid and spend less time in front of your computer.