The grid as a design construct

Widely used by designers in print, web, and other media, the grid has proven itself a useful foundation for arranging content into a structured layout that can be adapted for practically any combination of content and format. Grids allow for both structured hierarchies and visually dynamic arrangements, and grounding a publication or website with the constraints of a specific grid system gives it an underlying consistency — even when the layouts within each page use the grid differently.

Responsive grid systems

Responsive grid systems are a pattern for building layouts around columns and rows, with gutters between them. As a UI pattern, grids tend to shape the main layout of a page and occasionally smaller sections within it.

A grid’s configuration needs to be provided in two layers: on the grid container and on each column within it. The container controls the size of gutters between its columns, and the columns themselves control their width as a fraction of the total grid width. The width of a column is usually expressed in terms of the number of columns the grid can be divided into. For example, a 12 column grid could be filled with three 4/12 columns, four 3/12 columns, or asymmetrically with a 4/12 column and an 8/12 column.

Using a negative grid container margins with positive column margins, we can create the gutters defensively and allow columns to wrap into multiple rows without needed extra classes for the first and last columns. This technique is useful to prevent leaking margins outside of the container.

The grid implementation below shows what’s possible with flexbox, and the same pattern can also be implemented with floats if necessary. As CSS Grid browser support reaches critical mass, we should be able to implement responsive, three-dimensional grids with greater control over complex layouts and fewer tricks needed for simple ones.

.grid {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
}
 
.grid__col {
  flex-grow: 0;
  flex-shrink: 0;
  width: 100%;
}
 
.grid--medium {
  margin: -2.4rem 0 0 -2.4rem;
}
 
.grid--medium > .grid__col {
  margin: 2.4rem 0 0 2.4rem;
}
 
@for $i from 1 through 12 {
  .grid__col--#{$i} {
    width: $i / 12 * 100%;
  }
}
 
@media (min-width: $BREAKPOINT_SMALL) {
  @for $i from 1 through 12 {
    .grid__col--#{$i}\@small {
      width: $i / 12 * 100%;
    }
  }
}
 
@media (min-width: $BREAKPOINT_MEDIUM) {
  @for $i from 1 through 12 {
    .grid__col--#{$i}\@medium {
      width: $i / 12 * 100%;
    }
  }
}
<div class="grid grid--medium">
  <div class="grid__col grid__col--6@small grid__col--4@medium">
    ...
  </div>
  <div class="grid__col grid__col--6@small grid__col--8@medium">
    ...
  </div>
</div>

Avoiding the common pitfalls of grids

  • Overuse: Although grids are a fundamental design concept, as a implementation detail in UI, using a full-featured grid implementation means bringing in a powerful tool for a design need that may be better addressed through a simpler, more pattern-oriented solution. When a design looks like a grid, consider whether a more compact UI pattern would fill the need; one example of this is the tiles pattern, which is a straightforward alternative to a grid of equal-width columns.

  • Fluid gutters: Grids are sometimes written with gutters using fluid, percentage-based widths that shrink as the viewport is reduced. This exposes a design intent mistranslation between the designer and front-end developer, as the size of grid gutters is usually meant to match the padding inside boxes and other uses across a UI. Further, because a grid may be nested inside another container, the gutters of two grids inside containers of different widths would not be equal. Because of this, I recommend implementing grids with constant-width gutters and explicitly changing the gutters to a different width based on a media query if the available space for gutters become a concern. For instance, we can use a 2rem gutter for small viewports and a 4rem gutter above a certain breakpoint.

  • Row classes: Although used by some grid systems, I recommend not defining a specific class for rows within a grid. Row classes create unnecessary complexity in the markup, as the row construct is mostly redundant to the grid container. When the need for partially-filled rows arises, use stacked grids as rows, and apply simple spacing shims between them.

  • Depth of applicability: One of the most common mistakes I see in grid systems is failing to account for nested grids. Ensure any selectors are properly scoped to direct children when relating the grid container to its columns; for example, to control a grid’s gutter size through a modifier on the grid container.