CSS — Layout Patterns
Modern CSS gives us two powerful layout systems: Flexbox and Grid. Rather than choosing one over the other, production layouts combine both — Grid for two-dimensional page structure, Flexbox for one-dimensional component internals.
Flexbox is one-dimensional. It manages layout along a single axis — either a row or a column. Items wrap, grow, and shrink along that axis. It excels at navbars, toolbars, card rows, and centering.
CSS Grid is two-dimensional. It manages rows and columns simultaneously. You define a template and place items into named or numbered cells. It excels at page layouts, dashboards, and magazine-style compositions.
This page catalogs the most common layout patterns in real-world CSS — from Holy Grail pages to pricing tables, sticky footers to centering tricks. Each pattern shows the CSS, the logic, and a live preview.
| Layout System | Dimensionality | Best For |
|---|---|---|
| Flexbox | 1D — row OR column | Navbars, card rows, centering, toolbars |
| Grid | 2D — rows AND columns | Page layouts, dashboards, galleries, pricing tables |
| Both Combined | Grid for structure, Flex for internals | Real-world production layouts |
info
The Holy Grail is the classic web page layout: header, sidebar, main content, and footer. Before CSS Grid, this required floats, clearfix hacks, and brittle fixed heights. With Grid template areas, it is clean and self-documenting.
| 1 | .holy-grail { |
| 2 | display: grid; |
| 3 | grid-template-columns: 220px 1fr; |
| 4 | grid-template-rows: auto 1fr auto; |
| 5 | grid-template-areas: |
| 6 | "header header" |
| 7 | "sidebar main" |
| 8 | "footer footer"; |
| 9 | min-height: 100vh; |
| 10 | gap: 0; |
| 11 | } |
| 12 | |
| 13 | .header { grid-area: header; } |
| 14 | .sidebar { grid-area: sidebar; } |
| 15 | .main { grid-area: main; } |
| 16 | .footer { grid-area: footer; } |
| 17 | |
| 18 | /* Responsive — collapse sidebar on mobile */ |
| 19 | @media (max-width: 768px) { |
| 20 | .holy-grail { |
| 21 | grid-template-columns: 1fr; |
| 22 | grid-template-areas: |
| 23 | "header" |
| 24 | "main" |
| 25 | "footer"; |
| 26 | } |
| 27 | .sidebar { display: none; } |
| 28 | } |
note
Dashboards use Grid for the overall structure (sidebar + main area) and Flexbox inside the main area for card rows, chart containers, and stat blocks. This layered approach keeps code maintainable.
| 1 | .dashboard { |
| 2 | display: grid; |
| 3 | grid-template-columns: 240px 1fr; |
| 4 | min-height: 100vh; |
| 5 | } |
| 6 | |
| 7 | .sidebar { |
| 8 | display: flex; |
| 9 | flex-direction: column; |
| 10 | gap: 4px; |
| 11 | padding: 16px; |
| 12 | } |
| 13 | |
| 14 | .sidebar-link { |
| 15 | display: flex; |
| 16 | align-items: center; |
| 17 | gap: 10px; |
| 18 | padding: 10px 14px; |
| 19 | border-radius: 8px; |
| 20 | color: #a0a0a0; |
| 21 | } |
| 22 | |
| 23 | .sidebar-link.active { |
| 24 | background: rgba(0, 255, 65, 0.08); |
| 25 | color: #00ff41; |
| 26 | } |
| 27 | |
| 28 | .main-content { |
| 29 | display: flex; |
| 30 | flex-direction: column; |
| 31 | gap: 24px; |
| 32 | padding: 24px; |
| 33 | } |
| 34 | |
| 35 | .stats-row { |
| 36 | display: flex; |
| 37 | gap: 16px; |
| 38 | } |
| 39 | |
| 40 | .stat-card { |
| 41 | flex: 1; |
| 42 | display: flex; |
| 43 | flex-direction: column; |
| 44 | gap: 8px; |
| 45 | padding: 20px; |
| 46 | border-radius: 12px; |
| 47 | background: #1a1a2e; |
| 48 | } |
| 49 | |
| 50 | @media (max-width: 768px) { |
| 51 | .dashboard { |
| 52 | grid-template-columns: 1fr; |
| 53 | } |
| 54 | .sidebar { display: none; } |
| 55 | } |
The auto-fill + minmax() combination creates card grids that adapt from 1 to 4+ columns without any media queries. This is the most reusable pattern in modern CSS.
| 1 | .card-grid { |
| 2 | display: grid; |
| 3 | grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); |
| 4 | gap: 24px; |
| 5 | padding: 24px; |
| 6 | } |
| 7 | |
| 8 | .card { |
| 9 | display: flex; |
| 10 | flex-direction: column; |
| 11 | border-radius: 12px; |
| 12 | overflow: hidden; |
| 13 | } |
| 14 | |
| 15 | .card-image { |
| 16 | width: 100%; |
| 17 | aspect-ratio: 16 / 9; |
| 18 | object-fit: cover; |
| 19 | } |
| 20 | |
| 21 | .card-body { |
| 22 | display: flex; |
| 23 | flex-direction: column; |
| 24 | gap: 8px; |
| 25 | padding: 16px; |
| 26 | } |
| 27 | |
| 28 | .card-footer { |
| 29 | margin-top: auto; /* pushes CTA to bottom */ |
| 30 | padding: 0 16px 16px; |
| 31 | } |
best practice
Blog layouts combine a full-width hero section with a constrained content area. On wider screens, a sidebar for TOC or ads is placed alongside the article. Grid handles the two-column split; Flexbox handles heading and paragraph alignment.
| 1 | .blog-layout { |
| 2 | display: grid; |
| 3 | grid-template-columns: 1fr min(65ch, 100%) 1fr; |
| 4 | } |
| 5 | |
| 6 | /* Full-width hero spans all columns */ |
| 7 | .hero { |
| 8 | grid-column: 1 / -1; |
| 9 | } |
| 10 | |
| 11 | /* Article content in center column */ |
| 12 | .article { |
| 13 | grid-column: 2; |
| 14 | } |
| 15 | |
| 16 | /* With sidebar on wider screens */ |
| 17 | @media (min-width: 1024px) { |
| 18 | .blog-layout { |
| 19 | grid-template-columns: 1fr min(65ch, 100%) 260px 1fr; |
| 20 | } |
| 21 | .article { grid-column: 2; } |
| 22 | .sidebar { grid-column: 3; } |
| 23 | } |
| 24 | |
| 25 | .sidebar { |
| 26 | position: sticky; |
| 27 | top: 80px; |
| 28 | align-self: start; |
| 29 | } |
Pricing tables use Grid for equal-height columns and Flexbox inside each card for vertical alignment. The featured plan can span more columns or use visual emphasis to stand out.
| 1 | .pricing-grid { |
| 2 | display: grid; |
| 3 | grid-template-columns: repeat(3, 1fr); |
| 4 | gap: 24px; |
| 5 | max-width: 960px; |
| 6 | margin: 0 auto; |
| 7 | } |
| 8 | |
| 9 | .pricing-card { |
| 10 | display: flex; |
| 11 | flex-direction: column; |
| 12 | border: 1px solid #222; |
| 13 | border-radius: 16px; |
| 14 | padding: 32px 24px; |
| 15 | } |
| 16 | |
| 17 | .pricing-card.featured { |
| 18 | border-color: #00ff41; |
| 19 | transform: scale(1.05); |
| 20 | } |
| 21 | |
| 22 | .pricing-header { |
| 23 | text-align: center; |
| 24 | margin-bottom: 24px; |
| 25 | } |
| 26 | |
| 27 | .pricing-features { |
| 28 | display: flex; |
| 29 | flex-direction: column; |
| 30 | gap: 12px; |
| 31 | flex: 1; |
| 32 | list-style: none; |
| 33 | } |
| 34 | |
| 35 | .pricing-cta { |
| 36 | margin-top: 24px; |
| 37 | text-align: center; |
| 38 | } |
| 39 | |
| 40 | @media (max-width: 768px) { |
| 41 | .pricing-grid { |
| 42 | grid-template-columns: 1fr; |
| 43 | } |
| 44 | .pricing-card.featured { |
| 45 | transform: none; |
| 46 | order: -1; |
| 47 | } |
| 48 | } |
Split-screen layouts divide the viewport into two halves — typically text on one side and an image or illustration on the other. Use CSS clamp() for fluid typography that scales between breakpoints.
| 1 | .hero { |
| 2 | display: grid; |
| 3 | grid-template-columns: 1fr 1fr; |
| 4 | min-height: 100vh; |
| 5 | gap: 48px; |
| 6 | align-items: center; |
| 7 | padding: 0 clamp(24px, 5vw, 80px); |
| 8 | } |
| 9 | |
| 10 | .hero-content { |
| 11 | display: flex; |
| 12 | flex-direction: column; |
| 13 | gap: 24px; |
| 14 | } |
| 15 | |
| 16 | .hero-title { |
| 17 | font-size: clamp(2rem, 5vw, 4rem); |
| 18 | line-height: 1.1; |
| 19 | color: #e0e0e0; |
| 20 | } |
| 21 | |
| 22 | .hero-subtitle { |
| 23 | font-size: clamp(1rem, 2vw, 1.25rem); |
| 24 | color: #808080; |
| 25 | max-width: 480px; |
| 26 | } |
| 27 | |
| 28 | .hero-image { |
| 29 | width: 100%; |
| 30 | height: auto; |
| 31 | border-radius: 16px; |
| 32 | } |
| 33 | |
| 34 | @media (max-width: 768px) { |
| 35 | .hero { |
| 36 | grid-template-columns: 1fr; |
| 37 | min-height: auto; |
| 38 | padding: 48px 24px; |
| 39 | text-align: center; |
| 40 | } |
| 41 | .hero-subtitle { margin: 0 auto; } |
| 42 | } |
note
Centering in CSS has a reputation for being hard. In modern CSS, there are three clean approaches — each with different use cases.
Method 1: Grid with place-items (simplest)
| 1 | .center-grid { |
| 2 | display: grid; |
| 3 | place-items: center; |
| 4 | min-height: 100vh; |
| 5 | } |
| 6 | |
| 7 | /* That's it. Two lines. */ |
Method 2: Flexbox
| 1 | .center-flex { |
| 2 | display: flex; |
| 3 | justify-content: center; |
| 4 | align-items: center; |
| 5 | min-height: 100vh; |
| 6 | } |
Method 3: Absolute + Transform
| 1 | .center-abs { |
| 2 | position: absolute; |
| 3 | top: 50%; |
| 4 | left: 50%; |
| 5 | transform: translate(-50%, -50%); |
| 6 | } |
| 7 | |
| 8 | /* Best for: overlaying content on a parent */ |
| Method | Lines of CSS | Best For |
|---|---|---|
| Grid place-items | 2 | Simplest centering, one child element |
| Flexbox | 3 | Multiple children along an axis |
| Absolute + Transform | 3 | Overlay on a positioned parent |
These are the most frequent layout pitfalls that lead to fragile, hard-to-maintain CSS.
| 1 | ./* Wrong: nested flex for 2D */ |
| 2 | .row { display: flex; gap: 16px; } |
| 3 | .rows { display: flex; flex-direction: column; gap: 16px; } |
| 4 | |
| 5 | /* Right: Grid for 2D */ |
| 6 | .grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; } |
| 1 | ./* Wrong: uneven margins */ |
| 2 | .card { margin: 0 8px 16px 8px; } |
| 3 | |
| 4 | /* Right: consistent gap */ |
| 5 | .cards { display: flex; gap: 16px; } |
| 1 | ./* Wrong: fixed width */ |
| 2 | .sidebar { width: 300px; } |
| 3 | |
| 4 | /* Right: flexible with minimum */ |
| 5 | .sidebar { width: min(300px, 30vw); } |
| 1 | ./* Wrong: desktop first */ |
| 2 | @media (max-width: 768px) { /* shrink for mobile */ } |
| 3 | |
| 4 | /* Right: mobile first */ |
| 5 | .sidebar { display: none; } |
| 6 | @media (min-width: 768px) { .sidebar { display: block; } } |
Use this decision tree to quickly determine the right layout approach for any scenario.
| Question | If YES | If NO |
|---|---|---|
| Is it a single row OR single column? | Use Flexbox | Continue ↓ |
| Do you need to control both rows AND columns? | Use CSS Grid | Continue ↓ |
| Is it a page-level layout with header/sidebar/footer? | Use Grid with template areas | Continue ↓ |
| Do items need to wrap responsively? | Flexbox with flex-wrap | Continue ↓ |
| Do items have varying heights and need equal alignment? | Grid with auto-rows | Continue ↓ |
| Is it a simple centering problem? | Grid place-items: center | Combine both — Grid for structure, Flex for internals |
pro tip
best practice