CSS Layouts in 2026: Flexbox, Grid, and Beyond
If you started web development in the era of float-based layouts, you know the pain. Clearfix hacks, collapsing containers, margin weirdness that made you question your career choices. I spent years wrestling with floats before Flexbox arrived and made one-dimensional layouts simple. Then CSS Grid landed and gave us proper two-dimensional control. Now, in 2026, we have container queries, subgrid, and even more powerful tools at our disposal.
In this article, I will cover the layout tools that matter right now: Flexbox, CSS Grid, container queries, and subgrid. I will explain when to use each one, show practical code examples, and walk through common layout patterns that you can drop directly into your projects.
A Brief History of CSS Layouts
Before we dive into modern techniques, it is worth understanding how we got here, because it explains why certain patterns exist.
- Floats (late 1990s): Originally designed for wrapping text around images. Developers repurposed them for multi-column layouts, which led to the infamous clearfix hack and countless hours of debugging.
- Inline-block (2000s): Using
display: inline-blocksolved some float issues but introduced whitespace problems between elements and required workarounds for vertical alignment. - Flexbox (2012, stable ~2015): Purpose-built for one-dimensional layouts (rows or columns). Finally gave us proper vertical centering, equal-height columns, and flexible spacing without hacks.
- CSS Grid (2017): Purpose-built for two-dimensional layouts (rows and columns simultaneously). Made complex page layouts possible with clean, readable CSS.
- Container queries (2023-2025): Components that respond to their container's size instead of the viewport. A game-changer for reusable components.
- Subgrid (2023-2025): Lets nested grid items participate in their parent's grid tracks, solving alignment problems that were previously impossible.
Flexbox Fundamentals
Flexbox operates on a single axis at a time. You choose whether items flow horizontally (row) or vertically (column), and then you control how they are distributed along that axis and how they align on the perpendicular axis.
The Two Axes
The main axis is the direction items flow. For flex-direction: row (the default), the main axis is horizontal. For flex-direction: column, it is vertical. The cross axis is perpendicular to the main axis.
.container {
display: flex;
flex-direction: row; /* main axis: horizontal */
justify-content: center; /* align along main axis */
align-items: center; /* align along cross axis */
gap: 16px; /* space between items */
}
justify-content and align-items
justify-content distributes space along the main axis. The most useful values:
flex-start/flex-end: pack items to the start or endcenter: center itemsspace-between: equal space between items, no space at edgesspace-around: equal space around each itemspace-evenly: equal space between and at edges
align-items does the same along the cross axis. The default is stretch, which is why flex children fill the container height by default.
flex-grow, flex-shrink, and flex-basis
These three properties control how items share available space:
.item {
flex-grow: 1; /* grow to fill available space (proportional) */
flex-shrink: 1; /* shrink if container is too small */
flex-basis: 200px; /* starting size before growing/shrinking */
/* Shorthand */
flex: 1 1 200px;
}
A common pattern: flex: 1 on all children makes them share space equally. Setting flex: none on one child makes it stay at its natural size while siblings fill the remaining space.
When to Use Flexbox
Flexbox shines for one-dimensional arrangements. Here are my go-to use cases:
Navigation Bars
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 32px;
}
.nav-links {
display: flex;
gap: 24px;
list-style: none;
}
Centering (the Classic Problem)
/* Center anything, both axes, in three lines */
.center-me {
display: flex;
justify-content: center;
align-items: center;
}
Card Rows with Equal Height
.card-row {
display: flex;
gap: 16px;
}
.card {
flex: 1;
/* All cards stretch to the same height automatically */
}
You can experiment with all of these patterns using our CSS Layout Generator, which lets you adjust Flexbox and Grid properties visually and export the CSS.
CSS Grid Fundamentals
While Flexbox handles one direction at a time, CSS Grid lets you control both rows and columns simultaneously. This makes it the right choice for page-level layouts and complex component structures.
Template Areas
Grid template areas let you define layout structure using a visual ASCII-art syntax:
.page {
display: grid;
grid-template-areas:
"header header"
"sidebar content"
"footer footer";
grid-template-columns: 250px 1fr;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.content { grid-area: content; }
.footer { grid-area: footer; }
fr Units, repeat(), and minmax()
The fr unit represents a fraction of available space. Combined with repeat() and minmax(), you can create flexible grids with very little code:
/* Three equal columns */
grid-template-columns: repeat(3, 1fr);
/* Columns that are at least 250px, expand to fill */
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
/* First column fixed, rest flexible */
grid-template-columns: 200px repeat(3, 1fr);
auto-fill vs auto-fit
These two keywords behave identically when there are enough items to fill the row. The difference appears when there are fewer items than columns:
auto-fill: Creates as many tracks as will fit, even if they are empty. Items stay atminmax()size.auto-fit: Collapses empty tracks. Items stretch to fill the available space.
/* Items stay at 250px min even with extra space */
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
/* Items stretch to fill all available space */
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
When to Use Grid
Page Layouts
Any time you are laying out a full page with header, sidebar, content, and footer, Grid is the natural choice. The template areas syntax makes the structure immediately readable.
Dashboards
.dashboard {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: auto;
gap: 16px;
}
.widget-large {
grid-column: span 2;
grid-row: span 2;
}
.widget-wide {
grid-column: span 2;
}
Image Galleries
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 8px;
}
.gallery img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 8px;
}
Flexbox vs Grid: A Decision Framework
I used to overthink this. Here is the simple framework I follow now:
- One dimension (row OR column)? Use Flexbox.
- Two dimensions (rows AND columns)? Use Grid.
- Content-driven sizing? Flexbox lets items determine their own size. Grid defines track sizes at the container level.
- Unknown number of items? Flexbox with
flex-wrap, or Grid withauto-fill/auto-fit. - Precise placement needed? Grid. You can place items at exact row/column positions.
In practice, most layouts use both. A Grid for the page structure, Flexbox for the navigation bar, Grid for the card gallery, Flexbox for the card contents. They compose beautifully together.
Container Queries: Component-Level Responsiveness
Media queries respond to the viewport size. Container queries respond to the size of a parent element. This distinction is critical for reusable components.
Consider a card component. With media queries, it changes layout based on the browser window width. But what if that card appears in a narrow sidebar on a wide screen? Media queries would give it the "wide" layout even though it is in a narrow space. Container queries solve this.
/* Define the container */
.card-wrapper {
container-type: inline-size;
container-name: card;
}
/* Respond to the container's width, not the viewport */
@container card (min-width: 400px) {
.card {
display: flex;
flex-direction: row;
gap: 16px;
}
.card-image {
width: 40%;
}
}
@container card (max-width: 399px) {
.card {
display: flex;
flex-direction: column;
}
.card-image {
width: 100%;
}
}
Container queries have full browser support as of 2024 and are stable for production use. If you are building a component library or design system, they should be your default approach for responsive components.
Subgrid: Aligned Nested Grids
Before subgrid, nested grid items could not align with their parent grid's tracks. If you had a card grid where each card contained a title, description, and button, the titles might be different heights, pushing the descriptions out of alignment.
Subgrid solves this by letting a child grid inherit track definitions from its parent:
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}
.card {
display: grid;
grid-row: span 3;
grid-template-rows: subgrid; /* inherit row tracks from parent */
gap: 8px;
}
.card-title { /* aligns across all cards */ }
.card-desc { /* aligns across all cards */ }
.card-button { align-self: end; }
Subgrid reached full browser support in late 2023 (Safari was last) and is ready for production use. It is particularly powerful for form layouts, data tables, and any component where you need consistent vertical or horizontal alignment across siblings.
Common Layout Patterns
Holy Grail Layout
Header, footer, main content, and two sidebars. This used to require complex float gymnastics. With Grid, it is straightforward:
.holy-grail {
display: grid;
grid-template-areas:
"header header header"
"left main right"
"footer footer footer";
grid-template-columns: 200px 1fr 200px;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
}
Sticky Footer
A footer that stays at the bottom of the viewport when content is short, but gets pushed down when content is tall:
body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
main {
flex: 1; /* takes all available space, pushing footer down */
}
footer {
/* stays at the bottom naturally */
}
Responsive Card Grid
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 24px;
padding: 24px;
}
This creates a grid where cards are at least 280px wide and automatically wrap to the next row. No media queries needed.
Sidebar Layout
.with-sidebar {
display: grid;
grid-template-columns: minmax(200px, 25%) 1fr;
gap: 24px;
}
@media (max-width: 768px) {
.with-sidebar {
grid-template-columns: 1fr;
}
}
Box Shadows and Visual Polish
Layouts are structure. Shadows, gradients, and color add the visual polish that makes a layout feel finished. Box shadows create depth and help users understand visual hierarchy.
/* Subtle card elevation */
.card {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: box-shadow 0.2s ease;
}
.card:hover {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}
/* Layered shadows for realistic depth */
.elevated {
box-shadow:
0 1px 2px rgba(0, 0, 0, 0.07),
0 4px 8px rgba(0, 0, 0, 0.07),
0 16px 32px rgba(0, 0, 0, 0.07);
}
Getting box shadows right by hand can be tedious. Our Box Shadow Generator lets you adjust offset, blur, spread, and color visually and copy the CSS. For gradients, try the Gradient Generator. And if you need a cohesive color scheme for your layout, the Color Palette Generator can help you build one from a single base color.
Wrapping Up
CSS layout has come remarkably far. Flexbox handles one-dimensional distribution with elegance. Grid gives you full two-dimensional control. Container queries make components truly portable. And subgrid solves the alignment problems that have plagued nested layouts for years.
My recommendation: learn Flexbox and Grid deeply. They complement each other, and together they cover virtually every layout scenario. Adopt container queries for any component that needs to be reusable across different contexts. And use subgrid when you need sibling alignment across nested structures.
The best way to internalize these concepts is to build with them. Try recreating a layout you admire using only Flexbox and Grid. Use our CSS Layout Generator to experiment with property values visually. The muscle memory will follow.