[{"data":1,"prerenderedAt":4},["ShallowReactive",2],{"post-content-css-container-queries-guide":3},"\u003Cp>Last week I rebuilt a dashboard component for the third time. It was a simple stats card — icon on the left, number on the right. Looked perfect in the sidebar. Then I dropped it into the main content area and it stretched out like a melted candy bar. I sighed, reached for a media query, and stopped myself.\u003C\u002Fp>\n\n\u003Cp>I'd been fighting viewport-based breakpoints for years. Every time I reused a component in a different layout context, I had to override its styles. Sometimes with ugly \u003Ccode>!important\u003C\u002Fcode> flags. Sometimes with a new wrapper class. Always with the feeling that there had to be a better way.\u003C\u002Fp>\n\n\u003Cp>Turns out, there is. It's called container queries, and they've been shipping in every major browser since 2023. If you haven't touched them yet — or you tried them early and found them flaky — you're in for a treat. They're stable, they're powerful, and they'll fundamentally change how you write responsive CSS.\u003C\u002Fp>\n\n\u003Ch2>What's Wrong With Media Queries?\u003C\u002Fh2>\n\n\u003Cp>Nothing, for page-level layouts. If you're building a traditional responsive page where the header, sidebar, and main content area each respond to the browser viewport, media queries work fine.\u003C\u002Fp>\n\n\u003Cp>The problem starts when you build reusable components.\u003C\u002Fp>\n\n\u003Cp>Think about a product card. On a wide desktop screen, it shows a horizontal layout with the image on the left and details on the right. On a phone, it stacks vertically. Clean, right? But what happens when you put that same card inside a narrow sidebar widget, or a three-column grid on a widescreen monitor?\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-css\">\u002F* The old way — guesswork and overrides *\u002F\n.product-card {\n  display: flex;\n  gap: 1rem;\n}\n\n\u002F* What breakpoint do you even use here? *\u002F\n@media (max-width: 768px) {\n  .product-card {\n    flex-direction: column;\n  }\n}\n\n\u002F* Now inside a sidebar — oops, it's already stacked *\u002F\n.sidebar .product-card {\n  flex-direction: column; \u002F* Had to override *\u002F\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>That \u003Ccode>768px\u003C\u002Fcode> breakpoint is completely arbitrary. It has nothing to do with the card itself — it's just the point where the \u003Cem>page\u003C\u002Fem> happens to narrow. The card doesn't care about the viewport. It cares about how much space \u003Cem>it\u003C\u002Fem> has to work with.\u003C\u002Fp>\n\n\u003Ch2>Container Queries — The Mental Shift\u003C\u002Fh2>\n\n\u003Cp>Container queries flip the model: instead of asking \"how wide is the browser?\", you ask \"how wide is my parent container?\"\u003C\u002Fp>\n\n\u003Cp>The syntax is refreshingly direct. First, you designate an element as a \u003Cstrong>container\u003C\u002Fstrong>:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-css\">\u002F* Any block-level element can be a container *\u002F\n.sidebar-panel {\n  container-type: inline-size;\n  \u002F* or shorthand: contain: layout inline-size style; *\u002F\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>Then you write queries against that container:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-css\">@container (min-width: 400px) {\n  .product-card {\n    flex-direction: row;\n  }\n}\n\n@container (max-width: 399px) {\n  .product-card {\n    flex-direction: column;\n  }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>That's it. The \u003Ccode>@container\u003C\u002Fcode> rule checks the container's \u003Ccode>inline-size\u003C\u002Fcode> (logical width), not the viewport width. Drop the card anywhere — sidebar, main content, a modal — and it adapts to whatever space it actually has.\u003C\u002Fp>\n\n\u003Ch2>Setting Up Containers the Right Way\u003C\u002Fh2>\n\n\u003Cp>You have a few options for \u003Ccode>container-type\u003C\u002Fcode>:\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>\u003Ccode>inline-size\u003C\u002Fcode>\u003C\u002Fstrong> — Query based on the container's width (most common).\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>\u003Ccode>size\u003C\u002Fcode>\u003C\u002Fstrong> — Query based on both width and height. Use sparingly — it forces the browser to do extra layout work.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>\u003Ccode>normal\u003C\u002Fcode>\u003C\u002Fstrong> — The element participates in containment but doesn't enable size queries.\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>You can also name your containers, which becomes essential once you have nested containers:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-css\">.card-grid {\n  container-name: cards;\n  container-type: inline-size;\n}\n\n@container cards (min-width: 600px) {\n  \u002F* styles only for containers named \"cards\" *\u002F\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>Shorthand for the win:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-css\">.card-grid {\n  container: cards \u002F inline-size;\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch2>Real Example: A Component That Actually Adapts\u003C\u002Fh2>\n\n\u003Cp>Here's the dashboard stat card I mentioned earlier. I rebuilt it with container queries and never touched it again:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-markup\">&lt;div class=\"stats-grid\"&gt;\n  &lt;div class=\"stat-card\"&gt;\n    &lt;div class=\"stat-icon\"&gt;📊&lt;\u002Fdiv&gt;\n    &lt;div class=\"stat-body\"&gt;\n      &lt;span class=\"stat-value\"&gt;$12,450&lt;\u002Fspan&gt;\n      &lt;span class=\"stat-label\"&gt;Revenue&lt;\u002Fspan&gt;\n    &lt;\u002Fdiv&gt;\n  &lt;\u002Fdiv&gt;\n&lt;\u002Fdiv&gt;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cpre>\u003Ccode class=\"language-css\">.stats-grid {\n  container: stats \u002F inline-size;\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n  gap: 1rem;\n}\n\n.stat-card {\n  display: flex;\n  gap: 0.75rem;\n  padding: 1rem;\n  border-radius: 0.75rem;\n  background: white;\n  box-shadow: 0 1px 3px rgba(0,0,0,0.1);\n}\n\n\u002F* When each card has at least 300px of space *\u002F\n@container stats (min-width: 300px) {\n  .stat-card {\n    flex-direction: row;\n    align-items: center;\n  }\n  .stat-icon {\n    font-size: 2rem;\n  }\n}\n\n\u002F* Narrower than 300px — stack vertically *\u002F\n@container stats (max-width: 299px) {\n  .stat-card {\n    flex-direction: column;\n    text-align: center;\n  }\n  .stat-icon {\n    font-size: 1.5rem;\n    margin-bottom: 0.25rem;\n  }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>This card adapts based on its \u003Cem>actual\u003C\u002Fem> available width inside the grid. If the grid column shrinks to 200px, the card stacks. If it gets 350px, it goes horizontal. No media query involved.\u003C\u002Fp>\n\n\u003Ch2>The Gotchas Nobody Warns You About\u003C\u002Fh3>\n\n\u003Cp>Container queries aren't magic. Here's what tripped me up:\u003C\u002Fp>\n\n\u003Ch3>1. Containment affects layout\u003C\u002Fh3>\n\u003Cp>When you set \u003Ccode>container-type: inline-size\u003C\u002Fcode>, the element becomes a \u003Cstrong>containment context\u003C\u002Fstrong>. Its children can't overflow in the inline direction. If you have absolute-positioned elements inside, they'll clip unless you account for it.\u003C\u002Fp>\n\n\u003Ch3>2. Nested containers need names\u003C\u002Fh3>\n\u003Cp>If you nest containers, a \u003Ccode>@container\u003C\u002Fcode> query without a name will match the \u003Cem>nearest\u003C\u002Fem> ancestor container. That might not be what you want. Always name your containers once you have more than one level:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-css\">.page-section {\n  container: section \u002F inline-size;\n}\n.card-grid {\n  container: grid \u002F inline-size;\n}\n\n\u002F* Target only the grid container *\u002F\n@container grid (min-width: 400px) { ... }\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Ch3>3. Legacy browser support\u003C\u002Fh3>\n\u003Cp>Container queries are supported in Chrome 105+, Firefox 110+, Safari 16.4+, and Edge 105+. That covers roughly 95% of global users as of 2026. If you need to support the remaining 5%, provide a sensible fallback using media queries — the container query styles just won't apply, but the base styles still work.\u003C\u002Fp>\n\n\u003Ch3>4. No \u003Ccode>container-type\u003C\u002Fcode> on the \u003Ccode>&lt;html&gt;\u003C\u002Fcode> or \u003Ccode>&lt;body&gt;\u003C\u002Fcode>\u003C\u002Fh3>\n\u003Cp>You can't set container queries on the root elements. If you need a page-wide container, wrap everything in a \u003Ccode>&lt;div&gt;\u003C\u002Fcode>.\u003C\u002Fp>\n\n\u003Ch2>Where Container Queries Shine\u003C\u002Fh3>\n\n\u003Cp>After a year of using them in production, here's where I reach for container queries every time:\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Cstrong>Dashboard widgets\u003C\u002Fstrong> — Cards, charts, and stats that move between layouts.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Design system components\u003C\u002Fstrong> — Buttons, form fields, and modals that need to work anywhere.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Email templates\u003C\u002Fstrong> — Yes, email. Containers work in most modern email clients now.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>CMS-driven pages\u003C\u002Fstrong> — When editors can drop components anywhere and you can't control the context.\u003C\u002Fli>\n  \u003Cli>\u003Cstrong>Third-party embeds\u003C\u002Fstrong> — Code that runs inside other people's layouts (widgets, chat, analytics).\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>The common thread? Components that you don't control the parent of.\u003C\u002Fp>\n\n\u003Ch2>Container Query Units\u003C\u002Fh3>\n\n\u003Cp>One feature that doesn't get enough attention: container query units. Just like viewport units (\u003Ccode>vw\u003C\u002Fcode>, \u003Ccode>vh\u003C\u002Fcode>), you get units relative to your container:\u003C\u002Fp>\n\n\u003Cul>\n  \u003Cli>\u003Ccode>cqw\u003C\u002Fcode> — 1% of container's width\u003C\u002Fli>\n  \u003Cli>\u003Ccode>cqh\u003C\u002Fcode> — 1% of container's height\u003C\u002Fli>\n  \u003Cli>\u003Ccode>cqi\u003C\u002Fcode> — 1% of container's inline size\u003C\u002Fli>\n  \u003Cli>\u003Ccode>cqb\u003C\u002Fcode> — 1% of container's block size\u003C\u002Fli>\n  \u003Cli>\u003Ccode>cqmin\u003C\u002Fcode> \u002F \u003Ccode>cqmax\u003C\u002Fcode> — the smaller\u002Flarger of \u003Ccode>cqi\u003C\u002Fcode> and \u003Ccode>cqb\u003C\u002Fcode>\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>These are fantastic for fluid typography inside components:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-css\">.stat-value {\n  font-size: clamp(1.25rem, 4cqi + 0.5rem, 3rem);\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>That heading grows and shrinks with its container, not the viewport. Drop it in a sidebar, it's small. Move it to the hero area, it scales up. No media query needed.\u003C\u002Fp>\n\n\u003Ch2>Should You Use Them Today?\u003C\u002Fh2>\n\n\u003Cp>Yes. Absolutely yes. Container queries aren't experimental — they're a W3C Candidate Recommendation and they ship in every browser your users actually use. The only reason not to use them is if you're supporting IE11 (and if you are, I'm so sorry).\u003C\u002Fp>\n\n\u003Cp>Start small. Pick one reusable component — a card, a sidebar widget, a form field — and convert its media queries to container queries. Once you feel the difference, you'll find yourself reaching for \u003Ccode>@container\u003C\u002Fcode> before \u003Ccode>@media\u003C\u002Fcode> every single time.\u003C\u002Fp>\n\n\u003Cp>I rebuilt that dashboard component in about 10 minutes. It's been deployed for six months and I haven't touched the CSS once. That's the real win — CSS that doesn't break when you move things around.\u003C\u002Fp>\n\n\u003Chr \u002F>\n\n\u003Cp>Got a component that's been driving you crazy with media query overrides? Try container queries and see how much cleaner your CSS gets. If you're looking for a place to organize all those component snippets, \u003Ca href=\"https:\u002F\u002Fdevspera.com\u002Fsnippetark\u002F\" target=\"_blank\">Snippet Ark\u003C\u002Fa> lets you save, tag, and retrieve patterns exactly when you need them — no more digging through old projects for that one perfect card layout.\u003C\u002Fp>\n",1787133717915]