Responsive web design stopped being a "nice to have" a long time ago, but the way we actually build it has changed more in the last two years than in the previous eight combined. Media queries used to be the whole story. Now fluid grids, dynamic media handling, and CSS container queries have quietly rewritten the rulebook.
To get past the theory and into how this actually plays out in production code, I sat down with a frontend engineer who has spent the better part of a decade shipping layouts for everything from marketing sites to dashboard products. What follows is that conversation, lightly edited for length.
Q: Let's start broad. When people hear "responsive web design" in 2026, what do they usually get wrong?
They think it means "the site works on phones." That's the 2013 definition. Responsive web design today means your layout can survive being dropped into a context you didn't predict: a sidebar widget, a split screen on a foldable, a car dashboard, a component embedded in someone else's CMS. The viewport isn't the only thing that changes size anymore. The container around your component changes size too, and your CSS needs to respond to both.
I keep a small internal checklist for new components, and "does this survive an unknown container width" is item number one. It sounds simple until you've debugged a card component that looked perfect at every breakpoint and then broke the moment a product manager dropped it into a two column layout.
Q: Fluid grids are supposed to solve part of that. What's actually wrong with the old fixed breakpoint approach?
Fixed breakpoints assume you know every screen size your users will show up with. You don't. You pick 320, 768, 1024, 1440, whatever, and you write a media query for each one. Then a new device ships with a weird width, or someone resizes their browser to exactly the wrong pixel, and you get an awkward gap or an overflow you never tested for.
A fluid grid doesn't ask "which bucket does this screen fall into." It asks "how many columns can comfortably fit right now, and how should they share the available space." That's a completely different mental model, and once it clicks, you stop writing nearly as many media queries.
Q: Can you show what a fluid grid actually looks like in code?
Sure. Here's a card grid that adapts without a single breakpoint:
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
gap: clamp(0.75rem, 2vw, 1.5rem);
}
auto-fit tells the grid to fit as many columns as possible. minmax(min(100%, 16rem), 1fr) says each column should be at least 16rem wide, unless the container itself is narrower than that, in which case shrink to fit. That inner min() is the part most tutorials skip, and it's the difference between a grid that gracefully collapses to one column on small screens and one that causes horizontal scrolling.
The gap uses clamp() too, so spacing scales gently between a minimum and maximum instead of jumping at arbitrary breakpoints.
Q: What about typography and spacing? Do you use clamp() everywhere, or is that overkill?
Not everywhere, but for anything that sits along a visual hierarchy, yes. Headlines, section spacing, hero padding. Here's a type scale I've reused across a handful of projects:
:root {
--step-0: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
--step-1: clamp(1.25rem, 1.1rem + 0.75vw, 1.5rem);
--step-2: clamp(1.75rem, 1.4rem + 1.75vw, 2.5rem);
--step-3: clamp(2.25rem, 1.6rem + 3vw, 3.5rem);
}
h1 { font-size: var(--step-3); }
h2 { font-size: var(--step-2); }
p { font-size: var(--step-0); }
The middle value in each clamp is the fluid part, a mix of a fixed rem value and a viewport unit. That combination is what makes the growth feel proportional instead of linear or jumpy. I'd rather spend twenty minutes tuning these values once per project than write six font size overrides across six media queries.
Q: Let's move to dynamic media. Images and video are usually where responsive layouts fall apart. What's your process?
Images first, because they're the more common offender. The old habit of shipping one large image and letting CSS scale it down is still shockingly common, and it's a waste of bandwidth on smaller screens. The right approach uses srcset and sizes so the browser picks the most appropriate file:
<img
src="hero-800.jpg"
srcset="hero-480.jpg 480w, hero-800.jpg 800w, hero-1200.jpg 1200w, hero-1600.jpg 1600w"
sizes="(max-width: 600px) 100vw, (max-width: 1200px) 80vw, 1200px"
alt="Product dashboard overview"
loading="lazy"
>
The browser does the math. It knows the device pixel density, it knows the layout width from the sizes attribute, and it picks the smallest file that still looks sharp. Nobody has to write JavaScript for that.
When I need different crops, not just different resolutions, that's when <picture> comes in:
<picture>
<source media="(max-width: 640px)" srcset="banner-square.jpg">
<source media="(min-width: 641px)" srcset="banner-wide.jpg">
<img src="banner-wide.jpg" alt="Autumn sale banner">
</picture>
That's the distinction I see people miss constantly. srcset alone is for resolution switching. <picture> is for art direction, when the actual composition of the image needs to change, not just its size.
Q: What about video and embeds? Those seem harder to make fluid.
They used to be a nightmare before aspect-ratio shipped. People built padding hacks with a percentage top padding trick just to keep a 16 by 9 video from collapsing. Now it's one line:
.video-wrapper {
aspect-ratio: 16 / 9;
width: 100%;
}
.video-wrapper iframe {
width: 100%;
height: 100%;
}
No hack, no pseudo element, no JavaScript resize listener. The browser reserves the correct space immediately, which also helps your layout shift score, since the video area doesn't jump around while the iframe loads.
Q: Now for the part everyone's asking about. Why are container queries such a big deal, and how are they different from media queries?
Media queries only ever knew one thing: the size of the viewport. That's fine for page level layout decisions, but it falls apart the moment you build reusable components. A card component might live in a full width hero section on one page and a narrow sidebar on another. With media queries, that card has no idea which context it's in. It only knows the browser window is, say, 1400 pixels wide, even though the card itself might only have 280 pixels of actual space.
Container queries fix that by letting an element respond to the size of its own parent container instead of the whole viewport. It's a genuinely different axis of responsiveness, and it's the missing piece that finally makes component level responsive web design possible instead of just page level.
Here's the setup:
.card-container {
container-type: inline-size;
container-name: card;
}
@container card (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 120px 1fr;
gap: 1rem;
}
}
@container card (max-width: 399px) {
.card {
display: flex;
flex-direction: column;
}
}
That same card component now rearranges itself based on the space it's actually given, whether that's a full page section or a cramped sidebar slot. It doesn't care what the browser window is doing.
Q: Any gotchas people run into when adopting container queries for the first time?
A few. First, you have to explicitly declare container-type on the parent, or the query does nothing. People forget that step constantly and then assume container queries are broken.
Second, container-type: inline-size only lets you query width, which covers the vast majority of real use cases, but if you need both dimensions you're looking at container-type: size, and that comes with layout containment side effects worth reading up on before you reach for it.
Third, browser support caught up faster than most people realize. All major evergreen browsers, Chrome, Edge, Firefox, and Safari, have supported container queries since around 2023, so this isn't a bleeding edge feature you need a polyfill for anymore. I still run a quick check against caniuse before I ship anything unusual, just as a habit, but for basic inline-size queries there's nothing to worry about at this point.
Q: How do fluid grids, dynamic media, and container queries actually work together in a real component?
That's the fun part, honestly, because none of them work in isolation on a real project. Let me walk through a product card, since it's the component I've rebuilt the most across different jobs.
.product-card {
container-type: inline-size;
display: grid;
gap: clamp(0.5rem, 1.5vw, 1rem);
padding: clamp(0.75rem, 2vw, 1.25rem);
border-radius: 0.75rem;
}
.product-card img {
width: 100%;
aspect-ratio: 4 / 3;
object-fit: cover;
border-radius: 0.5rem;
}
.product-card__title {
font-size: clamp(1rem, 0.9rem + 0.5vw, 1.25rem);
}
@container (min-width: 320px) {
.product-card {
grid-template-columns: 100px 1fr;
align-items: center;
}
.product-card img {
aspect-ratio: 1 / 1;
}
}
The fluid grid handles the page level arrangement of however many cards fit. The clamp() values keep spacing and type proportional without new breakpoints. The aspect-ratio locks the image slot so nothing jumps while it loads. And the container query lets the card itself flip from a stacked layout to a side by side layout depending on how much room its parent actually gives it, independent of the browser width. Four techniques, one component, zero traditional media queries.
Q: Performance always comes up with responsive design. What should developers watch for?
Cumulative layout shift is the big one. If you don't reserve space for images and video ahead of time, using aspect-ratio or explicit width and height attributes, the page jumps around as content loads, and that tanks your Core Web Vitals. It also just feels bad to use.
The other thing is over-fetching. If your srcset only offers two sizes, a phone might still download an image sized for a desktop monitor. I try to offer at least three or four steps in the srcset for any hero or above the fold image, since bandwidth on mobile networks is still nowhere near desktop speeds in a lot of regions.
And with container queries specifically, container-type: size forces layout containment on that element, which can affect how its content is measured. It's not a performance killer, but it's not free either, so I reach for inline-size unless I have a real reason not to.
Q: How do you actually test all of this before shipping?
Browser devtools responsive mode gets you most of the way, but I don't fully trust a layout until I've seen it on real hardware. Our team keeps a small device lab, nothing fancy, just a shelf of older phones and tablets people have donated, and at this point it covers something like 18 distinct screen sizes and pixel densities. That number matters more than it sounds like it should, because emulators are good at simulating width but bad at simulating things like actual touch target sizing, real world network throttling, and font rendering quirks on older operating systems.
For container queries specifically, I resize a browser panel or a CMS preview pane instead of resizing the whole window, since that's the scenario the feature was built for. It catches a different class of bug than viewport resizing does.
Q: Where does accessibility fit into all of this? It feels like a separate topic, but I assume it isn't.
It's not separate at all, it's part of the same job. A layout that looks flawless at every screen size but has 12 pixel tap targets on mobile isn't actually responsive, it's just decorative. WCAG's target size guidance calls for interactive elements to be at least 24 by 24 CSS pixels, and that number becomes a lot easier to hit once your spacing is built with clamp() instead of fixed values, because padding naturally grows on smaller, touch-first viewports instead of shrinking.
Text zoom is the other piece people forget. Someone might set their browser text size to 200 percent, and if your layout was built entirely with fixed pixel heights, things overlap or clip. Fluid grids and clamp() based type scales tend to survive that kind of zoom far better than fixed layouts, since nothing is locked to an exact pixel value in the first place. I treat a 200 percent zoom test as a normal part of QA now, right alongside checking narrow viewports, and it catches a surprising number of layout bugs that a plain resize test misses entirely.
Q: Any final advice for someone getting serious about responsive web design this year?
Stop thinking in fixed breakpoints as your default. Start every component by asking what its minimum and maximum reasonable size is, then use clamp(), fluid grid tracks, and container queries to fill that range naturally. Reserve media queries for genuine page level layout shifts, like switching from a single column article layout to a two column layout with a sidebar. Everything else, spacing, type, card layouts, image sizing, can usually be handled with the fluid and container based tools we've talked about.
The other piece of advice, and it sounds unglamorous, is to actually read the specs and the MDN documentation instead of only skimming blog posts. A lot of the confusion around container queries in particular comes from people copying snippets without understanding why container-type has to be declared, or why inline-size behaves differently from size. Fifteen minutes with the actual documentation saves hours of debugging later.
Frequently Asked Questions
What is the difference between responsive web design and adaptive design?
Responsive design uses fluid grids, flexible media, and CSS breakpoints so a single layout continuously adjusts to any screen size. Adaptive design instead serves a small number of fixed layouts chosen based on detected device categories. Responsive is generally preferred today because it scales smoothly rather than jumping between discrete templates. See MDN's guide on responsive images: https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Responsive_images
Do I still need media queries if I use container queries?
Yes. Container queries handle component level responsiveness, how an individual element reacts to its parent's size, while media queries remain the right tool for page level decisions such as overall layout structure, navigation patterns, or print styles. See web.dev's container query guide: https://web.dev/learn/css/container-queries
Is CSS clamp() supported in all major browsers?
Yes, clamp() has been supported across Chrome, Firefox, Safari, and Edge for several years now, making it safe to use in production for fluid typography and spacing without fallback code. See "How to use container queries now" on web.dev for related browser support notes: https://web.dev/blog/how-to-use-container-queries-now
What does container-type: inline-size actually do?
It tells the browser that an element should establish size containment along the inline axis, which is width in most writing modes, so its descendants can query that container's width with @container rules. Without declaring it, container queries on descendants simply won't fire. See MDN's picture element reference for related layout containment context: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/picture
How many breakpoints should a responsive layout have?
There is no fixed number that works for every project. Rather than counting breakpoints, focus on the points where your specific content actually starts to look cramped or overly spaced out, and use fluid techniques like clamp() and auto-fit grids to minimize how many hard breakpoints you need in the first place. See Smashing Magazine's grid layout guidance: https://www.smashingmagazine.com/2018/04/best-practices-grid-layout/
What is the minimum touch target size for accessible responsive design?
WCAG 2.5.8 recommends interactive elements be at least 24 by 24 CSS pixels, with additional spacing if a target is smaller than that. See Smashing Magazine's accessible tap target size cheatsheet: https://www.smashingmagazine.com/2023/04/accessible-tap-target-sizes-rage-taps-clicks/
References
- MDN Web Docs, "Using responsive images": https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Responsive_images
- MDN Web Docs, "picture: The Picture element": https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/picture
- web.dev, "Container queries": https://web.dev/learn/css/container-queries
- web.dev, "How to use container queries now": https://web.dev/blog/how-to-use-container-queries-now
- web.dev, "Container queries land in stable browsers": https://web.dev/blog/cq-stable
- CSS-Tricks, "A Complete Guide to CSS Flexbox": https://css-tricks.com/snippets/css/a-guide-to-flexbox/
- Smashing Magazine, "Best Practices With CSS Grid Layout": https://www.smashingmagazine.com/2018/04/best-practices-grid-layout/
- Smashing Magazine, "Accessible Tap Target Sizes Cheatsheet": https://www.smashingmagazine.com/2023/04/accessible-tap-target-sizes-rage-taps-clicks/
- MetaGo, "Architecting for Scale: Building a Resilient Frontend with Design Systems":https://metago.net/enterprise-frontend-architecture-design-systems/
Top comments (0)