{"id":85,"date":"2026-10-04T03:59:38","date_gmt":"2026-10-04T03:59:38","guid":{"rendered":"https:\/\/webdevpuneet.com\/blog\/?p=85"},"modified":"2026-10-04T04:06:09","modified_gmt":"2026-10-04T04:06:09","slug":"css-container-queries-a-practical-guide-with-real-examples","status":"publish","type":"post","link":"https:\/\/webdevpuneet.com\/blog\/css-container-queries-a-practical-guide-with-real-examples\/","title":{"rendered":"CSS Container Queries: A Practical Guide with Real Examples"},"content":{"rendered":"<p><!--\n================================================================\n  POST TITLE (paste into the WordPress title field \u2014 it becomes the page H1)\n  CSS Container Queries: A Practical Guide with Real Examples\n\n  SEO TITLE (52 chars, target 50\u201360, max 60 \u2014 \"Optimize SEO\" panel \u2192 SEO TITLE)\n  CSS Container Queries: 10 Live Examples You Can Edit\n\n  SEO DESCRIPTION (158 chars, target 150\u2013160, max 160 \u2014 \"Optimize SEO\" panel \u2192 SEO DESCRIPTION)\n  Learn CSS container queries with 10 draggable live demos: cards, grids, units, named containers and style queries. Edit each free in your browser, no sign-up.\n\n  HOW TO PASTE: click the first line after this comment, press Ctrl+Shift+End, Ctrl+C.\n  In WordPress click \"Type \/ to choose a block\", then Ctrl+V.\n  Fallback: \u22ee menu \u2192 Code editor \u2192 paste.\n================================================================\n--><\/p>\n\n\n<p class=\"wp-block-paragraph\">A media query only ever knows one thing: how wide the browser window is. That&#8217;s a problem the moment a component gets reused somewhere its container isn&#8217;t the full page \u2014 a sidebar widget, a dashboard tile that might span one grid column or three, a card dropped into a narrow panel on a wide desktop screen. The component has no way to know its own width; all it can ask is &#8220;how big is the whole window,&#8221; which is often a completely different number.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Container queries<\/strong> fix this by letting an element query the size of its own container instead of the viewport. Mark an ancestor as a &#8220;query container&#8221; with one CSS property, and anything inside it can react to <em>that element&#8217;s<\/em> width (or height) with an <code>@container<\/code> rule \u2014 the same component then looks right whether it&#8217;s sitting in a 200px sidebar or a 600px main column, without a single line of JavaScript and without caring what the browser window is doing.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The 10 demos below are all real, working examples \u2014 nine of them have a handle you drag to resize a box live and watch the CSS react in real time, and the tenth is a switch that flips a style query. Nothing here is a static screenshot of &#8220;before&#8221; and &#8220;after&#8221;; you&#8217;re resizing the actual container and watching the actual rule fire.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Every embed also has a <strong>Fork &amp; Edit<\/strong> button that opens the exact snippet, already running, in <a href=\"https:\/\/webdevpuneet.com\/ui-snippets\/mycode\/\">My Code<\/a> \u2014 webdevpuneet&#8217;s free in-browser editor. Change a breakpoint number, delete a <code>container-type<\/code> line, and watch the reaction stop. That&#8217;s a faster way to build real intuition for this feature than reading about it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The syntax, in one breath<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>.card { container-type: inline-size; }\n\n@container (min-width: 380px) {\n  .card { flex-direction: row; }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Two separate steps, and both are required. First, an element has to be explicitly turned into a <strong>query container<\/strong> with <code>container-type<\/code> \u2014 usually <code>inline-size<\/code>, which tracks the element&#8217;s own width. Nothing queries anything until some ancestor has this. Second, an <code>@container<\/code> rule reacts to that container&#8217;s size \u2014 and unlike <code>@media<\/code>, it applies to whichever container is nearest going up the tree, not to the whole page. The elements it styles inside the rule don&#8217;t have to be the container itself; they can be any descendant, the same way demo #1 below styles a card based on its own width.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why nearly every demo here is something you drag, not click<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Container queries are fundamentally about size, so a click-triggered demo would hide the actual point. Nine of the ten boxes below have a real resize handle wired to genuine <code>pointerdown<\/code>\/<code>pointermove<\/code> events that change an element&#8217;s actual <code>width<\/code> (and, in one case, <code>height<\/code>) in pixels \u2014 not a CSS transform, not a simulated state. The exception is demo #9: style queries react to a custom property rather than to size, so that one is a switch you click. The <code>@container<\/code> rule shown under each demo is what&#8217;s firing as you drag; the small JavaScript readout next to it only ever <em>reports<\/em> the current width and which side of a breakpoint it&#8217;s on, the same &#8220;JS reports, CSS decides&#8221; split used throughout these demos, so what you&#8217;re watching react is genuinely the container query, not a script pretending to be one.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">1. Resizable Profile Card<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Drag the handle on the right edge \u2014 the card switches from a stacked to a side-by-side layout once its own container crosses 380px, independent of how wide the browser window actually is.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/03\/1.html\" style=\"width:100%;height:500px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"Resizable Profile Card &mdash; CSS Container Query Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.cqCard { container-type: inline-size; }\n\n@container (min-width: 380px) {\n  .cqCard { flex-direction: row; }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The card element itself isn&#8217;t the query container here \u2014 its parent, <code>.resizeBox<\/code>, is (that&#8217;s the box you&#8217;re actually dragging). <code>.cqCard<\/code> just sits inside it and reacts, the same way any number of unrelated descendants could react to the same container at once. Before container queries, matching a profile card&#8217;s layout to the width of whatever panel it landed in meant either a JavaScript <code>ResizeObserver<\/code> measuring the element by hand, or accepting that the card would look wrong in at least one context.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> any card, panel, or widget that gets reused across differently-sized contexts \u2014 a dashboard, a CMS page builder, a component library. <strong>Tip:<\/strong> the container doesn&#8217;t have to be the direct parent \u2014 any ancestor with <code>container-type<\/code> set works, however many plain <code>div<\/code>s sit in between.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2. Same Component, Two Different Containers<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Identical HTML and CSS, dropped into two containers of different widths on the very same page, at the very same viewport width \u2014 the single clearest proof that this is genuinely about the container, not the screen.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/03\/2.html\" style=\"width:100%;height:470px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"Same Component, Two Different Containers &mdash; CSS Container Query Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.navWidget { container-type: inline-size; }\n\n@container (max-width: 200px) {\n  .navLabel { display: none; }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Nothing about the nav widget&#8217;s own markup or CSS differs between the two panels \u2014 the only difference is which container it happens to be sitting inside. A media query applied to <code>.navLabel<\/code> could never produce two different results on the same page at the same time like this; it only has one number to check, the viewport, and that number is identical for both copies.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> a component library where the same nav, card, or button genuinely gets embedded in unpredictable contexts \u2014 a CMS, a dashboard with resizable panels, an email-builder-style drag-and-drop layout. <strong>Tip:<\/strong> this is also why container queries matter for component libraries specifically \u2014 a component that queries itself, not the page, is the only kind that&#8217;s honestly reusable.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3. Product Grid With Per-Card Layout<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Drag the grid wider and watch closely: sometimes a <em>wider<\/em> grid flips a card&#8217;s layout back to stacked, because more columns now fit and each one gets less room \u2014 a reaction happening per-card, which a page-level media query has no way to see.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/03\/3.html\" style=\"width:100%;height:730px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"Product Grid With Per-Card Layout &mdash; CSS Container Query Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.prodCard { container-type: inline-size; }\n\n@container (min-width: 210px) {\n  .prodCard { flex-direction: row; }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Each grid item (<code>.prodItem<\/code>, which wraps a <code>.prodCard<\/code>) is its own independent query container, sized by whatever column <code>grid-template-columns: repeat(auto-fill, minmax(150px, 1fr))<\/code> happened to give it \u2014 not by the grid as a whole, and not by the viewport. That&#8217;s the detail that makes the counter-intuitive part possible: as the overall grid widens past the point where a fourth column fits, every card&#8217;s individual width drops, and cards that were previously wide enough to lay out sideways can flip back to stacked, at the exact same time the grid itself got bigger.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> any responsive grid of cards \u2014 products, articles, team members \u2014 where a fixed number of grid breakpoints in a media query always feels one step behind the actual column count. <strong>Tip:<\/strong> pair this with <code>grid-template-columns: repeat(auto-fill, minmax(...))<\/code> specifically \u2014 the container query then handles each card&#8217;s internal layout while the grid itself handles how many columns fit, and neither needs to know about the other.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">4. Container Query Units (cqw)<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Drag the handle \u2014 the heading&#8217;s font size is set in <code>cqw<\/code>, a unit meaning &#8220;percent of the container&#8217;s width,&#8221; not &#8220;percent of the viewport&#8217;s width&#8221; the way <code>vw<\/code> does. The text scales with the box; your browser window stays completely out of it.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/03\/4.html\" style=\"width:100%;height:400px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"Container Query Units (cqw) &mdash; CSS Container Query Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.cqHeading {\n  font-size: clamp(16px, 9cqw, 48px);\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>cqw<\/code> is one of a family of container query length units \u2014 <code>cqw<\/code>\/<code>cqh<\/code> for percent of the container&#8217;s width\/height, <code>cqi<\/code>\/<code>cqb<\/code> for the writing-mode-aware inline\/block equivalents, and <code>cqmin<\/code>\/<code>cqmax<\/code> for whichever of those is smaller or larger. Wrapping the unit in <code>clamp()<\/code> here does the same job it always does \u2014 it sets a floor and a ceiling so the text never gets unreadably small in a narrow box or absurdly large in a wide one, with the <code>cqw<\/code> value doing the actual scaling in between.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> fluid typography and spacing inside a specific component \u2014 a card headline, a hero title inside a variable-width section \u2014 where <code>vw<\/code>-based fluid type would scale with the whole page instead of the one element that should actually control it. <strong>Tip:<\/strong> these units also work inside a <code>calc()<\/code> alongside fixed units, e.g. <code>calc(12px + 2cqw)<\/code>, for scaling that never drops below a hard minimum.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">5. Dashboard KPI Tile<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Drag the handle wider \u2014 once the tile crosses 260px it reveals a trend line and description it was hiding, not just resizing a number. This is the pattern behind a dashboard whose widgets look intentional whether they occupy one grid column or three.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/03\/5.html\" style=\"width:100%;height:440px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"Dashboard KPI Tile &mdash; CSS Container Query Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.kpiDetail { display: none; }\n\n@container (min-width: 260px) {\n  .kpiDetail { display: block; }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is the same underlying mechanism as demo #1&#8217;s layout flip, applied to a different kind of decision: instead of rearranging what&#8217;s already visible, it&#8217;s choosing whether to show extra content at all. A narrow tile stays honest about what it can actually fit \u2014 just the number and its label \u2014 rather than cramming in a trend line and a description that would wrap awkwardly or overflow.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> dashboards, widget grids, or any layout where the same tile component can end up at genuinely different sizes depending on how a user arranges their view. <strong>Tip:<\/strong> this progressive-disclosure pattern (hide by default, reveal past a threshold) is usually the safer default compared to the reverse \u2014 it never risks a narrow container showing content that doesn&#8217;t fit.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">6. Named Containers<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Drag the outer panel \u2014 two rules are watching two different named ancestors at once, one keyed to the inner card and one reaching straight past it to the outer panel, proving <code>@container<\/code> can target a specific ancestor rather than just &#8220;whichever is nearest.&#8221;<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/03\/6.html\" style=\"width:100%;height:530px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"Named Containers &mdash; CSS Container Query Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.outerPanel { container: panel \/ inline-size; }\n.innerCard  { container: card  \/ inline-size; }\n\n@container card (min-width: 200px)  { .innerBadge { font-size: 15px; } }\n@container panel (min-width: 420px) { .innerBadge { background: gold; } }<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">With no name, <code>@container<\/code> always resolves to the <em>nearest<\/em> ancestor container \u2014 which is fine until an element is nested inside more than one, and you need the outer one specifically. The <code>container<\/code> shorthand (<code>name \/ type<\/code>) assigns a name alongside the type, and <code>@container &lt;name&gt; (...)<\/code> then skips past any nearer, unnamed containers to find the one you actually meant. Both rules above are reading from the exact same drag gesture, since resizing the outer panel resizes the inner card proportionally along with it \u2014 they just answer to different names and different thresholds.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> deeply nested component systems \u2014 a card inside a panel inside a page region \u2014 where more than one ancestor is a legitimate query container and &#8220;nearest&#8221; isn&#8217;t always the one that should decide. <strong>Tip:<\/strong> name containers for what they represent (<code>sidebar<\/code>, <code>modal<\/code>, <code>card<\/code>) rather than generically \u2014 it turns <code>@container<\/code> rules into something genuinely readable months later.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">7. Three-Tier Container Breakpoints<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A settings row with three real container-query breakpoints, not just one \u2014 drag across the full range and watch the label, then the value column, appear as the panel actually has room for them.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/03\/7.html\" style=\"width:100%;height:410px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"Three-Tier Container Breakpoints &mdash; CSS Container Query Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@container (min-width: 180px) { .rowLabel { display: inline; } }\n@container (min-width: 320px) { .rowValue { display: inline; } }<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Nothing stops a single container from being watched by any number of independent <code>@container<\/code> rules, each with its own threshold \u2014 exactly the way a page can have several <code>@media<\/code> breakpoints. Here that means a settings row can progressively reveal a label, then a value column, rather than needing to jump straight from &#8220;minimal&#8221; to &#8220;everything&#8221; at one single width.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> any information-dense row or list item \u2014 settings, table rows rendered as cards on narrow layouts, notification items \u2014 where more than two states genuinely exist between &#8220;cramped&#8221; and &#8220;spacious.&#8221; <strong>Tip:<\/strong> keep breakpoint thresholds meaningfully spaced apart (as 180px and 320px are here) \u2014 two triggers close together can cause visibly janky, flickery reflows while dragging or resizing.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">8. Height-Aware Container<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Drag the corner handle. This box uses <code>container-type: size<\/code> instead of <code>inline-size<\/code> \u2014 it can query height too, so the description disappears once the box gets short, no matter how wide it stays.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/03\/8.html\" style=\"width:100%;height:505px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"Height-Aware Container &mdash; CSS Container Query Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.noticeCard { container-type: size; }\n\n@container (max-height: 120px) {\n  .noticeDesc { display: none; }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>container-type: inline-size<\/code> \u2014 used in every other demo in this batch \u2014 only ever tracks width. Querying height needs <code>size<\/code> instead, which contains layout on <em>both<\/em> axes. That comes with a real requirement: a <code>size<\/code>-containing element needs a genuinely definite height from somewhere else (an explicit <code>height<\/code>, or a parent that gives it one), because size containment tells the browser to ignore the element&#8217;s children when computing its own size \u2014 without an explicit height, a size-contained box with no other height source can collapse to zero.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> fixed-aspect boxes, dashboard tiles with an explicit grid-row height, or any card whose available <em>height<\/em> \u2014 not just width \u2014 genuinely varies and should drive what&#8217;s shown. <strong>Tip:<\/strong> reach for <code>inline-size<\/code> by default and only use <code>size<\/code> when a height-based rule is a genuine requirement \u2014 it needs that explicit height, which most naturally-flowing content doesn&#8217;t have and shouldn&#8217;t be forced into.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">9. Style Container Queries<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Click the switch \u2014 it doesn&#8217;t resize anything. It flips a custom property on the container, and <code>@container style()<\/code> reacts to that property&#8217;s value instead of to a width or height.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/03\/9.html\" style=\"width:100%;height:510px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"Style Container Queries &mdash; CSS Container Query Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.densityBox { container-name: densitybox; }\n\n@container densitybox style(--density: compact) {\n  .densityCard { padding: 8px 10px; }\n  .densityCard p { display: none; }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Every other demo here queries size \u2014 this one queries <em>state<\/em>, expressed as a CSS custom property. Setting <code>--density: compact<\/code> on the container (in this demo, via one line of JavaScript on a button click, but it could just as easily come from a data attribute\u2013driven style rule) is what the <code>style()<\/code> condition checks; nothing about the container&#8217;s width or height needs to change for it to match. Notice the container here doesn&#8217;t need <code>container-type: inline-size<\/code> or <code>size<\/code> at all \u2014 a style query only needs the element to be a named container, since it isn&#8217;t reading the container&#8217;s dimensions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> a design-system &#8220;compact mode,&#8221; theme, or density toggle that needs to cascade down to descendants without threading a class onto every single child manually. <strong>Tip:<\/strong> style queries currently only reliably support custom-property conditions like the one here \u2014 they shipped later than size-based container queries and browser support isn&#8217;t as universal yet, so check <a href=\"https:\/\/caniuse.com\/css-container-queries\">caniuse.com\/css-container-queries<\/a> before relying on this one for anything load-bearing.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">10. Media Query vs. Container Query, Side by Side<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Drag the handle down to a narrow width. The top widget is styled with <code>@media (min-width: 500px)<\/code> \u2014 it only ever looks at your browser window, which never changes here, so it stays in its wide layout and overflows. The bottom widget uses <code>@container<\/code> and correctly adapts to the box it&#8217;s actually in.<\/p>\n\n\n\n<iframe src=\"https:\/\/webdevpuneet.com\/demos\/a1\/03\/10.html\" style=\"width:100%;height:500px;border:0;border-radius:10px;overflow:hidden;\" loading=\"lazy\" title=\"Media Query vs. Container Query, Side by Side &mdash; CSS Container Query Demo by webdevpuneet.com\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@media (min-width: 500px) { .mediaWidget { flex-direction: row; } }\n\n@container (min-width: 300px) { .containerWidget { flex-direction: row; } }<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is the demo that makes the whole article&#8217;s point in one gesture. Both widgets sit inside the exact same box, so this is an honest, identical comparison \u2014 the only difference is which condition each one checks. The media query is answering a question that was never actually useful here (&#8220;how wide is the window&#8221;), and the container query is answering the one that was (&#8220;how wide is <em>this<\/em>&#8220;) \u2014 and the visible overflow on the top widget as you narrow the box is exactly the failure mode container queries exist to prevent.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Best for:<\/strong> literally any component you&#8217;re deciding whether to reach for a media query or a container query for \u2014 if the honest answer to &#8220;what size actually matters here&#8221; is the component&#8217;s own box rather than the browser window, this demo is the reason to pick <code>@container<\/code>. <strong>Tip:<\/strong> media queries aren&#8217;t obsolete \u2014 page-level decisions like overall layout direction or hiding a whole sidebar are still legitimately about the viewport. The two are complementary, not competing.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Common container query pitfalls<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Forgetting container-type means the query silently never fires.<\/strong> An <code>@container<\/code> rule with no ancestor carrying <code>container-type<\/code> anywhere above it doesn&#8217;t error \u2014 it simply never matches, and the CSS inside it never applies. If a rule &#8220;isn&#8217;t working,&#8221; the very first thing to check is whether some ancestor actually opted in.<\/li>\n\n\n\n<li><strong>An element cannot query its own size.<\/strong> <code>container-type<\/code> establishes the container on one element, but the <code>@container<\/code> rule that reads it has to apply to a <em>descendant<\/em> \u2014 an element querying itself would be asking a question with no stable answer, since its own size could depend on the very rule deciding it. This is why every demo above sets <code>container-type<\/code> one level up from whatever actually changes.<\/li>\n\n\n\n<li><strong>container-type: size needs a real, explicit size to contain.<\/strong> As demo #8 covers, <code>size<\/code> containment (unlike <code>inline-size<\/code>) affects both axes and ignores children when sizing the box \u2014 an element with no explicit height and no other height source can collapse to zero rather than sizing to its content.<\/li>\n\n\n\n<li><strong>Container query units only work inside a sized container.<\/strong> <code>cqw<\/code>\/<code>cqh<\/code>\/<code>cqi<\/code>\/<code>cqb<\/code> resolve against the nearest ancestor with the matching containment \u2014 outside of any query container, they fall back to behaving like the small viewport units instead, which is easy to miss if a component gets moved outside its expected wrapper.<\/li>\n\n\n\n<li><strong>Broad browser support, but always worth a quick check.<\/strong> Size-based container queries (<code>container-type: inline-size<\/code>\/<code>size<\/code>, container query units) have shipped in every evergreen browser since 2023. Style queries are newer and less universally supported. Check <a href=\"https:\/\/caniuse.com\/css-container-queries\">caniuse.com\/css-container-queries<\/a> for the exact cutoffs your project needs.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Frequently asked questions<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">What are CSS container queries?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Container queries let an element apply styles based on the size (or, with style queries, a custom property&#8217;s value) of an ancestor element \u2014 its &#8220;query container&#8221; \u2014 rather than the size of the browser viewport, which is what a traditional <code>@media<\/code> query is limited to. This lets a single component adapt correctly to whatever context it&#8217;s actually placed in.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How do I set up a container query?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Two steps: mark an ancestor as a query container with <code>container-type: inline-size<\/code> (or <code>size<\/code> for height too), optionally giving it a name with <code>container-name<\/code> (or both at once via the <code>container: name \/ type<\/code> shorthand). Then write an <code>@container (condition) { ... }<\/code> rule targeting any descendant of that container.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What&#8217;s the difference between container-type: inline-size and size?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><code>inline-size<\/code> only contains and tracks the element&#8217;s inline dimension (width, in the default horizontal writing mode) \u2014 the right choice for the large majority of components, since it doesn&#8217;t require the element to have an explicit height. <code>size<\/code> contains and tracks both axes, which is necessary if you need to query height, but requires the container to have a genuinely definite size on both axes from somewhere.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What are container query units?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><code>cqw<\/code>, <code>cqh<\/code>, <code>cqi<\/code>, <code>cqb<\/code>, <code>cqmin<\/code>, and <code>cqmax<\/code> are length units that resolve as a percentage of the nearest query container&#8217;s width, height, inline-size, block-size, and the smaller\/larger of those, respectively \u2014 the container-relative equivalent of <code>vw<\/code>\/<code>vh<\/code>\/<code>vmin<\/code>\/<code>vmax<\/code>, which are always relative to the viewport instead.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Do container queries replace media queries?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No \u2014 they solve different problems. Media queries remain the right tool for page-level decisions that are genuinely about the viewport: overall layout direction, whether to show a full sidebar at all, print styles. Container queries are for a component that needs to adapt to its own box regardless of where that box ends up on the page, as demo #10 above demonstrates directly.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Are container queries well supported in browsers?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Size-based container queries and container query units have shipped in every evergreen browser (Chrome, Edge, Safari, Firefox) since 2023. Style queries arrived later and have less universal support. Check <a href=\"https:\/\/caniuse.com\/css-container-queries\">caniuse.com\/css-container-queries<\/a> for exact version cutoffs before depending on any of this for essential functionality in an older-browser context.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Every demo above ships its full HTML, CSS and JS in the <strong>HTML \/ CSS \/ JS<\/strong> tabs in its own top bar. But the better way to actually learn this is to click <strong>Fork &amp; Edit<\/strong> instead of just copying: it hands you the same snippet in a live editor where you can change a breakpoint, swap <code>inline-size<\/code> for <code>size<\/code>, or delete the <code>container-type<\/code> line entirely and watch exactly what stops working. Once you&#8217;ve got a version you like, save it to <a href=\"https:\/\/webdevpuneet.com\/ui-snippets\/mycode\/\">My Code<\/a> and it&#8217;s yours to keep, tweak further, or reuse in a real project.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>A media query only ever knows one thing: how wide the browser window is. That&#8217;s a problem the moment a component gets reused somewhere its container isn&#8217;t the full page \u2014 a sidebar widget, a dashboard tile that might span one grid column or three, a card dropped into a narrow panel on a wide [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":87,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"","_et_pb_old_content":"","_et_gb_content_width":"","advanced_seo_description":"Learn CSS container queries with 10 draggable live demos: cards, grids, units, named containers and style queries. Edit each free in your browser, no sign-up.","jetpack_seo_html_title":"CSS Container Queries: 10 Live Examples You Can Edit","jetpack_seo_noindex":false,"jetpack_seo_schema_type":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"Learn CSS container queries with 10 draggable live demos: cards, grids, units, named containers and style queries. Edit each free in your browser, no sign-up.","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[6,9],"tags":[],"class_list":["post-85","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-css","category-tutorials","et-has-post-format-content","et_post_format-et-post-format-standard"],"jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/phrh5V-1n","jetpack_featured_media_url":"https:\/\/i0.wp.com\/webdevpuneet.com\/blog\/wp-content\/uploads\/2026\/10\/css-container-queries-header-1200x630-1.webp?fit=1200%2C630&ssl=1","_links":{"self":[{"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/posts\/85","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/comments?post=85"}],"version-history":[{"count":2,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/posts\/85\/revisions"}],"predecessor-version":[{"id":89,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/posts\/85\/revisions\/89"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/media\/87"}],"wp:attachment":[{"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/media?parent=85"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/categories?post=85"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/webdevpuneet.com\/blog\/wp-json\/wp\/v2\/tags?post=85"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}