CSS Layout
Flexbox vs grid — when each one wins.
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.