CSS Modern Layouts: What Actually Clicked for Me
Addressing the most common points of confusion regarding CSS Grid, Flexbox, Container Queries, and logical properties.

Flexbox handles about 70% of what I build. Grid covers another 25%. The remaining 5% is some edge case involving position: sticky behaving wrong inside a scroll container, and that's where the afternoon goes.
Eight years of writing CSS and this is roughly where my head is at. Not a complete picture โ there's subgrid, container queries, has(), anchor positioning, and a pile of newer specs sitting in my "I should learn this properly" queue. But for day-to-day work, the mental model above is close to accurate. Flexbox for most things. Grid for the rest. Stack Overflow for the weird ones.
What follows is less of a tutorial and more of a field report. Where I reach for what, where things clicked, and a few spots where they still haven't.
Flexbox Remains the Default
Grid gets the conference talks. Every "modern CSS" article leads with Grid. Fair enough โ Grid is impressive, and I'll get to it. But on real projects, Monday through Friday, Flexbox does the heavy lifting.
Nav bars. Button groups. Cards sitting next to each other in a row. A sidebar layout with one fixed column and one that fills whatever's left. Stacking elements vertically on mobile. All of that is Flexbox. Single-axis problems, and most layout problems on a typical page are single-axis.
What took a while to properly internalize: Flexbox is content-driven. You hand items some rules โ grow, shrink, basis โ and the browser distributes space based on what's inside them. Grid works differently. Grid says "here's the structure, now place things into it." Neither approach is superior. They solve different shapes of problems. But my sense is that a lot of developers reach for Grid too early because it seems more modern, then spend an hour fighting it when Flexbox would have taken three minutes.
One note. If you're still writing float: left for layout purposes in 2026 โ and I've seen this in recent PRs, so this isn't hypothetical โ please stop. Not saying it harshly. Floats were designed for wrapping text around images. That we used them for page layout for a decade was a collective improvisation born from having no better option. Flexbox replaced that need years ago.
Where Grid Earns Its Place
Two use cases. Both strong enough that I wouldn't want to go back.
First: full page skeletons. Header, sidebar, main content, footer. Grid makes this almost absurdly readable:
.page-layout {
display: grid;
grid-template-columns: 250px 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
min-height: 100vh;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }
Named areas. You're drawing the layout in your code. Show this to someone who's been doing CSS for six months and their eyes light up, because compared to the old ways โ the clearfix hacks, the negative margin tricks, the "why is this div 1px too tall" debugging sessions โ this is almost embarrassingly simple. The layout is right there in the template string. Readable by anyone who can read a grid on paper.
Second use case: responsive card grids. The auto-fill plus minmax() pattern. Probably the single most useful CSS trick I've picked up in five years:
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: clamp(1rem, 2vw, 2rem);
}
No media queries. Grid figures out how many columns fit. Viewport narrows below two columns? Drops to one. Wide enough for four? Uses four. Picked this up from a CSS-Tricks article ages ago and it's showed up on nearly every project since.
Outside of these two โ page skeletons and auto-flowing grids โ Grid doesn't get much use from me. Some people use it for everything. Fine. For my workflow, Flexbox is the default and Grid is what comes out when the problem needs two-dimensional control. Your ratio might differ. The toolbox is the same either way.
Centering Is Solved. Move On.
Centering a div used to be a meme. A genuine source of developer suffering for years. The position: absolute plus transform: translate(-50%, -50%) trick. Table cells. Negative margins. All of it was terrible.
Now:
.centered {
display: grid;
place-items: center;
min-height: 100vh;
}
Or:
.centered {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
Either works. I tend toward the Grid version โ fewer properties โ but it doesn't matter. Pick one. The centering problem is done. Has been done for a while. If someone asks this in a 2026 interview, the correct response is one of the above, plus maybe a raised eyebrow about why they're still asking.
clamp() Changed How I Write Spacing and Type
This function had a bigger impact on my CSS habits than almost any other recent addition. Set a minimum, a preferred value that scales with the viewport, and a maximum. Browser picks whatever falls in range.
Font sizes:
h1 {
font-size: clamp(1.75rem, 4vw + 0.5rem, 3.5rem);
}
Spacing:
.section {
padding: clamp(2rem, 5vw, 6rem) clamp(1rem, 3vw, 4rem);
}
Before clamp(), I had breakpoints just for font size adjustments. Three or four media queries so headings would scale down on mobile. Now it's one line. Font scales smoothly between min and max as the viewport changes. No jumps. No breakpoints for something this routine.
One gotcha worth flagging. The middle value โ the preferred one โ needs a viewport-relative unit in it, or the whole function does nothing interesting. Seen people write clamp(1rem, 1.5rem, 2rem) and wonder why it's always 1.5rem. Right. Nothing responsive in that expression. You need vw or vi or something that actually changes when the window resizes.
Container Queries: Skepticism Converted
Was skeptical of container queries for a while. Read the spec discussions in 2022 or 2023 and thought "how often do I really need this?" Turns out: fairly often, once they're available.
Instead of a component responding to the viewport width (media queries), it responds to the width of its own parent container. A card component can switch from vertical to horizontal layout based on how much space it's been given โ doesn't matter what the screen size is.
.card-wrapper {
container-type: inline-size;
container-name: card;
}
@container card (min-width: 500px) {
.card {
display: flex;
flex-direction: row;
gap: 1.5rem;
}
.card-image {
flex: 0 0 200px;
}
}
@container card (max-width: 499px) {
.card {
display: flex;
flex-direction: column;
}
.card-image {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}
}
This matters because reusable components end up in all kinds of contexts. A card might sit in a three-column grid, a sidebar, a full-width hero section. With media queries, you'd need to know ahead of time what viewport widths map to what parent widths โ and that breaks the moment someone rearranges the page layout. Container queries sidestep that entirely. The component just looks at its own surroundings.
Browser support crossed 90% back in 2024. No reason to avoid them in production now. Mental model adjustment took a minute, though. You're used to thinking "when the screen is narrow, do this." Container queries flip it to "when this component's container is narrow, do this." Subtle shift. Tripped me up the first few times.
The Min-Width Overflow Trap
This one catches people constantly. Flexbox layout looks fine, then someone drops a long URL or an unbreakable string into one of the flex children, and it punches straight through the container edge. Horizontal scrollbar appears. Layout busted.
The cause: flex items default to min-width: auto, meaning they refuse to shrink below the size of their content. A long unbreakable string counts as content. So the item just... doesn't shrink. Even if you told it to.
Fix:
.flex-child {
min-width: 0;
}
That's it. Tells the flex item "yes, you're allowed to shrink below your content size." Pair with overflow: hidden or text-overflow: ellipsis and the problem goes away.
Spent an embarrassing amount of time debugging this on a project before figuring out what was happening. Now min-width: 0 gets added almost reflexively to flex children that hold text. Some people apply it as a universal reset to every flex child. Not quite that aggressive about it myself. But the impulse makes sense.
Logical Properties โ Know I Should, Don't Always
Going to be straight about this. Logical properties are something I know I should use consistently. Still don't.
The concept: instead of margin-left, you write margin-inline-start. Instead of padding-top, padding-block-start. The logical version adapts automatically to the writing direction. English (left-to-right) โ margin-inline-start is the left margin. Arabic or Hebrew (right-to-left) โ it flips to the right margin. No extra work needed.
Building something that might ever support RTL languages? Using logical properties from the start saves you from an annoying refactor later. Know this from experience โ did the annoying refactor on a project that didn't plan ahead. Not something I'd volunteer for again.
And yet my fingers still type padding-left about half the time. Muscle memory runs deep. Added a stylelint rule to catch it, which helps. Ongoing process.
Part of the resistance is the naming. padding-inline-start is a lot of characters compared to padding-left. When you write it dozens of times a day, the verbosity grates. Weak excuse, I know. The shorthand versions help โ padding-inline sets both start and end, margin-block handles top and bottom (or block-start and block-end, I should say). Those are less painful.
New projects: trying to go all-in on logical properties. Existing codebases: introducing them gradually where I'm touching files anyway. Not going to rewrite an entire stylesheet just for this. Using logical properties also plays well with web accessibility โ one of those foundational decisions that makes internationalization and assistive technology support easier down the road.
Subgrid: The Thing I Haven't Shipped Yet
Being upfront. Subgrid hasn't made it into a production project from my end. Built demos. Played with it on CodePen. Haven't shipped anything real.
The problem it solves: you have a grid, and inside one of its cells, you want a child element to align to the parent grid's tracks. Cards where the title, description, and footer need to line up across a row โ even when content lengths differ.
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 2rem;
}
.card {
display: grid;
grid-template-rows: subgrid;
grid-row: span 3; /* card takes up 3 rows of the parent */
}
Without subgrid, each card runs its own internal grid. Rows don't align between cards. Title in card one might be taller than the title in card two, pushing descriptions to different starting heights. Subgrid forces the child to participate in the parent's row tracks. Everything lines up.
This feels like it'll be useful once the muscle memory develops. The alignment problem it addresses is one I've hacked around with fixed heights and min-height values for years. Firefox had subgrid for ages before Chrome caught up. Chrome shipped it late 2023, Safari followed. Support is solid now. Just need to start using it on real work instead of sandboxes.
What a New Project Actually Looks Like
When a project starts, the layout approach usually shakes out like this. Page skeleton gets Grid โ the header, sidebar, main content, footer structure. Everything inside those regions is mostly Flexbox. Card grids and anything needing two-dimensional auto-flow gets Grid with the auto-fill / minmax() pattern.
clamp() goes everywhere for spacing and typography. Logical properties get used where I remember to use them (working on it). Container queries show up for any component that lives in multiple layout contexts.
That covers roughly 95% of what comes up. The remaining 5% is usually something weird with absolute positioning or a sticky element misbehaving inside an overflow container, and that's when forty-five minutes disappears into Stack Overflow and MDN tabs.
CSS frameworks for layout aren't part of my setup anymore. Used Bootstrap's grid system for years. Foundation before that. Both were necessary when CSS layout was painful. CSS layout isn't painful now. The native tools are good enough. Better than good enough. I'd rather write fifteen lines of Grid than import a framework.
Tailwind is a separate conversation โ I use it sometimes, but not for its layout utilities specifically. The layout classes in Tailwind are thin wrappers around the same Flexbox and Grid properties you'd write yourself. For the full comparison of the Tailwind approach versus writing your own CSS, I went into that decision in my Tailwind vs CSS Modules post.
Things I Keep Hearing About But Haven't Internalized
has() for layout. People talk about styling a grid differently based on whether one of its children has a certain class or state. The selector is powerful โ used it for form styling โ but for layout specifically, haven't found a case where it felt better than a modifier class or container query. Maybe I'm not thinking about it right. Possible.
@scope. Scoped styles where you limit a CSS block to a specific DOM subtree. Read the spec. Read articles. Understand what it does. Don't understand when I'd use it over just writing a class name. CSS Modules and framework scoping already handle naming collisions. What does @scope add on top? Asking because I don't know yet, not because the answer is nothing.
Anchor positioning. Tethering one element's position to another element, natively in CSS, no JavaScript. Tooltips, popovers, dropdowns. The demos look great. Haven't tried building anything real with it, and I'm curious about edge cases โ viewport boundaries, scrolling containers, the stuff that Floating UI's JavaScript handles with years of battle-testing. The CSS version would need to be at least as reliable for me to make the switch.
The Gap Between What Exists and What Gets Used
There's always a lag between CSS shipping a feature and developers adopting it. Container queries have been stable for over a year. Every project I start, I intend to use them from the beginning. About half the time, I end up falling back to media queries out of habit and only switch to container queries when something breaks because the component got placed in a context I didn't anticipate.
Subgrid has been available across browsers since late 2023. Still haven't shipped it. Logical properties have been stable for years. Still type padding-left when I'm not thinking about it.
The problem isn't knowledge. It's muscle memory. CSS is one of those technologies where you build patterns over years and those patterns become reflexive. When you're in flow, writing layout code quickly, your fingers produce what they've always produced. The new way has to fight against the old way, and the old way has the advantage of being automatic.
What helps: linting rules that flag the old patterns. Pair programming where someone else catches the habit. Starting a project with a CSS reset that uses logical properties by default so the baseline is already set correctly. Making the new way the path of least resistance instead of relying on willpower to override muscle memory.
CSS keeps moving. Directions I mostly like. Layout is better now than it has ever been. But there's always this sense of being slightly behind โ knowing some technique exists that would clean up code from six months ago if I sat down and learned it properly. Keep meaning to spend a weekend with subgrid and anchor positioning. Haven't gotten around to it. Maybe next month. Probably not next month. Eventually.
Further Resources
- MDN CSS Grid Layout โ Mozilla's comprehensive guide to CSS Grid including grid areas, auto-placement, subgrid, and alignment.
- web.dev: Learn CSS โ Google's structured course covering modern CSS features including Flexbox, Grid, container queries, and logical properties.
- CSS-Tricks: A Complete Guide to Flexbox โ The widely referenced visual guide to Flexbox properties with interactive examples for both parent and child elements.
Written by
Anurag Sinha
Full-stack developer specializing in React, Next.js, cloud infrastructure, and AI. Writing about web development, DevOps, and the tools I actually use in production.
Stay Updated
New articles and tutorials sent to your inbox. No spam, no fluff, unsubscribe whenever.
I send one email per week, max. Usually less.
Comments
Loading comments...
Related Articles

Tailwind vs CSS Modules: What We Ended Up Doing
How our team debated and resolved the Tailwind vs CSS Modules question. We didn't pick just one.

WebAssembly Demystified โ It's Not Just 'Fast JavaScript'
What WebAssembly actually is under the hood, why calling it fast JavaScript misses the point, and the Rust-to-WASM pipeline I use in real projects.

HTTP/3 and QUIC โ Why HTTP/2 Wasn't the Final Answer
The protocol running a third of the web that most developers haven't thought about. Connection migration, 0-RTT handshakes, and why switching from TCP to UDP was the only way forward.