frontend · level 2

CSS Layout

Flexbox vs grid — when each one wins.

130 XP

CSS Layout

Two layout systems dominate modern CSS. They solve related but distinct problems. Choosing the wrong one doesn't break anything — it just makes the CSS harder to read and maintain.

Analogy

Imagine arranging furniture. Flexbox is like lining chairs along a hallway: one axis, space them evenly, let an extra chair bump to the next row when the wall ends. Grid is like seating guests in a wedding reception hall — you draw a plan with numbered tables, rows and columns, and every guest has a specific cell. You'd never sketch a seating chart for three chairs in an entryway, and you'd never try to place 200 guests in a conference hall by just pushing them down a hallway.

Flexbox: one dimension

Flexbox distributes items along a single axis — either a row or a column. Use it when the content controls the size and you want the container to wrap or stretch around it.

.nav {
  display: flex;
  gap: 1rem;           /* space between items */
  align-items: center; /* cross-axis alignment */
}

Good fits: navigation bars, button groups, centring a single element, card rows that wrap.

Grid: two dimensions

Grid lets you define explicit rows and columns. The container controls the layout; items slot into cells. Use it when you're thinking in terms of a template.

.gallery {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
}

Good fits: page layouts, card grids, dashboard panels, anything with both row and column alignment.

When each wins

Scenario Winner Why
Navigation bar Flex Single row, items flow naturally
Card grid Grid Columns and rows both matter
Centring content Flex justify-content + align-items in 2 lines
Magazine layout Grid Explicit placement in named areas
Inline tags / chips Flex + wrap Items spill to next line automatically
Side-by-side panels Either Grid if sizes are fixed; Flex if content-driven

The choice is not religious. A page typically uses both: Grid for the outer skeleton, Flex for components inside cells.

Logical properties

Avoid left, right, top, bottom in layout code. Use logical properties instead:

Physical Logical
margin-left margin-inline-start
padding-top padding-block-start
border-right border-inline-end
width inline-size
height block-size

Logical properties adapt automatically when the document direction changes (RTL languages, vertical writing modes). The physical properties don't.

Container queries

Media queries respond to the viewport. Container queries respond to the parent element. This makes components truly reusable — a card component can switch from a row to a column layout based on the space it's given, not the screen size.

.card-wrapper {
  container-type: inline-size;
}

@container (min-width: 400px) {
  .card {
    display: grid;
    grid-template-columns: 120px 1fr;
  }
}

Cascade layers

@layer controls specificity without specificity wars. Earlier layers lose to later ones, regardless of selector weight:

@layer base, components, overrides;

@layer base {
  h1 { font-size: 2rem; }
}

@layer overrides {
  h1 { font-size: 1.5rem; } /* wins, even with the same selector weight */
}

The modern reset

*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; }
img, video { max-inline-size: 100%; block-size: auto; }

box-sizing: border-box makes width include padding and border — the model developers expect. Without it, every box calculation needs a mental adjustment.