Entry 01

Content grid with breakouts

Use it as the container of every page, whether or not anything on it goes wider than the content. A centered max-width wrapper is the sign, such as .container, .wrapper or Tailwind's mx-auto max-w-*. One grid on the section replaces the section > .container wrapper pair and costs no more than it, and any child can be content width, wider, or edge to edge. Put it on main for a page of flowing content, or on each section. A band, a figure or a block that an editor later wants wider then takes one class, with no negative margin and no new wrapper.

Open
Make the frame narrower. The breakout shrinks to content width, the gutters hold, and nothing overflows.
Code CSS · 2 blocks · 20 lines

The container defines three nested widths with named grid lines:

.content-grid {
  --gutter: 1.5rem;
  --content: 64rem;
  --breakout: 80rem;

  display: grid;
  grid-template-columns:
    [full-width-start] minmax(var(--gutter), 1fr)
    [breakout-start] minmax(0, calc((var(--breakout) - var(--content)) / 2))
    [content-start] min(100% - var(--gutter) * 2, var(--content)) [content-end]
    minmax(0, calc((var(--breakout) - var(--content)) / 2)) [breakout-end]
    minmax(var(--gutter), 1fr) [full-width-end];
}

Each child picks a width with grid-column. Content is the default:

:is(.content-grid, .full-width) > * { grid-column: content; }
:is(.content-grid, .full-width) > .breakout { grid-column: breakout; }
:is(.content-grid, .full-width) > .full-width {
  grid-column: full-width;
  display: grid;
  grid-template-columns: inherit;
}
Why it works, the rules and the credits 4 reasons · 4 rules · credits

Why it works

  1. Two lines named content-start and content-end create a named area, content. That is why grid-column: content works with no line numbers.
  2. min(100% - 2 × gutter, max) shrinks the content column on small screens with no media query.
  3. minmax(0, …) lets the breakout tracks collapse, so a breakout falls back to content width when there is no room. With a fixed breakout size the page overflows on small screens.
  4. grid-template-columns: inherit gives a full-width band the same grid, so content inside it lines up with the page column without another wrapper. This is Kevin Powell's addition. In Ryan Mulligan's original the full-width child is a plain box.

Rules

  1. Every child must be placed. An unplaced child of the grid lands in the gutter track.
  2. Every direct child is a grid item. Wrap a run of inline elements in one element.
  3. Change a width by overriding --content, not by writing a new column template.
  4. In a pass over existing CSS, replace each centered max-width wrapper with the grid, and keep the wrapper's width as --content on its section.

In other systems

The column template goes wherever the project defines reusable styles. Where child selectors are not available, as in StyleX or Tailwind without a custom variant, each child sets its own grid-column to content, breakout or full-width.

Credits

Borrowed from: Kevin Powell, A new approach to container and wrapper classes, 2023-11-09 · Ryan Mulligan, Layout Breakouts with CSS Grid · Stephanie Eckles, SmolCSS breakout grid

Docs: MDN: named grid lines · MDN: min()