Frontend
Lesson 4 of 8About 3 min readSuggest an edit

CSS layout with flexbox and grid

Most layout bugs come from not knowing how the browser decides a box’s size. Flexbox and grid both distribute space, but they start from different questions.

One dimension or two

Flexbox lays items out along a single axis. Content comes first: each item has a natural size, and flexbox shares out the leftover space (or takes it back) using flex-grow and flex-shrink. It suits toolbars, button rows and anything where the number of items varies.

Grid lays items out in rows and columns at the same time. Layout comes first: you define tracks, then place content into them. It suits page shells, card galleries and forms with aligned columns.

A useful test: if you find yourself setting fixed widths on flex items so that rows line up, you wanted grid.

The box model and sizing

Set box-sizing: border-box globally so width includes padding and border. Without it, a width: 100% box with padding overflows its parent.

Sizes are either extrinsic, set from outside (width: 20rem, 50%), or intrinsic, derived from the content:

  • min-content is the narrowest the box can get without overflowing, usually the longest word or widest image;
  • max-content is the width the content wants with no wrapping;
  • fit-content behaves like max-content but never grows beyond the available space.

Grid adds the fr unit, a share of the free space left after fixed and intrinsic tracks are sized, and minmax(min, max), which gives a track a floor and a ceiling.

.page {
  display: grid;
  grid-template-columns: minmax(12rem, 16rem) minmax(0, 1fr);
  gap: 1.5rem;
}

Responsive without breakpoints

Many layouts can adapt to their container without a single media query. This gallery fits as many columns of at least 16rem as the space allows, and stretches them to fill the row:

.cards {
  display: grid;
  grid-template-columns:
    repeat(auto-fill, minmax(min(16rem, 100%), 1fr));
  gap: 1rem;
}

The inner min(16rem, 100%) stops a single column overflowing on very narrow screens. Use auto-fit instead of auto-fill if you want a few items to stretch across the whole row rather than leave empty tracks at the end.

For type and spacing, clamp(min, preferred, max) scales smoothly between two limits:

h1 { font-size: clamp(1.75rem, 1.2rem + 2.5vw, 3rem); }

Keep a rem component in the preferred value so text still responds to the user’s font size setting. When a component needs to change shape depending on where it is placed, not on the viewport, use container queries rather than media queries.

Common layout bugs

The min-width blowout. Flex and grid items have min-width: auto by default, which in practice means they refuse to shrink below their min-content width. A long URL, a wide table or a <pre> block then pushes the item, and the whole page, wider than the screen. The fix is to let the item shrink:

.main { min-width: 0; }  /* flex item */

In grid, 1fr means minmax(auto, 1fr), so the same thing happens. Write minmax(0, 1fr) for tracks that hold unpredictable content.

Overflow that hides the real problem. Adding overflow: hidden to the body silences horizontal scrolling but also clips focus rings and sticky elements. Find the element that is too wide instead: in DevTools, temporarily add * { outline: 1px solid red } and scroll sideways.

Long words. User-generated text needs overflow-wrap: anywhere (or break-word) so a long string wraps instead of overflowing.

Truncation that does nothing. text-overflow: ellipsis only works together with overflow: hidden and white-space: nowrap, and inside a flex item it also needs min-width: 0.

How to decide

  • Content-sized items in a row or column: flexbox.
  • Anything that must align in two directions: grid.
  • Let content define sizes where you can; set fixed sizes only where the design needs them.
  • Use minmax(0, 1fr) and min-width: 0 wherever content is unpredictable.
  • Try auto-fill, minmax() and clamp() before adding a breakpoint.
  • Test with long names, long URLs and a 320px wide viewport.

Next: Web performance and Core Web Vitals

What LCP, INP and CLS measure, how to collect them, and how to improve each one.