Skip to content

Why Three Breakpoints Broke My Card in a Narrow Sidebar

Container queries beat fixed breakpoints for real component design. Here is a practical strategy for building layouts that respond to their container.

· · 5 min read
Laptop, tablet, and phone arranged on a desk showing the range of screen sizes a responsive layout must support

I built a dashboard card component in 2024 with the standard three breakpoints, mobile, tablet, desktop. Then a client dropped that exact card into a narrow sidebar widget, and it broke instantly. Text overflowed, the icon overlapped the label, the whole thing looked like a bug report screenshot. The viewport was desktop-sized. The card just wasn't.

That's the blind spot in fixed breakpoint thinking: it assumes a component's context matches the browser window. It rarely does anymore, not with dashboards, CMS blocks, and widgets that get dropped into wildly different layouts on the same page.

TL;DR: Fixed viewport breakpoints can't account for a component's actual rendered width. Container queries fix that: 41% of developers have used them at least once, up from awareness of 81% in 2024 to 86% in 2025 (State of CSS 2025), and browser support has been baseline since 2023.

Photo by Tai Bui on Unsplash

Why Do Fixed Breakpoints Keep Breaking on Real Projects?

Because a fixed breakpoint asks the wrong question. It asks "how wide is the browser window?" when the question that actually matters is "how wide is this specific component right now?" A card in a full-width hero section and the same card in a 280px sidebar live at completely different effective widths, on the exact same device, in the exact same browser session. Mobile-first design solved the small-screen-up ordering problem years ago. It never solved the reusable-component problem, because that problem didn't really exist until component libraries and CMS-driven layouts became the default way teams build.

I used to work around this with JavaScript resize observers, manually toggling classes based on an element's getBoundingClientRect() width. It worked. It was also fragile, added a layout thrash risk, and meant every component carried its own bespoke measurement logic. Nobody wants to maintain that at scale.

What Do Container Queries Actually Fix?

Container queries let a component respond to the width of its containing element instead of the viewport. You mark a wrapper with container-type: inline-size, name it if you want to target it specifically, then write @container rules that fire based on that container's rendered width.

.card-wrapper {
  container-type: inline-size;
  container-name: card;
}

@container card (min-width: 320px) {
  .card {
    display: grid;
    grid-template-columns: 96px 1fr;
  }
}

@container card (max-width: 319px) {
  .card {
    display: flex;
    flex-direction: column;
  }
}

Drop that same .card into a sidebar, a modal, or a full grid column, and it adapts on its own. No JavaScript, no per-page overrides, one rule that travels with the component wherever it gets used.

Media queriesContainer queries
Responds toBrowser viewportContaining element's width
OwnsPage-level layout shape, sidebar presenceComponent-level adaptation
Best forOverall structureReusable components (cards, nav items, form rows)

Should You Rewrite Your Whole Breakpoint System Right Now?

No, and I'd push back hard on anyone telling you to. Media queries still do the job at the page level, layout shape, navigation pattern (the mobile navigation patterns guide covers the tab-bar versus drawer call), whether a sidebar exists at all. Rip those out and replace them with container queries and you've just made your codebase harder to reason about for no real gain. The right split is boring: page-level structural decisions stay in media queries, component-level adaptation moves to container queries. Most teams I've consulted for get the best results by migrating their three or four most-reused components first, cards, navigation items, form rows, and leaving everything else alone until there's a real reason to touch it.

Photo by Team Nocoloco on Unsplash

How Do You Actually Plan a Breakpoint Strategy for a New Project?

Start at the page level with four viewport breakpoints, not eleven. Mobile at 375px, tablet at 768px, desktop at 1280px, and an optional wide catch above 1920px if your audience actually has ultra-wide monitors, check your analytics before you assume they do. That's the outer frame. Then, for anything that gets reused, a card, a stat block, or the tiers of a pricing page layout, wrap it in a container and let it manage its own internal layout with @container rules.

If you're building the numeric side of this, our free spacing scale generator produces a consistent token set for gaps and padding that holds up across both breakpoint types, so your container-query components and your page-level layout share the same rhythm instead of drifting apart. Container query length units (cqi, cqb) let font sizes and padding scale with the container too, which pairs naturally with the fluid typography clamp() approach most modern sites already use for page-level type. To see the resulting clamp() curve before you commit numbers, the clamp preview tool plots min, preferred and max across every viewport width.

Fixed breakpoints aren't dead, they're just no longer the whole strategy. Treat the viewport and the component as two separate, related problems, and stop trying to solve both with one set of numbers.

Frequently Asked Questions

Do container queries replace media queries entirely?
No, and I'd be skeptical of anyone who tells you they do. Media queries still own the page-level decisions: overall layout shape, whether a sidebar exists at all, font-size scaling across the full viewport. Container queries take over once you're inside a component that gets reused in different contexts, a card in a three-column grid on desktop and a full-width stack in a sidebar. I run both on every serious project now. Media queries set the stage, container queries handle what happens inside it. Treating them as competitors misses the point of either one.
What is the performance cost of container queries?
Lower than most people assume. Container queries require the browser to establish containment on a wrapping element with `container-type`, which does add a small layout cost, but it's nowhere near the cost of the resize-observer JavaScript polyfills teams used before native support landed. On a component library I rebuilt last year, swapping a JS-based container query polyfill for the native CSS version cut the total bundle by 4KB gzipped and removed a whole category of layout thrashing bugs. If you're on modern evergreen browsers only, which is most B2B and SaaS traffic in 2026, the performance conversation is basically moot.
How many breakpoints should a design system actually define?
Four, maybe five, at the page level: mobile, tablet, small desktop, large desktop, and occasionally an ultra-wide catch above 1920px. Past five, you're not designing a system anymore, you're maintaining a spreadsheet nobody updates. I've inherited systems with eleven named breakpoints and every single one of them existed because a past developer needed to fix one specific bug and never went back to consolidate it. Set your four or five viewport breakpoints, then push everything else, component-level adaptation, into container queries where it belongs. That split alone prevents most breakpoint sprawl.
Can I use container queries with Tailwind or other utility frameworks?
Yes, and it's one of the more pleasant developer experiences in CSS right now. Tailwind added native `@container` support with `@sm:`, `@md:` style variants tied to a container context instead of the viewport. You mark a parent with `container-type: inline-size` (or the Tailwind `@container` class), and the descendant utility classes respond to that container's width instead of the browser window. The learning curve is maybe an hour if you already know Tailwind's responsive prefixes. The bigger adjustment is mental, not technical, you have to stop thinking in viewport width and start thinking in component width.