Yester­year-grids

An overview of pre-Flexbox and pre-CSS Grid layout techniques in CSS with modern CSS syntax.

Padding-based float grid with gutters via padding

This float-based grid uses inline-padding to create gutters between columns. Each column is floated tot the left and positioned immediately after the previous column. In some cases, the last column wraps to the next line due to rounding errors.

Because the background color also extends into the padding-area, background-clip:content-box is required to limit the background color rendering the content-box area. To add inner spacing without making content touch the edges of the column, each column uses a nested child element that applies its own padding.

Each column has padding equal to half the gutter width to create the gutters. The parent row compensates for this by using negative inline-margins, that are equal to half the gutter width.

Column
Column
Column
Column
Column
Column
.row {
  --number-of-columns: 12;
  --gutter-size: 1rem;
  --size-of-column: calc(100% / var(--number-of-columns));

  display: flow-root;
  margin-inline: calc(var(--gutter-size) / -2);
}

.column {
  inline-size: var(--size-of-column);
  padding-inline: calc(var(--gutter-size) / 2);
  float: inline-start;
  background-clip: content-box;
}

Margin-based float grid with gutters (single direction margins)

This float-based grid uses inline-margins to create gutters between columns. Each column is floated to the left and positioned immediately after the previous column. In some cases, the last column wraps to the next line due to rounding errors.

Each column except the first one, has an inline-start margin equal to the gutter width. This creates consistent spacing between columns but also keeps the first column flush with the container edge.

Column
Column
Column
Column
Column
Column
.row {
  --number-of-columns: 12;
  --gutter-size: 1rem;
  --gutter-size-total: calc((var(--number-of-columns) - 1) * var(--gutter-size));
  --size-of-column: calc((100% - var(--gutter-size-total)) / var(--number-of-columns));

  display: flow-root;
}

.column {
  inline-size: var(--size-of-column);
  margin-inline-start: var(--gutter-size);
  float: inline-start;

  &:first-child {
    margin-inline-start: 0;
  }
}

Isolated floats

This float-based grid uses inline-margins to position components relative to the container. Each column is positioned independently from other columns to avoid rounding errors that could cause wrapping.

Each column is floated to the left, taking it out of normal flow. A margin-inline-end value of -100% pulls each column back to the left edge of the container and overrides the default float behaviour where each float follows the previous one. The actual positon of a column is then set via margin-inline-start.

Column
Column
Column
Column
Column
Column
.row {
  --number-of-columns: 12;
  --gutter-size: 1rem;
  --gutter-size-total: calc((var(--number-of-columns) - 1) * var(--gutter-size));
  --size-of-column: calc((100% - var(--gutter-size-total)) / var(--number-of-columns));

  display: flow-root;
}

.column {
  inline-size: var(--size-of-column);
  margin-inline-start: calc((var(--size-of-column) + var(--gutter-size)) * (sibling-index() - 1));
  margin-inline-end: -100%; /* move every column to the left edge of container */
  float: inline-start;
}

Inline block grids

This inline-block-based grid uses inline margins to create gutters between columns. There are multiple approaches to remove the whitespace between inline-block columns. One approach is to set letter-spacing: -1em on the parent row and reset it to normal in each column. Another is to set font-size: 0 on the parent row and reset it in each column. A third option is to strip the whitespace from the markup using HTML comments. Finally, setting the parent row to display: table causes some browsers to ignore whitespace entirely.

Each column except the first has an inline-start margin equal to the gutter width. This creates consistent spacing between columns while keeping the first column flush with the container edge.

Column
Column
Column
Column
Column
Column
.row {
  --number-of-columns: 12;
  --gutter-size: 1rem;
  --gutter-size-total: calc((var(--number-of-columns) - 1) * var(--gutter-size));
  --size-of-column: calc((100% - var(--gutter-size-total)) / var(--number-of-columns));

  display: table;
  table-layout: fixed;
  inline-size: 100%;
  letter-spacing: -1em;
}

.column {
  display: inline-block;
  inline-size: var(--size-of-column);
  margin-inline-start: var(--gutter-size);
  letter-spacing: normal;

  &:first-child {
    margin-inline-start: 0;
  }
}

Justified inline block grid

This inline-block-based grid uses text-align: justify to create gutters between columns. To remove the whitespace between inline-block columns, the parent row is set to font-size: 0 and reset in each column.

text-align: justify distributes inline content evenly across the full width of the row, creating equal gutters between columns. Since the last line of a justified block is never justified, a spacer element is appended to the row so it wraps to the next line, forcing the columns into a fully justified row. The block-size of the spacer must be compensated for by applying a negative margin on the columns.

Column
Column
Column
Column
Column
Column
.row {
  --number-of-columns: 12;
  --gutter-size: 1rem;
  --gutter-size-total: calc((var(--number-of-columns) - 1) * var(--gutter-size));
  --size-of-column: calc((100% - var(--gutter-size-total)) / var(--number-of-columns));

  text-align: justify;
  line-height: 0;
}

.column {
  display: inline-block;
  inline-size: var(--size-of-column);
  margin-block-end: -7px; /* compensate for spacer element */
  line-height: normal;
  text-align: start;
}

/* neccessary as "&::after" on row container doesn't work; it needs to be a real element */
.spacer {
  display: inline-block;
  inline-size: 100%;
  block-size: 0;
}

Justified inline block grid with text-align-last

This inline-block-based grid uses text-align: justify and text-align-last: justify to create gutters between columns. The latter avoids having to use a spacer element to make the former work as text-align: justify isn't applied to the last line of a justified block.

Column
Column
Column
Column
Column
Column
.row {
  --number-of-columns: 12;
  --gutter-size: 1rem;
  --gutter-size-total: calc((var(--number-of-columns) - 1) * var(--gutter-size));
  --size-of-column: calc((100% - var(--gutter-size-total)) / var(--number-of-columns));

  text-align: justify;
  text-align-last: justify;
}

.column {
  display: inline-block;
  inline-size: var(--size-of-column);
  text-align: center;
  text-align-last: center;
}

Border spacing grid

This table-based grid uses border-spacing to create gutters between columns when border-collapse: separate is set. border-spacing also creates gutters between the first and last cell and the edge of the table. The gutters at the start and end must be compensated for via negative margins. Negative margin-inline on a table element are ignored in some browsers, so an additional wrapper is needed to apply the negative inline margins.

Column
Column
Column
Column
Column
Column
/* required as negative margin-inline-end is ignored on table elements */
.wrapper {
  --gutter-size: 1rem;

  margin-inline: calc(var(--gutter-size) * -1);
}

.row {
  display: table;
  border-collapse: separate;
  table-layout: fixed;
  inline-size: 100%;
  border-spacing: var(--gutter-size) 0; /* order of values is reversed: horizontal vertical */
}

.column {
  display: table-cell;
}