# Flexbox and Grid

> The two CSS layout systems that replaced floats and hacks: Flexbox for rows and columns, Grid for full two-dimensional layouts, and how to pick between them.


---

# Flexbox and Grid

Before Flexbox and Grid, lining up boxes in CSS meant floats, `inline-block` hacks, and table layouts
abused for their alignment behavior. Centering something vertically was a running joke. Flexbox (2015)
and Grid (2017) fixed this properly: two purpose-built layout systems that handle alignment, spacing,
and responsiveness without a single hack.

This guide assumes you know the box model and `position` from [CSS Without Tears](/guides/css-without-tears).
It builds the layouts you'll actually use: navbars, card rows, dashboards, photo galleries, and the
classic "holy grail" page layout.

## The phases

1. **[Flexbox: One-Dimensional Layout](01-flexbox-one-dimensional-layout.md)** - `display: flex`, the
   main axis and cross axis, `justify-content`/`align-items`, wrapping, and growing/shrinking items.
   Builds a navbar and a row of equal-width cards.
2. **[When a Flex Item Won't Shrink](02-when-a-flex-item-wont-shrink.md)** - the `min-width: auto`
   floor that lets one long token blow a row apart, and why `min-width: 0` is the fix.
3. **[CSS Grid: Two-Dimensional Layout](03-css-grid-two-dimensional-layout.md)** - `display: grid`,
   defining columns and rows, `grid-template-areas`, and spanning cells. Builds a dashboard layout and
   a responsive photo gallery.
4. **[Choosing Between Them (and Combining Them)](04-choosing-between-them-and-combining-them.md)** -
   the one-dimension-vs-two-dimension rule of thumb, and a holy grail layout that uses both together.

By the end you'll reach for the right tool instead of fighting the wrong one.


---

# Flexbox: One-Dimensional Layout

Flexbox arranges children of a container along a single line - a row or a column. That's the whole
premise: one dimension at a time. It's the right tool anytime you're thinking "put these things next
to each other" or "stack these and space them out."

## Turning it on

One property starts it:

```css
.navbar {
  display: flex;
}
```

Every direct child of `.navbar` is now a **flex item**. By default they line up in a row, left to
right, each sized to its own content - no floats, no `inline-block`, no clearfix.

## Main axis vs cross axis

Flexbox thinks in two axes, and almost every property is "along the main axis" or "along the cross
axis":

- **Main axis** - the direction items flow. Row by default (left to right). Set `flex-direction:
  column` to flow top to bottom instead, which swaps which axis is "main."
- **Cross axis** - perpendicular to the main axis. Row layout → cross axis is vertical. Column layout →
  cross axis is horizontal.

```text
flex-direction: row  (default)
┌─────────────────────────────┐
│  [Item] [Item] [Item]   →   │  ← main axis (horizontal)
│    ↕ cross axis (vertical)  │
└─────────────────────────────┘
```

Once that clicks, `justify-content` and `align-items` stop being two properties to memorize and become
one idea applied twice.

## Positioning items: justify-content and align-items

- **`justify-content`** - spacing along the main axis: `flex-start`, `center`, `flex-end`,
  `space-between`, `space-around`.
- **`align-items`** - alignment along the cross axis: `flex-start`, `center`, `flex-end`, `stretch`
  (default).

```css
.navbar {
  display: flex;
  justify-content: space-between; /* push children to opposite ends */
  align-items: center;            /* vertically center them */
}
```

*What just happened:* in a row layout, `justify-content` controls left-right spacing and
`align-items` controls up-down alignment. `space-between` is what makes "logo left, links right" a
one-line fix instead of a positioning puzzle.

💡 **Key point.** "Center a div" - the CSS joke for a decade - is two lines:
```css
.center-me {
  display: flex;
  justify-content: center;
  align-items: center;
}
```

**`align-self`** overrides `align-items` for one specific item, when everyone else stays aligned one
way and a single item needs to sit differently:

```css
.navbar .help-link {
  align-self: flex-end; /* this one link sits low while everything else centers */
}
```

## Build it: a real navbar

The logo-left, links-right navbar, done properly:

```html
<nav class="navbar">
  <div class="logo">Acme</div>
  <ul class="nav-links">
    <li><a href="/">Home</a></li>
    <li><a href="/docs">Docs</a></li>
    <li><a href="/about">About</a></li>
  </ul>
</nav>
```

```css
.navbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1rem 2rem;
}

.nav-links {
  display: flex;
  gap: 1.5rem;
  list-style: none;
}
```

*What just happened:* the outer `.navbar` flex container pushes `.logo` and `.nav-links` to opposite
ends and centers them vertically. The inner `.nav-links` is *also* a flex container, laying its `<li>`s
out in a row with `gap` for spacing - no margin math, no `:last-child { margin-right: 0 }` cleanup.
Flex containers nest freely; each one only manages its own children.

## Wrapping: flex-wrap

By default, flex items shrink to fit on one line, however cramped that gets. `flex-wrap: wrap` lets
them drop to a new line instead:

```css
.card-row {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}
```

Without `flex-wrap`, five cards in a narrow viewport squeeze into an unreadable sliver. With it, cards
that don't fit flow onto the next row, like text wrapping at the edge of a paragraph.

## Sizing items: flex-grow, flex-shrink, flex-basis

Three properties control how an item's size responds to available space, almost always used through
the `flex` shorthand (`flex-grow flex-shrink flex-basis`):

- **`flex-grow`** - how much of the *leftover* space this item claims, relative to siblings. `0`
  (default) means it won't grow.
- **`flex-shrink`** - how much this item shrinks when there isn't enough space. `1` (default) means it
  shrinks proportionally.
- **`flex-basis`** - the item's starting size before growing/shrinking, like `width` but flex-aware.

## Build it: a row of equal-width cards

```html
<div class="card-row">
  <div class="card">Card 1</div>
  <div class="card">Card 2</div>
  <div class="card">Card 3</div>
</div>
```

```css
.card-row {
  display: flex;
  gap: 1rem;
}

.card {
  flex: 1;
  padding: 1.5rem;
  border: 1px solid #ddd;
  border-radius: 8px;
}
```

*What just happened:* `flex: 1` is shorthand for `flex-grow: 1; flex-shrink: 1; flex-basis: 0%`. Each
card starts at zero width, then grows to fill the row equally - three cards, three equal thirds,
recalculated automatically whether you have three cards or five. Change one card to `flex: 2` and it
claims twice the leftover space of its siblings; a common pattern for a "featured" card that's wider
than the rest.

⚠️ **Gotcha.** `flex: 1` and `width: 33%` look similar but behave differently on resize: percentage
widths are fixed fractions of the container, while `flex: 1` items renegotiate space with their
siblings every time one shrinks, grows, or wraps. For equal-width items in a flex row, `flex: 1` is the
right tool - it rebalances space instead of dividing it once and freezing.

Check your intuition with a quick quiz:

```quiz
[
  {
    "q": "In a default (row) flex container, what does justify-content control?",
    "choices": ["Vertical alignment", "Horizontal spacing along the main axis", "The stacking order of items", "The font size of items"],
    "answer": 1,
    "explain": "justify-content works along the main axis, which is horizontal in a row layout. align-items handles the cross (vertical) axis."
  },
  {
    "q": "Three items each have flex: 1 in a flex row. What happens if you change one to flex: 2?",
    "choices": ["It disappears", "It becomes exactly twice as wide in pixels, always", "It claims roughly twice the leftover space of its siblings", "Nothing, flex-grow is ignored without flex-basis set separately"],
    "answer": 2,
    "explain": "flex-grow values are compared as ratios among siblings sharing the leftover space, not absolute sizes."
  },
  {
    "q": "Your flex row of five cards is unreadable on a narrow screen because they're all squeezed onto one line. What fixes it?",
    "choices": ["flex-direction: column-reverse", "flex-wrap: wrap", "justify-content: space-around", "align-self: stretch"],
    "answer": 1,
    "explain": "flex-wrap: wrap lets items that don't fit flow onto a new line instead of shrinking indefinitely."
  }
]
```


---

# When a Flex Item Won't Shrink: the min-width Floor

Your equal-width card row from the last phase is perfect. Three cards, `flex: 1`
each, three clean thirds. You ship it.

Then real data shows up. One card holds a user's email, or an API token, or a URL
with no spaces in it - and the whole row detonates. That one card balloons out,
crushes its siblings into slivers, and a horizontal scrollbar crawls across the
page. You didn't change the layout. You changed the *text*, and `flex: 1` -
which was supposed to keep everything equal - just let one card ignore it
completely.

This is the single most common "it worked on my machine, then broke with real
content" bug in flexbox. It has one cause and one real fix.

## What's actually happening

`flex-shrink` (the middle value of `flex: 1`) says an item is *allowed* to shrink.
But every flex item also has a **floor** it won't shrink past, and by default that
floor is `min-width: auto`.

`auto` doesn't mean zero. It means: **don't shrink below your content's own
minimum width** - the width of the widest thing inside you that can't be broken
up. For text, that "widest unbreakable thing" is your longest word.

📝 **min-content.** An element's min-content width is how narrow it can get before
its content would have to be sliced mid-word. For a paragraph of normal prose,
that's just the longest word - narrow, harmless. For a 45-character API token with
no spaces or hyphens, it's the *entire token*, because there's nowhere to break it.

## Why one long string is different

Here's the trap. A long *sentence* is fine - it's full of spaces, so it wraps, and
its min-content is just its longest word. Even a long hyphenated word is fine,
because browsers can break after a hyphen. The floor only gets dangerous when the
content is one long **unbreakable** run: a token, a hash, a URL with no breakable
characters, a raw email address.

```css
.card { flex: 1; }   /* from the last phase */
```

```console
container: 400px wide, two cards, flex:1 each

┌──────────────────────────────────────────┐┌──┐
│ sk9Q2xZm7Kp4Rw8Tn5Vy1Bc6Hf3Jd0Ls2Gg8Ww4Aa │s…│   ← and it still runs off
└──────────────────────────────────────────┘└──┘      the right edge
        card A: 402px                       card B: 34px
```

*What just happened:* card A's content is a 45-character token with no break
points, so its min-content width is the full token - about 400px. Its
`min-width: auto` floor won't let it shrink past that, so `flex: 1` is overruled:
card A takes 402px, card B is crushed to 34px, and the row overflows its 400px
container and scrolls. `flex-shrink` never got a chance, because the floor stopped
it first.

**Why the default is `auto` and not `0`.** It's a protective default. The browser
assumes that silently shrinking a box until its content is clipped and unreadable
is worse than letting it overflow where you can at least see the problem. It's a
reasonable call - overflow is visible, hidden content is not - and, like margin
collapse, it's a sensible rule that ambushes you the first time.

## The fix: `min-width: 0`

Lower the floor. `min-width: 0` tells the item it's allowed to shrink below its
content's natural minimum:

```css
.card {
  flex: 1;
  min-width: 0;
}
```

*What just happened:* both cards are back to 200px, equal halves. `flex: 1` works
again. But run it and you'll see the token now spills out of card A's box, over
the top of card B - because you told the *card* it may shrink, but you never told
the *text* it may break. Two different problems: the card sizing, and the content
inside it.

So the complete fix is both - shrink the card, and let its text wrap:

```css
.card {
  flex: 1;
  min-width: 0;
  overflow-wrap: break-word;   /* let the unbreakable string break */
}
```

Now card A is 200px, the token wraps neatly onto multiple lines inside it, and
nothing overflows.

⚠️ **The gotcha within the gotcha.** `overflow-wrap: break-word` *by itself*,
without `min-width: 0`, does nothing here - the row still blows out to 402px. That
surprises people who reach for it first. The reason is exact: `break-word` lets
text wrap once a box is narrow, but it does not lower the item's min-content
width, so the `min-width: auto` floor is still standing at the full token width.
You have to remove the floor with `min-width: 0` *first*; the wrapping only
matters after the box is allowed to get small. (If you'd rather clip than wrap,
`overflow: hidden` also removes the floor and cuts the token off at the edge.)

## The same floor lives on grid items

Grid items have the identical default, so a long token overflows a `1fr` track the
same way - `1fr` is really `minmax(auto, 1fr)`, and that `auto` minimum is the same
floor. The fixes are the same idea: `min-width: 0` on the item, or write the track
as `minmax(0, 1fr)` so its minimum is zero from the start. You'll meet `1fr` and
`minmax` properly in [Phase 3](03-css-grid-two-dimensional-layout.md); just tuck
away that `minmax(0, 1fr)` is the grid version of this exact fix.

## Recap

1. Every flex item has a floor: by default `min-width: auto`, which means "don't
   shrink below my content's min-content width."
2. For normal text that floor is tiny (the longest word). For one long
   *unbreakable* string - a token, hash, or URL - it's the whole string, and that's
   what overrides `flex: 1` and blows the row apart.
3. `min-width: 0` removes the floor so the item shrinks to its fair share again.
4. That fixes the *box*, not the *text* - add `overflow-wrap: break-word` to wrap
   the string (or `overflow: hidden` to clip it).
5. `overflow-wrap` alone won't do it: it doesn't lower the floor. Grid items have
   the same floor, fixed with `min-width: 0` or `minmax(0, 1fr)`.

Check your intuition:

```quiz
[
  {
    "q": "A flex row of `flex: 1` cards looks fine with placeholder text but breaks when one card gets a long unbreakable API token. Why does that one card refuse to shrink?",
    "choices": ["flex: 1 only works with three or fewer items", "Its default min-width: auto won't let it shrink below the token's width", "Tokens are treated as images by the browser", "flex-shrink defaults to 0"],
    "answer": 1,
    "explain": "A flex item's default min-width: auto floor is its min-content width, which for an unbreakable token is the whole token. That floor overrides flex-shrink."
  },
  {
    "q": "You add `overflow-wrap: break-word` to the card but the row still overflows. What's missing?",
    "choices": ["word-break: keep-all", "min-width: 0 to lower the shrink floor", "flex-basis: 100%", "A fixed width on the container"],
    "answer": 1,
    "explain": "overflow-wrap lets text wrap once the box is narrow, but it doesn't lower the min-width: auto floor. min-width: 0 removes the floor so the box can actually get small."
  },
  {
    "q": "What's the grid-track equivalent of putting `min-width: 0` on a flex item?",
    "choices": ["grid-auto-flow: dense", "minmax(0, 1fr) instead of 1fr", "grid-template-columns: auto", "place-items: stretch"],
    "answer": 1,
    "explain": "1fr carries an implicit auto minimum (the same floor). Writing the track as minmax(0, 1fr) sets that minimum to zero, letting the track shrink."
  }
]
```


---

# CSS Grid: Two-Dimensional Layout

Flexbox handles one direction at a time. Grid handles rows *and* columns together, as a single
layout - which is what you actually want for a page skeleton: a header across the top, a sidebar down
one side, a footer across the bottom, all defined at once instead of nested flex containers fighting
each other.

With Flexbox, getting a sidebar to line up with a header above it and a footer below it means
carefully matching widths across three separate flex containers that don't know about each other.
Grid defines all three regions as one layout, so they can't drift out of alignment - they're cells in
the same table-like structure, not three unrelated rows guessing at the same width.

## Turning it on

```css
.dashboard {
  display: grid;
  grid-template-columns: 200px 1fr;
  grid-template-rows: auto 1fr auto;
}
```

Every direct child becomes a **grid item**, placed into the cells this creates - by default, in source
order, left to right, top to bottom.

## Defining columns and rows

`grid-template-columns` and `grid-template-rows` take a list of sizes, one per track:

```css
.layout {
  display: grid;
  grid-template-columns: 200px 1fr 100px; /* three columns */
  grid-template-rows: 80px auto;          /* two rows */
}
```

The unit to know is **`fr`** (fraction) - it divides *leftover* space after fixed-size tracks are
subtracted, similar in spirit to `flex-grow`. `1fr 1fr 1fr` is three equal columns; `200px 1fr` is a
fixed sidebar plus a column that eats everything else.

`repeat()` avoids repeating yourself:

```css
grid-template-columns: repeat(3, 1fr); /* same as 1fr 1fr 1fr */
```

**`gap`** puts consistent space between tracks, no margin hacks on edge items:

```css
.layout {
  display: grid;
  gap: 1rem;
}
```

## Naming the layout: grid-template-areas

The most readable way to build a page skeleton is naming regions and drawing the layout as ASCII art
directly in your CSS:

```css
.dashboard {
  display: grid;
  grid-template-columns: 220px 1fr;
  grid-template-rows: 70px 1fr 50px;
  grid-template-areas:
    "header  header"
    "sidebar main"
    "footer  footer";
  gap: 1rem;
  min-height: 100vh;
}

.header  { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main    { grid-area: main; }
.footer  { grid-area: footer; }
```

```html
<div class="dashboard">
  <header class="header">Dashboard</header>
  <nav class="sidebar">Nav</nav>
  <main class="main">Content</main>
  <footer class="footer">Footer</footer>
</div>
```

*What just happened:* `grid-template-areas` is a literal picture of the layout - "header" spans both
columns on row one, "sidebar" and "main" split row two, "footer" spans both columns on row three.
Each child is placed by name with `grid-area`, not by counting rows and columns. Read the CSS, see the
page.

💡 **Key point.** This is the single biggest readability win Grid has over Flexbox. Six months from
now, `grid-template-areas` still reads like a floor plan. Nested flex containers reconstructing the
same layout do not.

## Spanning cells: grid-column and grid-row

An item can span multiple tracks with `grid-column` / `grid-row`, using `span`:

```css
.featured-photo {
  grid-column: span 2; /* takes up two columns instead of one */
  grid-row: span 2;    /* and two rows */
}
```

This is how a "featured" item in a grid of photos or cards gets to be visibly bigger than its
neighbors while everything else still auto-places around it. You don't have to hand-place every other
item either - only the spanning one needs an explicit rule. Grid's auto-placement algorithm flows the
rest into whatever cells are left, in source order, exactly like it would without any spanning at all.

## Build it: a responsive photo gallery

The pattern for "as many equal-width columns as fit, wrapping automatically, no media queries":

```css
.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 1rem;
}
```

```html
<div class="gallery">
  <img src="a.jpg" alt="" />
  <img src="b.jpg" alt="" />
  <img src="c.jpg" alt="" />
  <!-- more images -->
</div>
```

*What just happened:* `minmax(180px, 1fr)` tells each column "never shrink below 180px, but grow to
fill available space." `auto-fit` computes how many 180px+ columns fit the container width and
generates exactly that many tracks - four on a wide screen, two on a tablet, one on a phone - with zero
`@media` rules. Resize the browser and watch columns appear and disappear on their own.

⚠️ **Gotcha - `auto-fit` vs `auto-fill`.** `auto-fit` collapses empty tracks to `0px` and lets existing
items stretch to fill the row, which is what you want for a gallery. `auto-fill` keeps empty tracks at
their minimum size (visible gaps, items don't stretch) - useful only when you specifically want
placeholder-style blank columns.

Lock in the two properties before moving on:

```quiz
[
  {
    "q": "What does grid-template-areas let you do that raw grid-template-columns/rows doesn't as directly?",
    "choices": ["Add animations to grid items", "Draw the layout as named regions that read like a floor plan", "Make the grid responsive automatically", "Skip using display: grid entirely"],
    "answer": 1,
    "explain": "grid-template-areas names each region and lays it out as ASCII art, so the CSS visually matches the page structure."
  },
  {
    "q": "grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)) produces what kind of layout?",
    "choices": ["A fixed 3-column grid regardless of screen size", "As many 180px-minimum columns as fit the container, wrapping automatically", "A single column on all screen sizes", "A grid that requires JavaScript to resize"],
    "answer": 1,
    "explain": "auto-fit calculates how many minmax-sized tracks fit the available width and generates that many columns, with 1fr letting them stretch to fill the row."
  },
  {
    "q": "What does grid-column: span 2 do to a grid item?",
    "choices": ["Moves it to column 2", "Makes it occupy two columns instead of one", "Duplicates it into two items", "Hides it on screens narrower than 2 columns"],
    "answer": 1,
    "explain": "span 2 tells the item to stretch across two column tracks, letting it sit visibly larger than single-column siblings."
  }
]
```


---

# Choosing Between Them (and Combining Them)

By now you've used both systems on real layouts. The question left is which one to reach for first -
and the real answer is that it's rarely either/or.

## The rule of thumb

**One dimension → Flexbox. Two dimensions → Grid.**

Ask yourself what you're actually arranging:

- A row of nav links, a stack of form fields, a row of buttons, centering one thing inside
  another - you're thinking about a single line of items. **Flexbox.**
- A page skeleton (header/sidebar/main/footer), a photo gallery, a dashboard of cards that need to
  align in both rows and columns at once - you're thinking about a grid. **Grid.**

A quick test: if you can describe the layout with words like "row" or "column," it's Flexbox. If you
need both words at once - "these should line up in rows *and* columns" - it's Grid.

⚠️ **Common tell.** If you're using Flexbox and reaching for `flex-wrap` plus fixed widths to fake
columns lining up, that's usually Grid work being done with the wrong tool. Grid keeps columns aligned
by definition; Flexbox wrapping breaks onto new lines without any column-alignment guarantee.

## They compose

Grid and Flexbox aren't competing systems - a `display: grid` container and a `display: flex`
container are both boxes, and boxes nest. A grid of flex containers is normal, common CSS, not a hack:

```css
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1.5rem;
}

.card {
  display: flex;
  flex-direction: column;
  justify-content: space-between; /* footer sticks to bottom of each card */
}
```

```html
<div class="card-grid">
  <div class="card">
    <h3>Plan A</h3>
    <p>Description text of varying length.</p>
    <button>Choose</button>
  </div>
  <div class="card">
    <h3>Plan B</h3>
    <p>A much longer description that wraps onto more lines than the others.</p>
    <button>Choose</button>
  </div>
</div>
```

*What just happened:* Grid handles the outer problem - how many cards fit per row, keeping them
aligned as a gallery. Flexbox handles the inner problem - inside each card, stacking title, text, and
button vertically, with `justify-content: space-between` pinning the button to the bottom regardless
of how much text is above it. Each system solves the dimension it's good at; neither fakes the other's
job.

## Build it: a holy grail layout

The "holy grail" layout - header, footer, and three columns (nav, main content, aside) - used to be a
CSS interview question because it was genuinely hard with floats. With Grid for the page and Flexbox
for the header's contents, it's short:

```css
.page {
  display: grid;
  grid-template-columns: 200px 1fr 200px;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header header header"
    "nav    main   aside"
    "footer footer footer";
  min-height: 100vh;
  gap: 1rem;
}

.header { grid-area: header; }
.nav    { grid-area: nav; }
.main   { grid-area: main; }
.aside  { grid-area: aside; }
.footer { grid-area: footer; }

.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
```

```html
<div class="page">
  <header class="header">
    <div class="logo">Acme</div>
    <nav class="nav-links">…links…</nav>
  </header>
  <nav class="nav">Sidebar nav</nav>
  <main class="main">Page content</main>
  <aside class="aside">Related links</aside>
  <footer class="footer">Footer</footer>
</div>
```

*What just happened:* `.page` is a Grid defining the three-column, three-row skeleton by name -
identical in spirit to the dashboard from phase 2, with one extra column. `.header`, one of the
grid's own cells, is *also* a flex container internally, spacing its logo and links the way you built
in phase 1. Two systems, two different jobs, one layout.

This is the pattern to internalize: Grid answers "where do the big regions go," Flexbox answers "how
do things line up inside a region." Reach for both without ceremony - there's no penalty for nesting
one inside the other, and most real-world pages do exactly this.

Test the big picture before moving on:

```quiz
[
  {
    "q": "You need a row of navigation links that stay centered vertically next to a logo. Which layout system fits best?",
    "choices": ["CSS Grid, because it's newer", "Flexbox, because it's a one-dimensional row arrangement", "Neither - use floats", "Grid, because navbars always need two dimensions"],
    "answer": 1,
    "explain": "A navbar is a single row of items - the one-dimension case Flexbox is built for."
  },
  {
    "q": "What's true about combining Grid and Flexbox in the same page?",
    "choices": ["They conflict and shouldn't be mixed", "A Grid container can hold Flex containers as children, and this is a normal pattern", "You must pick one system for the entire site", "Flexbox items can't be placed inside a Grid"],
    "answer": 1,
    "explain": "Grid and Flex containers are both boxes; nesting a flex container inside a grid cell (or vice versa) is standard, common CSS."
  },
  {
    "q": "In the holy grail layout example, what is grid-template-areas responsible for, and what is the header's internal display: flex responsible for?",
    "choices": ["Both do the same job redundantly", "grid-template-areas places the big page regions; flex arranges the logo and links inside the header region", "flex places the big regions; grid arranges items inside the header", "grid-template-areas only works with exactly three columns"],
    "answer": 1,
    "explain": "Grid handles the outer page skeleton (header/nav/main/aside/footer placement); Flexbox handles alignment inside the header cell itself."
  }
]
```

## Where to go next

Both layout systems assume a page that already reflows sensibly at different sizes. For the full
picture on breakpoints, fluid units, and mobile-first design, see
[Responsive Design](/guides/responsive-design).
