← All writing
Craft · · 6 min

Grid and Flexbox aren't competing

Three people asked me in two weeks. The framing everyone repeats is true, and it is not the bit that helps you decide.

CSS

Grid landed a month ago and the question I’ve now been asked three times, twice by people I work with and once by someone at a meetup who I’d only just met, is “should I be using grid or flexbox for this.”

I gave a decent answer all three times and I’ve since realized the reasoning I gave was the reasoning everybody gives, which is that flexbox is one-dimensional and grid is two-dimensional. That’s accurate. It’s also, I think, not the sentence that helps, because the very next thing anyone says is “but flexbox wraps onto multiple lines, so that’s two dimensions,” and they’re not being difficult, they have a point.

So here’s the version I’ve talked myself into.

Content out, or layout in

Flexbox starts with the things. You put items in a container, the items have sizes that come from their own contents, and the container’s job is to distribute whatever space is left over. Nothing in a flex container knows what the finished arrangement looks like. The arrangement is an outcome.

Grid starts with the space. You draw the tracks first, in the container, before anything is in them, and then items go into the cells you drew. The arrangement is an input.

That’s the difference I actually use, and it explains the wrapping thing. Flexbox with flex-wrap does produce multiple rows, but it produces them by running out of room, one line at a time, and no item on line two has any relationship with an item on line one. Grid rows are a thing you declared. Items in column two line up because column two exists, not because they happened to break there.

Flexbox starts with the things and works out the space. Grid starts with the space and works out where the things go.

Could you draw it?

If I’d draw it on paper before I knew what was going in it, it’s grid.

A page with a header, a content column, a sidebar and a footer is a drawing. It’s the same drawing whether the sidebar has two things in it or nine. Grid.

A nav bar is not a drawing. It’s however many links the client has, at whatever widths the words happen to be, pushed apart to fill the bar. If they add “Careers” next week the arrangement changes and that’s correct. Flexbox.

A row of three cards is the interesting middle case, and it depends entirely on whether “three” is a fact about the design or a fact about this week’s content. If the design says three across at desktop, that’s a drawing, that’s grid. If it’s “however many products are in this category,” that’s auto-fit and minmax, which is still grid but for the opposite reason, or it’s a wrapping flex row if you don’t mind the last line being ragged.

Which brings up the thing that decides a lot of these in practice: a wrapping flex row leaves the final line uneven, because the items on it stretch or bunch according to how many landed there. Sometimes that’s fine and honest. Often the designer meant a grid and drew four boxes.

Why not both?

The framing of the question implies you pick one per project, and in practice almost every component I’ve built this month is both.

The page is a grid. One of its cells is a card. The card is a grid of image / body / footer. The card’s footer is a flex row, because it’s a price and a button and they want to be at opposite ends and their widths come from their contents.

Nobody has to lose. The bad instinct, which I’ve caught myself at, is deciding you’re now A Grid Person and doing a two-item horizontal row as a grid with two columns you had to name. That works. It’s just more typing than display: flex and one justify-content, and you’ve written down a size that the content already knew.

Two afternoon-eaters

Both of these are the same underlying thing, which is that neither system will shrink a track or an item below the size of its contents unless you tell it it’s allowed to.

In flexbox, flex: 1 on a row of items looks like it means “share the space equally” and mostly behaves that way until one item contains a long unbroken string, a URL, a German compound noun, a <pre> block. Then that item refuses to go below its minimum content size and shoves everything else along, and the row overflows its container with no explanation.

flex-overflow.css
.row__item {
  flex: 1;         /* shorthand for 1 1 0%, which is not the problem */
  min-width: 0;    /* this is. the default is auto, which means "min-content" */
}
CSS

Grid has the identical trap wearing a different hat. 1fr is not “one fraction of the space,” it’s shorthand for minmax(auto, 1fr), and that auto floor is the same min-content floor as above. So a grid column with a wide table in it grows past its share and pushes the layout sideways.

grid-overflow.css
.page {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 20rem;   /* rather than 1fr 20rem */
}
CSS

I lost most of a Thursday to the grid version before I understood it was the flexbox one I already knew, and I’d file both under “the layout algorithm is protecting your content and you have to say you don’t want that.”

Blame the bloggers

Us, I think, and the way this stuff gets written about, because a comparison is easier to publish than a description. Two specs, both about arranging boxes, both new-ish to most people, so obviously one must be replacing the other. That’s the shape of every tooling post I’ve ever written, including mine, twice.

They were designed alongside each other by an overlapping group of people to solve adjacent problems, and the alignment properties are shared deliberately so that what you learn in one carries over. If you want a rule small enough to keep in your head: rows and things whose size is their own business, flexbox. Anything you’d sketch, grid.

And if you get it wrong it’s about four lines to swap, which is the least stressful thing about any of this.

Read similar posts
6 min

Style queries

Five surface variants, four child elements, and 20 selectors that existed only to tell a child which parent it was in. Style queries delete the whole grid of them. That isn't quite the same as saying you needed them.

6 min

currentColor

I opened a button component with 11 color declarations in it, across four variants, and the correct number was one per variant, because six of those properties already default to the thing they were being set to.