CSS Layouts: Common Methods for Building Web Pages
Learn when to use normal flow, Flexbox, Grid, multi-column layout, floats, and positioning, with responsive examples and practical guidance.
CSS layouts control how elements relate to normal document flow, their neighbors, their parent, and the viewport. Start with semantic HTML that remains readable in normal flow, then use Flexbox for a single row or column, Grid for coordinated rows and columns, multi-column layout for one continuous stream of text, floats for text wrapping around an item, and positioning for deliberate offsets or overlays. Responsive design adapts these layouts to available space; it is not a separate layout algorithm. These methods can be combined on the same page.
1. Start with normal flow
Normal flow is the browser’s default layout. In a typical horizontal writing mode, block-level elements stack vertically, while inline content shares a line and wraps as needed. Use it as the readable foundation of the page: headings, paragraphs, lists, and sections should make sense in source order without layout styling.
<main>
<h1>Page title</h1>
<p>Introductory text that reads naturally before any layout is applied.</p>
<section>
<h2>First section</h2>
<p>Section content.</p>
</section>
</main>
CSS changes how boxes participate in flow through properties such as display, position, float, and grid or flex container declarations. A robust page keeps its content understandable if a layout rule is removed or the available width changes.
2. Use Flexbox for one-dimensional layouts
Flexbox arranges items along one main axis, either a row or a column. It is suited to navigation bars, aligned controls, toolbars, and collections that need to share space along one axis. Items can grow or shrink as space changes.
<nav class="site-nav" aria-label="Main navigation">
<a class="brand" href="/">Example</a>
<div class="nav-links">
<a href="/docs">Docs</a>
<a href="/about">About</a>
</div>
</nav>
.site-nav {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}
.nav-links {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
align-items aligns items across the cross axis; justify-content distributes free space along the main axis; and gap sets spacing between items. Use flex-wrap: wrap when items may not fit on one line. Set flex-direction: column for a vertical stack. Avoid relying on visual order changes to compensate for a confusing HTML source order.
3. Use Grid for rows and columns
Grid is a two-dimensional layout system. Use it when items or page regions should coordinate across both rows and columns, when placement should be explicit, or when a fluid collection can auto-place its items into tracks.
<section class="cards" aria-label="Products">
<article class="card">
<h2>Starter</h2>
<p>For small projects.</p>
<a href="/starter">View details</a>
</article>
<article class="card">
<h2>Team</h2>
<p>For collaborative work.</p>
<a href="/team">View details</a>
</article>
</section>
.cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
}
.card {
display: flex;
flex-direction: column;
gap: 0.75rem;
padding: 1rem;
border: 1px solid #ccc;
}
.card a {
margin-top: auto;
}
repeat(auto-fill, minmax(200px, 1fr)) creates as many tracks as fit, with a chosen minimum width and tracks that can expand. The 200px minimum here is an example, not a universal value; choose a minimum that fits your content. The card uses Flexbox internally to stack its own contents and keep the link near the bottom. Combining Grid for the collection and Flexbox inside each item is common.
For named page regions, define areas with grid-template-areas and assign each child a grid-area. For example, a header, sidebar, main region, and footer can be mapped into rows and columns, then rearranged at a breakpoint. Use explicit placement when the relationships matter; let auto-placement handle collections where the exact row and column are not important.
4. Use multi-column layout for continuous content
Multi-column layout flows one continuous content stream from one column into the next, like newspaper text. It is appropriate for long prose or lists where reading order should continue down one column and then into another. It is not the same as a Grid of independent cells.
.article-index {
columns: 18rem 3;
column-gap: 2rem;
column-rule: 1px solid #ddd;
}
The columns shorthand accepts a preferred column width and count. The browser balances and lays out the stream within the available space. You can instead set column-count or column-width, and control spacing and dividers with column-gap and column-rule. Use break-inside: avoid selectively for items that should stay together, while recognizing that content may not fit perfectly in every column.
5. Use floats when text should wrap around an item
A float moves an item to one side so surrounding inline content, such as text, wraps around it. This remains useful for a small image or note alongside prose. Floats were once used to build page columns, but Grid and Flexbox are the relevant tools for structured page layouts.
<figure class="article-image">
<img src="map.jpg" alt="A map of the route">
<figcaption>The route through the valley.</figcaption>
</figure>
<p>This paragraph flows beside the image and continues below it when the floated box ends.</p>
.article-image {
float: inline-start;
max-inline-size: min(40%, 18rem);
margin: 0 1rem 1rem 0;
}
.article-image img {
display: block;
max-inline-size: 100%;
block-size: auto;
}
Floats affect surrounding text and can make later content wrap unexpectedly. Clear a float when following content should begin below it, or use a containing block that establishes a new formatting context where appropriate. Do not use floats as a substitute for a grid of cards or a site-wide column system.
6. Use positioning for deliberate placement
position: static is the default. Relative positioning keeps an element in normal flow and lets you offset its painted position. Absolute, fixed, and sticky positioning have different flow behavior and should be used when the placement is intentional.
| Value | Flow behavior | Typical use |
|---|---|---|
static |
Remains in normal flow | Default content layout |
relative |
Remains in flow; can be offset | A containing reference for an absolutely positioned child |
absolute |
Removed from normal flow | An icon or badge placed within a positioned component |
fixed |
Removed from normal flow; placed relative to the viewport | A viewport-attached control |
sticky |
Participates in flow until its scroll threshold, then sticks within its scrolling context | A section header or navigation element during scrolling |
.card {
position: relative;
}
.card-badge {
position: absolute;
inset-block-start: 0.75rem;
inset-inline-end: 0.75rem;
}
.help-button {
position: fixed;
inset-inline-end: 1rem;
inset-block-end: 1rem;
}
The card establishes a containing block for its badge. An absolutely positioned element does not reserve its usual space, so it can overlap other content. Fixed and sticky placement also need care on small screens and around scroll containers. Use logical inset properties when layouts should adapt to writing direction.
7. Make layouts responsive
Responsive design is a set of practices for adapting a page to its viewing conditions, including available width and resolution. It is not itself a layout mode. Flexible tracks can adapt naturally; media queries can change the arrangement when content needs it.
.page {
width: min(100% - 2rem, 72rem);
margin-inline: auto;
}
.layout {
display: grid;
grid-template-columns: minmax(0, 2fr) minmax(14rem, 1fr);
gap: 2rem;
}
@media (max-width: 48rem) {
.layout {
grid-template-columns: 1fr;
}
}
The breakpoint is an example; choose it based on when the actual content becomes cramped. minmax(0, ...) allows a grid track to shrink below its content’s automatic minimum, which can help prevent long content from forcing overflow. Also check long words, large images, and embedded content at narrow widths. Media queries can target other conditions, including resolution, where that is relevant.
8. Choose a method by content behavior
| Need | Use | Reason |
|---|---|---|
| Text and blocks in a readable sequence | Normal flow | It is the browser’s natural document layout |
| One row or one column of related items | Flexbox | It aligns and distributes along one axis |
| Items coordinated across rows and columns | Grid | It handles two-dimensional tracks and placement |
| One prose stream flowing between columns | Multi-column | Content continues from column to column |
| Text wrapping around an image or item | Float | That is the float behavior |
| Overlay, viewport attachment, or deliberate offset | Positioning | Placement can be controlled independently of normal flow |
First ask whether the content is a continuous stream or independent items. Then ask whether items need to align along one axis or two. Finally, decide whether placement should adapt naturally or be explicitly anchored. These questions usually identify the simplest suitable method.
9. Build and inspect a page layout
- Write semantic HTML in the order a reader should encounter it.
- Let normal flow establish a sensible narrow-screen baseline.
- Use Flexbox or Grid for the structural relationship each group needs.
- Add responsive changes where the content or available width calls for them.
- Check for overflow, overlap, and source order that differs from visual order.
- Inspect the rendered page at the viewport sizes and states that matter to your users.
For a repeatable visual check, render the page at a chosen viewport and compare the resulting image as the CSS changes. A screenshot can reveal unexpected wrapping, spacing, clipping, and overlap that are easy to miss when inspecting the stylesheet alone.
10. Or skip the browser setup
If you need a screenshot for reviewing a page layout, ScreenshotNeo can render a URL with one GET request. See the ScreenshotNeo API documentation for request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for AI agents. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Sign up for free ScreenshotNeo screenshots.
11. Troubleshooting common layout problems
| Symptom | Likely cause | Fix |
|---|---|---|
| Flex items overflow or become unexpectedly narrow | The row has more minimum content width than available space, or items cannot wrap | Allow wrapping, adjust flex sizing, and check long unbroken content |
| Grid content forces the page wider than the viewport | A track or item has a large intrinsic minimum size | Try minmax(0, ...) for the track and constrain images and long content |
| Card actions do not line up | Cards have different content lengths and no internal layout strategy | Make each card a column flex container and use margin-top: auto on the action |
| An absolute badge covers text | Absolute positioning does not reserve space | Keep the badge small, add room in the component, or place it in normal flow |
| Sticky content does not stick | No inset threshold is set, or a scroll ancestor constrains the sticky element | Set an inset such as top: 0 and inspect the relevant scrolling container |
| Text wraps around content farther than intended | A float remains active for following content | Clear the float or establish an appropriate new formatting context |
| Multi-column content splits awkwardly | Items are taller than a column or break rules prevent balancing | Adjust column width/count and apply break avoidance only to items that should stay together |
| Mobile layout looks cramped despite a breakpoint | The breakpoint does not match the content, or fixed minimum widths remain | Reduce minimum track sizes, inspect real content, and move the breakpoint to the point where the layout needs to change |
12. Performance, reliability, and maintenance
For ordinary page layouts, choose the method that expresses the content relationship directly; the cited layout guidance does not establish a performance ranking among these methods. Avoid complicated positioning when flow or tracks can describe the structure. Test with realistic content because intrinsic sizes, late-loading images, and long labels can change wrapping and dimensions. Keep document order meaningful so content remains understandable when styling or viewport conditions differ.
For visual checks, capture the same URL with consistent viewport and page state so changes are comparable. Dynamic content, consent dialogs, animations, and delayed images can make screenshots differ between captures; decide which state your review should represent and wait for the relevant content before capturing.
13. Frequently asked questions
Can Flexbox and Grid be used together?
Yes. For example, Grid can place cards in rows and columns, while Flexbox arranges the contents within each card.
Is multi-column layout another name for Grid?
No. Multi-column layout flows one continuous stream through columns. Grid places items into tracks and cells.
Should I use floats to build a page sidebar?
Use Grid or Flexbox for a structured sidebar and main-content layout. A float is appropriate when text should wrap around an item.
Do I need a media query for every responsive layout?
No. Flexible tracks can respond to available width on their own. Add a media query when the content needs a different arrangement under a condition.
Why keep normal flow as the starting point?
It gives the content a readable order and useful baseline before more specific layout rules are applied.


