202 companion flashcards · AI-assisted study content · Open the deck →
This deck is all about one of the most practical questions in modern CSS: when should you reach for Grid, and when does Flexbox make more sense? Each card walks you through a real layout scenario — like centering an element, building a sidebar, or handling equal-height columns — and asks you to pick the right tool. It's a focused way to sharpen your layout instincts rather than just memorizing syntax.
The deck is a great fit if you're comfortable writing basic CSS but find yourself hesitating whenever a new layout comes up. Whether you're a beginner trying to build intuition or a more experienced developer who wants to stop second-guessing between `display: grid` and `display: flex`, the spaced question-and-answer format helps the decision patterns stick. You'll cover essentials like `auto-fit` versus `auto-fill`, the meaning of `flex: 1`, and how Grid's `order` property compares to Flexbox's.
To get the most out of these cards, try answering each one out loud before flipping — even a quick gut guess trains your decision-making. Reviewing in short sessions across a few days works better than cramming everything at once, since layout concepts really click when you see them repeated from different angles. And keep a code editor open nearby so you can test any answer you're unsure about; nothing beats seeing Grid and Flexbox behave side by side.
The most important question to answer before writing any layout CSS is whether the structure you need is one-dimensional or two-dimensional. Flexbox is a one-dimensional layout system: its children flow along a single main axis (a row or a column), and any relationship between rows and columns must be assembled by hand. CSS Grid, by contrast, is a two-dimensional layout system where rows and columns are defined together and items can be placed precisely within that grid. A practical heuristic is to ask whether you are thinking "row or column" — reach for Flexbox — or "rows and columns with named positions" — reach for Grid.
Even with that rule, the two systems overlap. Centering a single child on the page is a trivial example: both \(display: grid; place-items: center;\) and \(display: flex; align-items: center; justify-content: center;\) produce the same result. The choice comes down to context. If the page itself is laid out with Grid and the centered child is just a small component, Flexbox is fine. If you are building the entire page skeleton — header, sidebar, main, footer — Grid's two-dimensional framing usually wins because rows and columns of the page are explicitly declared up front.
There is also a small class of layouts where neither module is the right tool. A lone child centered inside a box of known size can use \(margin: auto\), absolute positioning with a transform, or even a single line-height trick. Modern guidance generally favors reaching for Grid first when the layout is structural, then dropping into Flexbox for one-dimensional internals of individual components. The two systems coexist rather than compete, and the best layouts often mix them: Grid for the page, Flexbox for cards, navigation bars, and the like.
A flex container has two axes. The main axis is the direction children flow, controlled by \(flex-direction\) (with options \(row\), \(row-reverse\), \(column\), \(column-reverse\)), and \(row\) is the default. The cross axis is perpendicular to that. Alignment along the main axis is governed by \(justify-content\), which defaults to \(flex-start\) and offers \(center\), \(flex-end\), \(space-between\), \(space-around\), and \(space-evenly\). Alignment along the cross axis is governed by \(align-items\), which defaults to \(stretch\) so that children fill the cross axis — the reason flex columns in a row are naturally the same height.
Every flex item is sized by three properties: \(flex-grow\), \(flex-shrink\), and \(flex-basis\). The basis is the starting size before free space is distributed; grow distributes extra space, shrink takes it away under overflow. The shorthand \(flex\) is the everyday form: \(flex: 1\) is \(1 1 0%\) (equal fills regardless of content), \(flex: auto\) is \(1 1 auto\) (grow and shrink from content size), \(flex: initial\) is \(0 1 auto\) (shrink but do not grow), and \(flex: none\) is \(0 0 auto\) (no grow, no shrink). Setting \(flex-basis: 0\) makes widths purely proportional because the starting size is ignored; \(flex-basis: auto\) starts from the content's intrinsic size.
Wrapping onto multiple lines requires \(flex-wrap: wrap\) (or \(wrap-reverse\)); the shorthand \(flex-flow: row wrap\) combines direction and wrap. When items wrap, \(align-content\) distributes the resulting lines along the cross axis, while \(align-items\) continues to align each item within its own line. \(gap\) works on flex containers in modern browsers and replaces the older "margins on every child except the last" pattern. One often-overlooked trap: flex items default to \(min-width: auto\), which means they cannot shrink below their content's minimum size and can blow out the layout. The standard fix is \(min-width: 0\) on the item. For the visual equivalent of CSS Grid's \(1fr\) in flex, simply set \(flex: 1\) on each child.
A CSS grid is defined by declaring explicit tracks on its container. \(grid-template-columns: 200px 1fr 200px\) sets up two fixed sides and a flexible middle; \(grid-template-rows: 100px 1fr auto\) does the same vertically. Tracks may use keyword \(auto\) (sized by largest content), fixed lengths, the flexible \(fr\) unit, or be calculated with \(minmax(100px, 1fr)\) and \(fit-content(200px)\). The \(repeat()\) function reduces repetition: \(repeat(3, 1fr)\) produces three equal columns. Named lines inside \(repeat()\) work too — \(repeat(3, [col-start] 1fr [col-end])\) — letting items be placed by name.
Beyond raw track sizes, Grid offers three higher-level placement tools. \(grid-template-areas\) lets you paint the layout as ASCII art using names; \(grid-area\) then assigns a child to a named region (\(grid-area: header\)) or by numeric lines (\(grid-area: 1 / 1 / 2 / 4\) meaning row-start / column-start / row-end / column-end). Implicit named areas automatically generate four line names per region, which can be referenced with the explicit-line syntax. The full shorthand \(grid\) resets all sub-properties and combines template, auto-flow, and auto-rows/columns; \(grid-template\) is narrower and covers only rows, columns, and areas.
When items spill outside the explicit grid, an implicit grid is created and its tracks are sized by \(grid-auto-rows\) and \(grid-auto-columns\). \(grid-auto-flow\) controls whether items fill row by row (default) or column by column, and the \(dense\) keyword backfills holes when items have varying spans — at the cost of reordering visually. Spanning is performed with the keyword \(span\) or explicit lines: \(grid-column: span 2\) reaches across two columns, \(grid-column: 1 / 3\) reaches from line 1 to line 3, and \(grid-column: 1 / -1\) spans every column because negative line numbers count from the end (-1 is the last line). A modern addition, \(grid-template-columns: subgrid\), lets a nested grid inherit track sizes and named lines from its parent.
Alignment in Flex and Grid shares the same vocabulary but applies slightly differently. \(justify-content\) distributes items along the main axis (Flex) or aligns the whole grid within its container (Grid); \(align-items\) aligns each item along the cross axis (Flex) or within its cell (Grid). \(align-content\) goes one level up in both: when multiple lines exist (wrapped flex or implicit grid rows), it distributes those lines along the cross axis. \(align-self\) and \(justify-self\) override the parent's alignment for a single item — important to know that \(justify-self\) has no effect in Flexbox, where cross-axis alignment is done with \(align-self\) or \(margin: auto\).
The shorthand \(place-*\) properties combine align and justify versions. \(place-items: center\) on a grid (or \(place-content: center\) on a flex container) centers any child with one declaration. \(place-self: end\) pins one item to the bottom-right of its cell. Margins still work in flex: \(margin-left: auto\) on the last flex child pushes it to the end, which is the classic "logo on the left, links on the right" navbar trick. For Grid, the analogue of flex's \(space-between\) is usually handled at the track level rather than via \(justify-content\).
A concrete comparison helps cement the model. \(display: grid; place-items: center;\) and \(display: flex; align-items: center; justify-content: center;\) both center a single child. With multiple children, Flexbox's \(justify-content\) spreads them along the row (\(space-between\) leaves no edge gap, \(space-around\) gives half-gap at the edges making interior gaps look larger, \(space-evenly\) gives identical spacing everywhere). In Grid, the equivalent "spread" effect is generally achieved by track sizing rather than content alignment — for instance \(grid-template-columns: repeat(3, 1fr)\) places three equal gaps of free space between four items.
Two-column sidebars are the canonical comparison. In Grid, \(grid-template-columns: 250px 1fr;\) defines an explicit sidebar plus flexible main region. In Flex, the sidebar gets \(flex: 0 0 250px\) and the main area gets \(flex: 1\). Both produce identical visual results, but the Grid version declares the structure once while the Flex version encodes proportions on each child. For the "Holy Grail" layout — header, navigation, main, aside, footer — Grid's \(grid-template-areas\) shines. A compact declaration such as \(grid-template: 'header header header' auto 'nav main aside' 1fr 'footer footer footer' auto / 200px 1fr 200px;\) names every region, after which children are placed with \(grid-area: header\) and so on.
Sticky footers are equally idiomatic in both systems. With Flexbox: make the body a column flex container with \(min-height: 100vh\), give the content area \(flex: 1\), and the footer hugs the bottom. With Grid: \(grid-template-rows: 1fr auto\) on the body achieves the same. Cards with a body that grows to fill and a footer that sticks to the end rely on the same \(display: flex; flex-direction: column\) pattern with \(flex: 1\) on the body. Forms are particularly well-suited to Grid because label/input pairs are inherently two-dimensional: \(grid-template-columns: max-content 1fr\) aligns labels to their content and inputs to remaining space, with a single \(gap\) replacing per-row margin tricks.
Other recurring patterns include navbars (Flexbox along a single row, often with \(justify-content: space-between\) or the \(margin-left: auto\) trick), chat layouts (a flex column with a scrolling \(flex: 1\) body and an input pinned to the bottom via \(align-self: flex-end\)), image galleries (Grid with \(grid-template-columns: repeat(auto-fill, minmax(150px, 1fr))\) plus \(aspect-ratio: 1;\)), kanban boards (a flex row of columns, each itself a flex column of cards), and pricing tables (Grid with a header row that spans and feature rows aligned across columns). Mixing the two is a feature, not a workaround: Grid for the page skeleton and Flex for component internals is a common and recommended division of labor. \(display: contents\) can remove a wrapper's box so its children participate directly in a parent's flex/grid context, though it has historically caused screen-reader gaps that are now being addressed.
The simplest responsive switch is a media query on the grid template itself. A sidebar-plus-main layout collapses to a single column on mobile with \(@media (max-width: 768px) { grid-template-columns: 1fr; }\). For a truly automatic responsive grid, the combination \(grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));\) produces as many columns as fit at the current width, each between 220 pixels and a fair share of remaining space. The flex equivalent is \(flex-wrap: wrap\) with \(flex: 1 1 220px\) on each card and a \(gap\) for spacing.
A subtle distinction inside that auto-track syntax is the difference between \(auto-fit\) and \(auto-fill\). Both create as many tracks as fit, but \(auto-fill\) reserves empty tracks when there are fewer items than tracks (they collapse to their minimum size), while \(auto-fit\) collapses empty tracks entirely so the remaining items stretch to fill the row. Choosing the wrong one can leave awkward gaps in a sparse gallery. To avoid layout jumps when an \(auto-fit\) minimum is too aggressive, choose the \(minmax\) minimum carefully — typically a few pixels below the intended card width.
Container queries extend this idea beyond the viewport. With \(container-type: inline-size\) on a parent, \(@container (min-width: 400px) { ... }\) applies styles based on the parent's own width — perfect for reusable components placed in sidebars, modals, or main content. \(container-type: size\) queries both axes but requires a fixed height on the parent. The accompanying container query units (\(cqw\), \(cqh\), \(cqi\), \(cqb\), \(cqmin\), \(cqmax\)) let you size children relative to the container rather than the viewport. The older \(column-count\) property remains useful for newspaper-style text flow but is a different category from component layout — it is not a substitute for flex or grid in interactive UIs.
Several pitfalls recur across flex and grid. As noted earlier, flex and grid items have an implicit \(min-width: auto\) (or \(min-height: auto\)) that prevents them from shrinking below their content size, causing overflow; the fix is to set \(min-width: 0\) on the item, or \(minmax(0, 1fr)\) on the track. Images in flex containers without \(max-width: 100%; height: auto;\) can blow out their parent. \(overflow: hidden\) on a flex parent can clip absolutely-positioned children unintentionally. Sticky elements inside grid cells require a parent of definite height for the stickiness to fire.\()\)
Accessibility deserves special attention. Both \(order\) (flex) and visual \(grid-area\) reassignment change only the rendering, not the DOM. Screen readers and keyboard tab order follow source order regardless of visual order, so non-cosmetic reshuffling with these properties creates a disconnect between what users see and what assistive technology traverses. When the visual order matters for comprehension, restructure the DOM rather than relying on visual overrides. \(display: contents\) similarly removes a box from the accessibility tree in some older screen readers; modern engines are improving this but verification is wise. On the upside, defining grid tracks and using \(aspect-ratio\) on images prevents most of the layout shift that hurts Cumulative Layout Shift scores, because content reserves space before it loads.
For older browsers, Grid has a clear story: a legacy, partially-implemented version exists in IE10 and IE11 with a different specification, so the modern grid should be wrapped in \(@supports (display: grid) { ... }\). Flexbox works in all modern browsers and even IE11 (with quirks); old \(-ms\)-\) prefixes are rarely needed today. Looking forward, subgrid is now available across recent versions of Chrome, Safari, and Firefox and lets nested grids inherit track sizes from their parent — eliminating a long-standing pain point where inner items couldn't align cleanly to outer columns without redundant declarations. Native CSS masonry via \(grid-template-rows: masonry;\) is being standardized and partially implemented in Firefox. The View Transitions API works across flex and grid for animated DOM changes. For day-to-day work the guidance remains: reach for Grid when structure is two-dimensional and known, Flexbox for one-dimensional component internals, and remember that DOM order is sacred even when visual order seems to need adjusting.
For deeper study, CSS-Tricks' "A Complete Guide to Flexbox" and "A Complete Guide to Grid" remain definitive references, and tools like cssgrid-generator.netlify.app plus the Chrome and Firefox DevTools grid and flex inspectors make iterating on track and alignment values far easier than reading back from rendered pixels. Newer selectors like \(:has()\) and \(:where()\) reshape how layout-adjacent styling can be written, but the grid-versus-flex decision itself rarely changes: it is still mostly about whether you are laying out along one axis or two.
flex-wrap: wrap;stretch — children expand to fill the cross axis.order and grid-area for non-cosmetic reflow.@supports (display: grid) to opt-in.grid-column: 1 / -1;Drill this topic
202 flashcards on CSS Grid Vs Flexbox When To Use Which — free, no signup needed to start.
Study CSS Grid Vs Flexbox When To Use Which flashcardsLearnWiki pages are generated with AI assistance from LearnCoachAssist's reviewed study catalog and may contain errors — verify anything critical against your course materials.