20 / 20
All 20 in 20 CSS Container Queries
- 01Container Queries Getting Started
- 02Container Query Units cqi cqw cqmin
- 03Named Containers with container-name
- 04Card Component Adapting to Container Width
- 05Sidebar Aware Main Content Reflow
- 06Container Queries vs Media Queries
- 07CSS Style Queries with container style
- 08Scroll State Container Queries
- 09Fluid Typography with Container Units
- 10Dashboard Widget Component Resizing
- 11Responsive Product Card Grid Reflow
- 12Media Object Layout Inline to Stacked
- 13Dynamic Form Layout Adaptation
- 14Navigation Component Collapse by Container
- 15Responsive Table to Card Layout Switch
- 16Progressive Enhancement and Fallback Strategy
- 17Nested Container Contexts
- 18Avoiding Infinite Layout Loops
- 19Combining Container Queries with CSS Grid
- 20Debugging Container Query Sizing Issues
Debugging Container Query Sizing Issues
Five ways a container query silently does nothing, as broken-then-fixed pairs: container-type missing on the parent, container-type on the element being styled, a container with no definite inline size, an unnamed query resolving too near, and size used where inline-size was meant. Each pair is live, so the fix is visible rather than described.
Build UI like this from scratch: Flexbox, Grid, animations & responsive CSS
ZTM · The CSS Bootcamp · 30‑day money-back guarantee · Affiliate link
The code
<section class="cq-20" aria-labelledby="cq-20-h">
<fieldset class="cq-20__theme">
<legend class="cq-20__themelegend">Theme</legend>
<input class="cq-20__themeinput" type="radio" name="cq-20-theme" id="cq-20-theme-auto" checked>
<label class="cq-20__themeopt" for="cq-20-theme-auto">Auto</label>
<input class="cq-20__themeinput" type="radio" name="cq-20-theme" id="cq-20-theme-light">
<label class="cq-20__themeopt" for="cq-20-theme-light">Light</label>
<input class="cq-20__themeinput" type="radio" name="cq-20-theme" id="cq-20-theme-dark">
<label class="cq-20__themeopt" for="cq-20-theme-dark">Dark</label>
</fieldset>
<div class="cq-20__stage">
<header class="cq-20__head">
<p class="cq-20__kicker">Checklist · in order of how often it bites</p>
<h2 class="cq-20__h" id="cq-20-h">Why isn't my container query working?</h2>
</header>
<ol class="cq-20__list">
<li class="cq-20__case">
<h3 class="cq-20__ct"><span class="cq-20__no">01</span>container-type is missing on the parent</h3>
<p class="cq-20__cd">The query was written for the element to style, and nothing above it opted in.</p>
<div class="cq-20__pair">
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--bad">broken</p><div class="cq-20__w1b"><p class="cq-20__probe cq-20__p1"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--ok">fixed</p><div class="cq-20__w1f"><p class="cq-20__probe cq-20__p1"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
</div>
</li>
<li class="cq-20__case">
<h3 class="cq-20__ct"><span class="cq-20__no">02</span>container-type is on the element being styled</h3>
<p class="cq-20__cd">A query never styles its own container. Move the container up one level.</p>
<div class="cq-20__pair">
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--bad">broken</p><div class="cq-20__w2b"><p class="cq-20__probe cq-20__p2b"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--ok">fixed</p><div class="cq-20__w2f"><p class="cq-20__probe cq-20__p2f"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
</div>
</li>
<li class="cq-20__case">
<h3 class="cq-20__ct"><span class="cq-20__no">03</span>the container has no definite inline size</h3>
<p class="cq-20__cd">A display: contents wrapper generates no box, so containment cannot apply and the declaration is ignored.</p>
<div class="cq-20__pair">
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--bad">broken · display: contents</p><div class="cq-20__w3b"><p class="cq-20__probe cq-20__p3"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--ok">fixed · display: block</p><div class="cq-20__w3f"><p class="cq-20__probe cq-20__p3"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
</div>
</li>
<li class="cq-20__case">
<h3 class="cq-20__ct"><span class="cq-20__no">04</span>an unnamed query found a nearer container</h3>
<p class="cq-20__cd">Resolution stops at the nearest ancestor container. Name the one you meant.</p>
<div class="cq-20__pair">
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--bad">broken · unnamed</p><div class="cq-20__w4"><div class="cq-20__inner"><p class="cq-20__probe cq-20__p4b"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div></div>
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--ok">fixed · named</p><div class="cq-20__w4"><div class="cq-20__inner"><p class="cq-20__probe cq-20__p4f"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div></div>
</div>
</li>
<li class="cq-20__case">
<h3 class="cq-20__ct"><span class="cq-20__no">05</span>size was used where inline-size was meant</h3>
<p class="cq-20__cd">The block axis collapses, so the bug looks like a layout error, not a query error.</p>
<div class="cq-20__pair">
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--bad">broken · container-type: size</p><div class="cq-20__w5b"><p class="cq-20__probe cq-20__p5">block axis collapsed to zero</p></div></div>
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--ok">fixed · inline-size</p><div class="cq-20__w5f"><p class="cq-20__probe cq-20__p5">height from content, query fires</p></div></div>
</div>
</li>
</ol>
</div>
</section><section class="cq-20" aria-labelledby="cq-20-h">
<fieldset class="cq-20__theme">
<legend class="cq-20__themelegend">Theme</legend>
<input class="cq-20__themeinput" type="radio" name="cq-20-theme" id="cq-20-theme-auto" checked>
<label class="cq-20__themeopt" for="cq-20-theme-auto">Auto</label>
<input class="cq-20__themeinput" type="radio" name="cq-20-theme" id="cq-20-theme-light">
<label class="cq-20__themeopt" for="cq-20-theme-light">Light</label>
<input class="cq-20__themeinput" type="radio" name="cq-20-theme" id="cq-20-theme-dark">
<label class="cq-20__themeopt" for="cq-20-theme-dark">Dark</label>
</fieldset>
<div class="cq-20__stage">
<header class="cq-20__head">
<p class="cq-20__kicker">Checklist · in order of how often it bites</p>
<h2 class="cq-20__h" id="cq-20-h">Why isn't my container query working?</h2>
</header>
<ol class="cq-20__list">
<li class="cq-20__case">
<h3 class="cq-20__ct"><span class="cq-20__no">01</span>container-type is missing on the parent</h3>
<p class="cq-20__cd">The query was written for the element to style, and nothing above it opted in.</p>
<div class="cq-20__pair">
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--bad">broken</p><div class="cq-20__w1b"><p class="cq-20__probe cq-20__p1"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--ok">fixed</p><div class="cq-20__w1f"><p class="cq-20__probe cq-20__p1"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
</div>
</li>
<li class="cq-20__case">
<h3 class="cq-20__ct"><span class="cq-20__no">02</span>container-type is on the element being styled</h3>
<p class="cq-20__cd">A query never styles its own container. Move the container up one level.</p>
<div class="cq-20__pair">
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--bad">broken</p><div class="cq-20__w2b"><p class="cq-20__probe cq-20__p2b"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--ok">fixed</p><div class="cq-20__w2f"><p class="cq-20__probe cq-20__p2f"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
</div>
</li>
<li class="cq-20__case">
<h3 class="cq-20__ct"><span class="cq-20__no">03</span>the container has no definite inline size</h3>
<p class="cq-20__cd">A display: contents wrapper generates no box, so containment cannot apply and the declaration is ignored.</p>
<div class="cq-20__pair">
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--bad">broken · display: contents</p><div class="cq-20__w3b"><p class="cq-20__probe cq-20__p3"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--ok">fixed · display: block</p><div class="cq-20__w3f"><p class="cq-20__probe cq-20__p3"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
</div>
</li>
<li class="cq-20__case">
<h3 class="cq-20__ct"><span class="cq-20__no">04</span>an unnamed query found a nearer container</h3>
<p class="cq-20__cd">Resolution stops at the nearest ancestor container. Name the one you meant.</p>
<div class="cq-20__pair">
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--bad">broken · unnamed</p><div class="cq-20__w4"><div class="cq-20__inner"><p class="cq-20__probe cq-20__p4b"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div></div>
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--ok">fixed · named</p><div class="cq-20__w4"><div class="cq-20__inner"><p class="cq-20__probe cq-20__p4f"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div></div>
</div>
</li>
<li class="cq-20__case">
<h3 class="cq-20__ct"><span class="cq-20__no">05</span>size was used where inline-size was meant</h3>
<p class="cq-20__cd">The block axis collapses, so the bug looks like a layout error, not a query error.</p>
<div class="cq-20__pair">
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--bad">broken · container-type: size</p><div class="cq-20__w5b"><p class="cq-20__probe cq-20__p5">block axis collapsed to zero</p></div></div>
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--ok">fixed · inline-size</p><div class="cq-20__w5f"><p class="cq-20__probe cq-20__p5">height from content, query fires</p></div></div>
</div>
</li>
</ol>
</div>
</section>.cq-20,
.cq-20[data-theme="dark"] {
--bg: #0b0d10;
--surf: #14171c;
--surf2: #1b1f26;
--line: #262c35;
--ink: #e7ebf0;
--mut: #8a93a1;
--ok: #3ecf8e;
--bad: #ff6a5e;
}
.cq-20[data-theme="light"] {
--bg: #f5f6f8;
--surf: #ffffff;
--surf2: #eef0f3;
--line: #d7dbe1;
--ink: #12161c;
--mut: #5a6472;
--ok: #12805c;
--bad: #b3261e;
}
@media (prefers-color-scheme: light) {
.cq-20:not([data-theme="dark"]) {
--bg: #f5f6f8;
--surf: #ffffff;
--surf2: #eef0f3;
--line: #d7dbe1;
--ink: #12161c;
--mut: #5a6472;
--ok: #12805c;
--bad: #b3261e;
}
}
@supports (color: oklch(0.5 0.1 250)) {
.cq-20,
.cq-20[data-theme="dark"] {
--ok: oklch(0.8 0.15 160);
--bad: oklch(0.72 0.18 25);
}
.cq-20[data-theme="light"] {
--ok: oklch(0.5 0.12 160);
--bad: oklch(0.5 0.18 28);
}
@media (prefers-color-scheme: light) {
.cq-20:not([data-theme="dark"]) {
--ok: oklch(0.5 0.12 160);
--bad: oklch(0.5 0.18 28);
}
}
}
.cq-20 {
width: 100%;
min-height: 100vh;
display: block;
box-sizing: border-box;
background: var(--bg);
color: var(--ink);
font-family: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
line-height: 1.4;
}
.cq-20 *,
.cq-20 *::before,
.cq-20 *::after {
box-sizing: border-box;
}
.cq-20__stage {
max-width: 72rem;
margin-inline: auto;
padding: clamp(0.85rem, 3vw, 2.25rem);
display: grid;
gap: 1rem;
}
.cq-20__kicker {
margin: 0 0 0.3rem;
font-size: 0.66rem;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--mut);
}
.cq-20__h {
margin: 0;
font-size: clamp(1.15rem, 0.95rem + 1.5vw, 1.7rem);
font-weight: 700;
letter-spacing: -0.01em;
}
.cq-20__list {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 0.75rem;
}
.cq-20__case {
padding: 0.85rem;
background: var(--surf);
border: 1px solid var(--line);
border-radius: 6px;
display: grid;
gap: 0.55rem;
}
.cq-20__ct {
margin: 0;
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
align-items: baseline;
font-size: 0.85rem;
font-weight: 600;
}
.cq-20__no {
color: var(--bad);
font-size: 0.72rem;
font-weight: 700;
}
.cq-20__cd {
margin: 0;
font-size: 0.74rem;
color: var(--mut);
max-width: 76ch;
}
.cq-20__pair {
display: flex;
flex-wrap: wrap;
gap: 0.6rem;
}
.cq-20__mini {
flex: 1 1 14rem;
min-inline-size: 0;
display: grid;
gap: 0.3rem;
align-content: start;
}
.cq-20__mt {
margin: 0;
font-size: 0.64rem;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.cq-20__mt--bad {
color: var(--bad);
}
.cq-20__mt--ok {
color: var(--ok);
}
.cq-20__probe {
margin: 0;
padding: 0.5rem 0.6rem;
background: var(--surf2);
border: 1px dashed var(--bad);
border-radius: 4px;
font-size: 0.72rem;
color: var(--bad);
}
.cq-20__p {
display: none;
}
.cq-20__w1f {
container: cq-20-a / inline-size;
}
.cq-20__w2b {
padding: 0;
}
.cq-20__p2b {
container: cq-20-b / inline-size;
}
.cq-20__w2f {
container: cq-20-b2 / inline-size;
}
.cq-20__w3b {
display: contents;
container: cq-20-c / inline-size;
}
.cq-20__w3f {
display: block;
container: cq-20-c2 / inline-size;
}
.cq-20__w4 {
container: cq-20-d / inline-size;
}
.cq-20__inner {
container-type: inline-size;
max-inline-size: 9rem;
}
.cq-20__w5b {
container-type: size;
border: 1px solid var(--bad);
}
.cq-20__w5f {
container-type: inline-size;
border: 1px solid var(--ok);
}
.cq-20__w5b .cq-20__probe,
.cq-20__w5f .cq-20__probe {
border-style: solid;
}
.cq-20__w5f .cq-20__probe {
border-color: var(--ok);
color: var(--ok);
}
@container cq-20-a (min-width: 8em) {
.cq-20__p1 {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p1 .cq-20__f {
display: none;
}
.cq-20__p1 .cq-20__p {
display: inline;
}
}
@container cq-20-b (min-width: 8em) {
.cq-20__p2b {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
}
@container cq-20-b2 (min-width: 8em) {
.cq-20__p2f {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p2f .cq-20__f {
display: none;
}
.cq-20__p2f .cq-20__p {
display: inline;
}
}
@container cq-20-c (min-width: 8em) {
.cq-20__p3 {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p3 .cq-20__f {
display: none;
}
.cq-20__p3 .cq-20__p {
display: inline;
}
}
@container cq-20-c2 (min-width: 8em) {
.cq-20__p3 {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p3 .cq-20__f {
display: none;
}
.cq-20__p3 .cq-20__p {
display: inline;
}
}
@container (min-width: 14em) {
.cq-20__p4b {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p4b .cq-20__f {
display: none;
}
.cq-20__p4b .cq-20__p {
display: inline;
}
}
@container cq-20-d (min-width: 14em) {
.cq-20__p4f {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p4f .cq-20__f {
display: none;
}
.cq-20__p4f .cq-20__p {
display: inline;
}
}
/* Theme switch (added 2026-08-25). Pure CSS: three radios + :has(), zero
JavaScript. Auto inherits both the OS preference and the site's
[data-theme] stamp; Light and Dark pin the palette and beat both. The
pinned values are the demo's own [data-theme] declarations reused
verbatim, so a toggled demo can never drift from how the same demo
renders under the site theme. */
.cq-20 {
position: relative;
}
.cq-20__theme {
position: absolute;
inset-block-start: 0.875rem;
inset-inline-end: 0.875rem;
z-index: 9;
display: flex;
align-items: center;
gap: 0.125rem;
margin: 0;
padding: 0.1875rem;
border: 1px solid color-mix(in srgb, currentColor 18%, transparent);
border-radius: 999px;
background: color-mix(in srgb, currentColor 8%, transparent);
-webkit-backdrop-filter: blur(6px);
backdrop-filter: blur(6px);
font-family: system-ui, sans-serif;
}
.cq-20__themelegend {
position: absolute;
inline-size: 1px;
block-size: 1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-20__themeinput {
position: absolute;
inline-size: 1px;
block-size: 1px;
margin: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-20__themeopt {
display: inline-flex;
align-items: center;
justify-content: center;
min-block-size: 2.25rem;
min-inline-size: 2.75rem;
padding: 0 0.6875rem;
border-radius: 999px;
font-size: 0.6875rem;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
color: inherit;
opacity: 0.55;
cursor: pointer;
user-select: none;
white-space: nowrap;
}
.cq-20__themeopt:hover {
opacity: 0.85;
}
.cq-20__themeinput:checked + .cq-20__themeopt {
opacity: 1;
background: color-mix(in srgb, currentColor 15%, transparent);
}
.cq-20__themeinput:focus-visible + .cq-20__themeopt {
outline: 2px solid currentColor;
outline-offset: 2px;
}
.cq-20:has(#cq-20-theme-light:checked) {
--bg: #f5f6f8;
--surf: #ffffff;
--surf2: #eef0f3;
--line: #d7dbe1;
--ink: #12161c;
--mut: #5a6472;
--ok: #12805c;
--bad: #b3261e;
}
.cq-20:has(#cq-20-theme-dark:checked) {
--bg: #0b0d10;
--surf: #14171c;
--surf2: #1b1f26;
--line: #262c35;
--ink: #e7ebf0;
--mut: #8a93a1;
--ok: #3ecf8e;
--bad: #ff6a5e;
}.cq-20,
.cq-20[data-theme="dark"] {
--bg: #0b0d10;
--surf: #14171c;
--surf2: #1b1f26;
--line: #262c35;
--ink: #e7ebf0;
--mut: #8a93a1;
--ok: #3ecf8e;
--bad: #ff6a5e;
}
.cq-20[data-theme="light"] {
--bg: #f5f6f8;
--surf: #ffffff;
--surf2: #eef0f3;
--line: #d7dbe1;
--ink: #12161c;
--mut: #5a6472;
--ok: #12805c;
--bad: #b3261e;
}
@media (prefers-color-scheme: light) {
.cq-20:not([data-theme="dark"]) {
--bg: #f5f6f8;
--surf: #ffffff;
--surf2: #eef0f3;
--line: #d7dbe1;
--ink: #12161c;
--mut: #5a6472;
--ok: #12805c;
--bad: #b3261e;
}
}
@supports (color: oklch(0.5 0.1 250)) {
.cq-20,
.cq-20[data-theme="dark"] {
--ok: oklch(0.8 0.15 160);
--bad: oklch(0.72 0.18 25);
}
.cq-20[data-theme="light"] {
--ok: oklch(0.5 0.12 160);
--bad: oklch(0.5 0.18 28);
}
@media (prefers-color-scheme: light) {
.cq-20:not([data-theme="dark"]) {
--ok: oklch(0.5 0.12 160);
--bad: oklch(0.5 0.18 28);
}
}
}
.cq-20 {
width: 100%;
min-height: 100vh;
display: block;
box-sizing: border-box;
background: var(--bg);
color: var(--ink);
font-family: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
line-height: 1.4;
}
.cq-20 *,
.cq-20 *::before,
.cq-20 *::after {
box-sizing: border-box;
}
.cq-20__stage {
max-width: 72rem;
margin-inline: auto;
padding: clamp(0.85rem, 3vw, 2.25rem);
display: grid;
gap: 1rem;
}
.cq-20__kicker {
margin: 0 0 0.3rem;
font-size: 0.66rem;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--mut);
}
.cq-20__h {
margin: 0;
font-size: clamp(1.15rem, 0.95rem + 1.5vw, 1.7rem);
font-weight: 700;
letter-spacing: -0.01em;
}
.cq-20__list {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 0.75rem;
}
.cq-20__case {
padding: 0.85rem;
background: var(--surf);
border: 1px solid var(--line);
border-radius: 6px;
display: grid;
gap: 0.55rem;
}
.cq-20__ct {
margin: 0;
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
align-items: baseline;
font-size: 0.85rem;
font-weight: 600;
}
.cq-20__no {
color: var(--bad);
font-size: 0.72rem;
font-weight: 700;
}
.cq-20__cd {
margin: 0;
font-size: 0.74rem;
color: var(--mut);
max-width: 76ch;
}
.cq-20__pair {
display: flex;
flex-wrap: wrap;
gap: 0.6rem;
}
.cq-20__mini {
flex: 1 1 14rem;
min-inline-size: 0;
display: grid;
gap: 0.3rem;
align-content: start;
}
.cq-20__mt {
margin: 0;
font-size: 0.64rem;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.cq-20__mt--bad {
color: var(--bad);
}
.cq-20__mt--ok {
color: var(--ok);
}
.cq-20__probe {
margin: 0;
padding: 0.5rem 0.6rem;
background: var(--surf2);
border: 1px dashed var(--bad);
border-radius: 4px;
font-size: 0.72rem;
color: var(--bad);
}
.cq-20__p {
display: none;
}
.cq-20__w1f {
container: cq-20-a / inline-size;
}
.cq-20__w2b {
padding: 0;
}
.cq-20__p2b {
container: cq-20-b / inline-size;
}
.cq-20__w2f {
container: cq-20-b2 / inline-size;
}
.cq-20__w3b {
display: contents;
container: cq-20-c / inline-size;
}
.cq-20__w3f {
display: block;
container: cq-20-c2 / inline-size;
}
.cq-20__w4 {
container: cq-20-d / inline-size;
}
.cq-20__inner {
container-type: inline-size;
max-inline-size: 9rem;
}
.cq-20__w5b {
container-type: size;
border: 1px solid var(--bad);
}
.cq-20__w5f {
container-type: inline-size;
border: 1px solid var(--ok);
}
.cq-20__w5b .cq-20__probe,
.cq-20__w5f .cq-20__probe {
border-style: solid;
}
.cq-20__w5f .cq-20__probe {
border-color: var(--ok);
color: var(--ok);
}
@container cq-20-a (min-width: 8em) {
.cq-20__p1 {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p1 .cq-20__f {
display: none;
}
.cq-20__p1 .cq-20__p {
display: inline;
}
}
@container cq-20-b (min-width: 8em) {
.cq-20__p2b {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
}
@container cq-20-b2 (min-width: 8em) {
.cq-20__p2f {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p2f .cq-20__f {
display: none;
}
.cq-20__p2f .cq-20__p {
display: inline;
}
}
@container cq-20-c (min-width: 8em) {
.cq-20__p3 {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p3 .cq-20__f {
display: none;
}
.cq-20__p3 .cq-20__p {
display: inline;
}
}
@container cq-20-c2 (min-width: 8em) {
.cq-20__p3 {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p3 .cq-20__f {
display: none;
}
.cq-20__p3 .cq-20__p {
display: inline;
}
}
@container (min-width: 14em) {
.cq-20__p4b {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p4b .cq-20__f {
display: none;
}
.cq-20__p4b .cq-20__p {
display: inline;
}
}
@container cq-20-d (min-width: 14em) {
.cq-20__p4f {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p4f .cq-20__f {
display: none;
}
.cq-20__p4f .cq-20__p {
display: inline;
}
}
/* Theme switch (added 2026-08-25). Pure CSS: three radios + :has(), zero
JavaScript. Auto inherits both the OS preference and the site's
[data-theme] stamp; Light and Dark pin the palette and beat both. The
pinned values are the demo's own [data-theme] declarations reused
verbatim, so a toggled demo can never drift from how the same demo
renders under the site theme. */
.cq-20 {
position: relative;
}
.cq-20__theme {
position: absolute;
inset-block-start: 0.875rem;
inset-inline-end: 0.875rem;
z-index: 9;
display: flex;
align-items: center;
gap: 0.125rem;
margin: 0;
padding: 0.1875rem;
border: 1px solid color-mix(in srgb, currentColor 18%, transparent);
border-radius: 999px;
background: color-mix(in srgb, currentColor 8%, transparent);
-webkit-backdrop-filter: blur(6px);
backdrop-filter: blur(6px);
font-family: system-ui, sans-serif;
}
.cq-20__themelegend {
position: absolute;
inline-size: 1px;
block-size: 1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-20__themeinput {
position: absolute;
inline-size: 1px;
block-size: 1px;
margin: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-20__themeopt {
display: inline-flex;
align-items: center;
justify-content: center;
min-block-size: 2.25rem;
min-inline-size: 2.75rem;
padding: 0 0.6875rem;
border-radius: 999px;
font-size: 0.6875rem;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
color: inherit;
opacity: 0.55;
cursor: pointer;
user-select: none;
white-space: nowrap;
}
.cq-20__themeopt:hover {
opacity: 0.85;
}
.cq-20__themeinput:checked + .cq-20__themeopt {
opacity: 1;
background: color-mix(in srgb, currentColor 15%, transparent);
}
.cq-20__themeinput:focus-visible + .cq-20__themeopt {
outline: 2px solid currentColor;
outline-offset: 2px;
}
.cq-20:has(#cq-20-theme-light:checked) {
--bg: #f5f6f8;
--surf: #ffffff;
--surf2: #eef0f3;
--line: #d7dbe1;
--ink: #12161c;
--mut: #5a6472;
--ok: #12805c;
--bad: #b3261e;
}
.cq-20:has(#cq-20-theme-dark:checked) {
--bg: #0b0d10;
--surf: #14171c;
--surf2: #1b1f26;
--line: #262c35;
--ink: #e7ebf0;
--mut: #8a93a1;
--ok: #3ecf8e;
--bad: #ff6a5e;
}Here's a working CSS Container Querie from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Debugging Container Query Sizing Issues
Source: https://codefronts.com/layouts/css-container-queries/debugging-container-query-sizing-issues/
Five ways a container query silently does nothing, as broken-then-fixed pairs: container-type missing on the parent, container-type on the element being styled, a container with no definite inline size, an unnamed query resolving too near, and size used where inline-size was meant. Each pair is live, so the fix is visible rather than described.
## HTML
```html
<section class="cq-20" aria-labelledby="cq-20-h">
<fieldset class="cq-20__theme">
<legend class="cq-20__themelegend">Theme</legend>
<input class="cq-20__themeinput" type="radio" name="cq-20-theme" id="cq-20-theme-auto" checked>
<label class="cq-20__themeopt" for="cq-20-theme-auto">Auto</label>
<input class="cq-20__themeinput" type="radio" name="cq-20-theme" id="cq-20-theme-light">
<label class="cq-20__themeopt" for="cq-20-theme-light">Light</label>
<input class="cq-20__themeinput" type="radio" name="cq-20-theme" id="cq-20-theme-dark">
<label class="cq-20__themeopt" for="cq-20-theme-dark">Dark</label>
</fieldset>
<div class="cq-20__stage">
<header class="cq-20__head">
<p class="cq-20__kicker">Checklist · in order of how often it bites</p>
<h2 class="cq-20__h" id="cq-20-h">Why isn't my container query working?</h2>
</header>
<ol class="cq-20__list">
<li class="cq-20__case">
<h3 class="cq-20__ct"><span class="cq-20__no">01</span>container-type is missing on the parent</h3>
<p class="cq-20__cd">The query was written for the element to style, and nothing above it opted in.</p>
<div class="cq-20__pair">
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--bad">broken</p><div class="cq-20__w1b"><p class="cq-20__probe cq-20__p1"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--ok">fixed</p><div class="cq-20__w1f"><p class="cq-20__probe cq-20__p1"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
</div>
</li>
<li class="cq-20__case">
<h3 class="cq-20__ct"><span class="cq-20__no">02</span>container-type is on the element being styled</h3>
<p class="cq-20__cd">A query never styles its own container. Move the container up one level.</p>
<div class="cq-20__pair">
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--bad">broken</p><div class="cq-20__w2b"><p class="cq-20__probe cq-20__p2b"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--ok">fixed</p><div class="cq-20__w2f"><p class="cq-20__probe cq-20__p2f"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
</div>
</li>
<li class="cq-20__case">
<h3 class="cq-20__ct"><span class="cq-20__no">03</span>the container has no definite inline size</h3>
<p class="cq-20__cd">A display: contents wrapper generates no box, so containment cannot apply and the declaration is ignored.</p>
<div class="cq-20__pair">
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--bad">broken · display: contents</p><div class="cq-20__w3b"><p class="cq-20__probe cq-20__p3"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--ok">fixed · display: block</p><div class="cq-20__w3f"><p class="cq-20__probe cq-20__p3"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
</div>
</li>
<li class="cq-20__case">
<h3 class="cq-20__ct"><span class="cq-20__no">04</span>an unnamed query found a nearer container</h3>
<p class="cq-20__cd">Resolution stops at the nearest ancestor container. Name the one you meant.</p>
<div class="cq-20__pair">
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--bad">broken · unnamed</p><div class="cq-20__w4"><div class="cq-20__inner"><p class="cq-20__probe cq-20__p4b"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div></div>
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--ok">fixed · named</p><div class="cq-20__w4"><div class="cq-20__inner"><p class="cq-20__probe cq-20__p4f"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div></div>
</div>
</li>
<li class="cq-20__case">
<h3 class="cq-20__ct"><span class="cq-20__no">05</span>size was used where inline-size was meant</h3>
<p class="cq-20__cd">The block axis collapses, so the bug looks like a layout error, not a query error.</p>
<div class="cq-20__pair">
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--bad">broken · container-type: size</p><div class="cq-20__w5b"><p class="cq-20__probe cq-20__p5">block axis collapsed to zero</p></div></div>
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--ok">fixed · inline-size</p><div class="cq-20__w5f"><p class="cq-20__probe cq-20__p5">height from content, query fires</p></div></div>
</div>
</li>
</ol>
</div>
</section>
```
## CSS
```css
.cq-20,
.cq-20[data-theme="dark"] {
--bg: #0b0d10;
--surf: #14171c;
--surf2: #1b1f26;
--line: #262c35;
--ink: #e7ebf0;
--mut: #8a93a1;
--ok: #3ecf8e;
--bad: #ff6a5e;
}
.cq-20[data-theme="light"] {
--bg: #f5f6f8;
--surf: #ffffff;
--surf2: #eef0f3;
--line: #d7dbe1;
--ink: #12161c;
--mut: #5a6472;
--ok: #12805c;
--bad: #b3261e;
}
@media (prefers-color-scheme: light) {
.cq-20:not([data-theme="dark"]) {
--bg: #f5f6f8;
--surf: #ffffff;
--surf2: #eef0f3;
--line: #d7dbe1;
--ink: #12161c;
--mut: #5a6472;
--ok: #12805c;
--bad: #b3261e;
}
}
@supports (color: oklch(0.5 0.1 250)) {
.cq-20,
.cq-20[data-theme="dark"] {
--ok: oklch(0.8 0.15 160);
--bad: oklch(0.72 0.18 25);
}
.cq-20[data-theme="light"] {
--ok: oklch(0.5 0.12 160);
--bad: oklch(0.5 0.18 28);
}
@media (prefers-color-scheme: light) {
.cq-20:not([data-theme="dark"]) {
--ok: oklch(0.5 0.12 160);
--bad: oklch(0.5 0.18 28);
}
}
}
.cq-20 {
width: 100%;
min-height: 100vh;
display: block;
box-sizing: border-box;
background: var(--bg);
color: var(--ink);
font-family: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
line-height: 1.4;
}
.cq-20 *,
.cq-20 *::before,
.cq-20 *::after {
box-sizing: border-box;
}
.cq-20__stage {
max-width: 72rem;
margin-inline: auto;
padding: clamp(0.85rem, 3vw, 2.25rem);
display: grid;
gap: 1rem;
}
.cq-20__kicker {
margin: 0 0 0.3rem;
font-size: 0.66rem;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--mut);
}
.cq-20__h {
margin: 0;
font-size: clamp(1.15rem, 0.95rem + 1.5vw, 1.7rem);
font-weight: 700;
letter-spacing: -0.01em;
}
.cq-20__list {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 0.75rem;
}
.cq-20__case {
padding: 0.85rem;
background: var(--surf);
border: 1px solid var(--line);
border-radius: 6px;
display: grid;
gap: 0.55rem;
}
.cq-20__ct {
margin: 0;
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
align-items: baseline;
font-size: 0.85rem;
font-weight: 600;
}
.cq-20__no {
color: var(--bad);
font-size: 0.72rem;
font-weight: 700;
}
.cq-20__cd {
margin: 0;
font-size: 0.74rem;
color: var(--mut);
max-width: 76ch;
}
.cq-20__pair {
display: flex;
flex-wrap: wrap;
gap: 0.6rem;
}
.cq-20__mini {
flex: 1 1 14rem;
min-inline-size: 0;
display: grid;
gap: 0.3rem;
align-content: start;
}
.cq-20__mt {
margin: 0;
font-size: 0.64rem;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.cq-20__mt--bad {
color: var(--bad);
}
.cq-20__mt--ok {
color: var(--ok);
}
.cq-20__probe {
margin: 0;
padding: 0.5rem 0.6rem;
background: var(--surf2);
border: 1px dashed var(--bad);
border-radius: 4px;
font-size: 0.72rem;
color: var(--bad);
}
.cq-20__p {
display: none;
}
.cq-20__w1f {
container: cq-20-a / inline-size;
}
.cq-20__w2b {
padding: 0;
}
.cq-20__p2b {
container: cq-20-b / inline-size;
}
.cq-20__w2f {
container: cq-20-b2 / inline-size;
}
.cq-20__w3b {
display: contents;
container: cq-20-c / inline-size;
}
.cq-20__w3f {
display: block;
container: cq-20-c2 / inline-size;
}
.cq-20__w4 {
container: cq-20-d / inline-size;
}
.cq-20__inner {
container-type: inline-size;
max-inline-size: 9rem;
}
.cq-20__w5b {
container-type: size;
border: 1px solid var(--bad);
}
.cq-20__w5f {
container-type: inline-size;
border: 1px solid var(--ok);
}
.cq-20__w5b .cq-20__probe,
.cq-20__w5f .cq-20__probe {
border-style: solid;
}
.cq-20__w5f .cq-20__probe {
border-color: var(--ok);
color: var(--ok);
}
@container cq-20-a (min-width: 8em) {
.cq-20__p1 {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p1 .cq-20__f {
display: none;
}
.cq-20__p1 .cq-20__p {
display: inline;
}
}
@container cq-20-b (min-width: 8em) {
.cq-20__p2b {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
}
@container cq-20-b2 (min-width: 8em) {
.cq-20__p2f {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p2f .cq-20__f {
display: none;
}
.cq-20__p2f .cq-20__p {
display: inline;
}
}
@container cq-20-c (min-width: 8em) {
.cq-20__p3 {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p3 .cq-20__f {
display: none;
}
.cq-20__p3 .cq-20__p {
display: inline;
}
}
@container cq-20-c2 (min-width: 8em) {
.cq-20__p3 {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p3 .cq-20__f {
display: none;
}
.cq-20__p3 .cq-20__p {
display: inline;
}
}
@container (min-width: 14em) {
.cq-20__p4b {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p4b .cq-20__f {
display: none;
}
.cq-20__p4b .cq-20__p {
display: inline;
}
}
@container cq-20-d (min-width: 14em) {
.cq-20__p4f {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p4f .cq-20__f {
display: none;
}
.cq-20__p4f .cq-20__p {
display: inline;
}
}
/* Theme switch (added 2026-08-25). Pure CSS: three radios + :has(), zero
JavaScript. Auto inherits both the OS preference and the site's
[data-theme] stamp; Light and Dark pin the palette and beat both. The
pinned values are the demo's own [data-theme] declarations reused
verbatim, so a toggled demo can never drift from how the same demo
renders under the site theme. */
.cq-20 {
position: relative;
}
.cq-20__theme {
position: absolute;
inset-block-start: 0.875rem;
inset-inline-end: 0.875rem;
z-index: 9;
display: flex;
align-items: center;
gap: 0.125rem;
margin: 0;
padding: 0.1875rem;
border: 1px solid color-mix(in srgb, currentColor 18%, transparent);
border-radius: 999px;
background: color-mix(in srgb, currentColor 8%, transparent);
-webkit-backdrop-filter: blur(6px);
backdrop-filter: blur(6px);
font-family: system-ui, sans-serif;
}
.cq-20__themelegend {
position: absolute;
inline-size: 1px;
block-size: 1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-20__themeinput {
position: absolute;
inline-size: 1px;
block-size: 1px;
margin: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-20__themeopt {
display: inline-flex;
align-items: center;
justify-content: center;
min-block-size: 2.25rem;
min-inline-size: 2.75rem;
padding: 0 0.6875rem;
border-radius: 999px;
font-size: 0.6875rem;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
color: inherit;
opacity: 0.55;
cursor: pointer;
user-select: none;
white-space: nowrap;
}
.cq-20__themeopt:hover {
opacity: 0.85;
}
.cq-20__themeinput:checked + .cq-20__themeopt {
opacity: 1;
background: color-mix(in srgb, currentColor 15%, transparent);
}
.cq-20__themeinput:focus-visible + .cq-20__themeopt {
outline: 2px solid currentColor;
outline-offset: 2px;
}
.cq-20:has(#cq-20-theme-light:checked) {
--bg: #f5f6f8;
--surf: #ffffff;
--surf2: #eef0f3;
--line: #d7dbe1;
--ink: #12161c;
--mut: #5a6472;
--ok: #12805c;
--bad: #b3261e;
}
.cq-20:has(#cq-20-theme-dark:checked) {
--bg: #0b0d10;
--surf: #14171c;
--surf2: #1b1f26;
--line: #262c35;
--ink: #e7ebf0;
--mut: #8a93a1;
--ok: #3ecf8e;
--bad: #ff6a5e;
}
```Here's a working CSS Container Querie from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Debugging Container Query Sizing Issues
Source: https://codefronts.com/layouts/css-container-queries/debugging-container-query-sizing-issues/
Five ways a container query silently does nothing, as broken-then-fixed pairs: container-type missing on the parent, container-type on the element being styled, a container with no definite inline size, an unnamed query resolving too near, and size used where inline-size was meant. Each pair is live, so the fix is visible rather than described.
## HTML
```html
<section class="cq-20" aria-labelledby="cq-20-h">
<fieldset class="cq-20__theme">
<legend class="cq-20__themelegend">Theme</legend>
<input class="cq-20__themeinput" type="radio" name="cq-20-theme" id="cq-20-theme-auto" checked>
<label class="cq-20__themeopt" for="cq-20-theme-auto">Auto</label>
<input class="cq-20__themeinput" type="radio" name="cq-20-theme" id="cq-20-theme-light">
<label class="cq-20__themeopt" for="cq-20-theme-light">Light</label>
<input class="cq-20__themeinput" type="radio" name="cq-20-theme" id="cq-20-theme-dark">
<label class="cq-20__themeopt" for="cq-20-theme-dark">Dark</label>
</fieldset>
<div class="cq-20__stage">
<header class="cq-20__head">
<p class="cq-20__kicker">Checklist · in order of how often it bites</p>
<h2 class="cq-20__h" id="cq-20-h">Why isn't my container query working?</h2>
</header>
<ol class="cq-20__list">
<li class="cq-20__case">
<h3 class="cq-20__ct"><span class="cq-20__no">01</span>container-type is missing on the parent</h3>
<p class="cq-20__cd">The query was written for the element to style, and nothing above it opted in.</p>
<div class="cq-20__pair">
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--bad">broken</p><div class="cq-20__w1b"><p class="cq-20__probe cq-20__p1"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--ok">fixed</p><div class="cq-20__w1f"><p class="cq-20__probe cq-20__p1"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
</div>
</li>
<li class="cq-20__case">
<h3 class="cq-20__ct"><span class="cq-20__no">02</span>container-type is on the element being styled</h3>
<p class="cq-20__cd">A query never styles its own container. Move the container up one level.</p>
<div class="cq-20__pair">
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--bad">broken</p><div class="cq-20__w2b"><p class="cq-20__probe cq-20__p2b"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--ok">fixed</p><div class="cq-20__w2f"><p class="cq-20__probe cq-20__p2f"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
</div>
</li>
<li class="cq-20__case">
<h3 class="cq-20__ct"><span class="cq-20__no">03</span>the container has no definite inline size</h3>
<p class="cq-20__cd">A display: contents wrapper generates no box, so containment cannot apply and the declaration is ignored.</p>
<div class="cq-20__pair">
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--bad">broken · display: contents</p><div class="cq-20__w3b"><p class="cq-20__probe cq-20__p3"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--ok">fixed · display: block</p><div class="cq-20__w3f"><p class="cq-20__probe cq-20__p3"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div>
</div>
</li>
<li class="cq-20__case">
<h3 class="cq-20__ct"><span class="cq-20__no">04</span>an unnamed query found a nearer container</h3>
<p class="cq-20__cd">Resolution stops at the nearest ancestor container. Name the one you meant.</p>
<div class="cq-20__pair">
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--bad">broken · unnamed</p><div class="cq-20__w4"><div class="cq-20__inner"><p class="cq-20__probe cq-20__p4b"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div></div>
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--ok">fixed · named</p><div class="cq-20__w4"><div class="cq-20__inner"><p class="cq-20__probe cq-20__p4f"><span class="cq-20__f">no match</span><span class="cq-20__p">matched</span></p></div></div></div>
</div>
</li>
<li class="cq-20__case">
<h3 class="cq-20__ct"><span class="cq-20__no">05</span>size was used where inline-size was meant</h3>
<p class="cq-20__cd">The block axis collapses, so the bug looks like a layout error, not a query error.</p>
<div class="cq-20__pair">
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--bad">broken · container-type: size</p><div class="cq-20__w5b"><p class="cq-20__probe cq-20__p5">block axis collapsed to zero</p></div></div>
<div class="cq-20__mini"><p class="cq-20__mt cq-20__mt--ok">fixed · inline-size</p><div class="cq-20__w5f"><p class="cq-20__probe cq-20__p5">height from content, query fires</p></div></div>
</div>
</li>
</ol>
</div>
</section>
```
## CSS
```css
.cq-20,
.cq-20[data-theme="dark"] {
--bg: #0b0d10;
--surf: #14171c;
--surf2: #1b1f26;
--line: #262c35;
--ink: #e7ebf0;
--mut: #8a93a1;
--ok: #3ecf8e;
--bad: #ff6a5e;
}
.cq-20[data-theme="light"] {
--bg: #f5f6f8;
--surf: #ffffff;
--surf2: #eef0f3;
--line: #d7dbe1;
--ink: #12161c;
--mut: #5a6472;
--ok: #12805c;
--bad: #b3261e;
}
@media (prefers-color-scheme: light) {
.cq-20:not([data-theme="dark"]) {
--bg: #f5f6f8;
--surf: #ffffff;
--surf2: #eef0f3;
--line: #d7dbe1;
--ink: #12161c;
--mut: #5a6472;
--ok: #12805c;
--bad: #b3261e;
}
}
@supports (color: oklch(0.5 0.1 250)) {
.cq-20,
.cq-20[data-theme="dark"] {
--ok: oklch(0.8 0.15 160);
--bad: oklch(0.72 0.18 25);
}
.cq-20[data-theme="light"] {
--ok: oklch(0.5 0.12 160);
--bad: oklch(0.5 0.18 28);
}
@media (prefers-color-scheme: light) {
.cq-20:not([data-theme="dark"]) {
--ok: oklch(0.5 0.12 160);
--bad: oklch(0.5 0.18 28);
}
}
}
.cq-20 {
width: 100%;
min-height: 100vh;
display: block;
box-sizing: border-box;
background: var(--bg);
color: var(--ink);
font-family: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
line-height: 1.4;
}
.cq-20 *,
.cq-20 *::before,
.cq-20 *::after {
box-sizing: border-box;
}
.cq-20__stage {
max-width: 72rem;
margin-inline: auto;
padding: clamp(0.85rem, 3vw, 2.25rem);
display: grid;
gap: 1rem;
}
.cq-20__kicker {
margin: 0 0 0.3rem;
font-size: 0.66rem;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--mut);
}
.cq-20__h {
margin: 0;
font-size: clamp(1.15rem, 0.95rem + 1.5vw, 1.7rem);
font-weight: 700;
letter-spacing: -0.01em;
}
.cq-20__list {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 0.75rem;
}
.cq-20__case {
padding: 0.85rem;
background: var(--surf);
border: 1px solid var(--line);
border-radius: 6px;
display: grid;
gap: 0.55rem;
}
.cq-20__ct {
margin: 0;
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
align-items: baseline;
font-size: 0.85rem;
font-weight: 600;
}
.cq-20__no {
color: var(--bad);
font-size: 0.72rem;
font-weight: 700;
}
.cq-20__cd {
margin: 0;
font-size: 0.74rem;
color: var(--mut);
max-width: 76ch;
}
.cq-20__pair {
display: flex;
flex-wrap: wrap;
gap: 0.6rem;
}
.cq-20__mini {
flex: 1 1 14rem;
min-inline-size: 0;
display: grid;
gap: 0.3rem;
align-content: start;
}
.cq-20__mt {
margin: 0;
font-size: 0.64rem;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.cq-20__mt--bad {
color: var(--bad);
}
.cq-20__mt--ok {
color: var(--ok);
}
.cq-20__probe {
margin: 0;
padding: 0.5rem 0.6rem;
background: var(--surf2);
border: 1px dashed var(--bad);
border-radius: 4px;
font-size: 0.72rem;
color: var(--bad);
}
.cq-20__p {
display: none;
}
.cq-20__w1f {
container: cq-20-a / inline-size;
}
.cq-20__w2b {
padding: 0;
}
.cq-20__p2b {
container: cq-20-b / inline-size;
}
.cq-20__w2f {
container: cq-20-b2 / inline-size;
}
.cq-20__w3b {
display: contents;
container: cq-20-c / inline-size;
}
.cq-20__w3f {
display: block;
container: cq-20-c2 / inline-size;
}
.cq-20__w4 {
container: cq-20-d / inline-size;
}
.cq-20__inner {
container-type: inline-size;
max-inline-size: 9rem;
}
.cq-20__w5b {
container-type: size;
border: 1px solid var(--bad);
}
.cq-20__w5f {
container-type: inline-size;
border: 1px solid var(--ok);
}
.cq-20__w5b .cq-20__probe,
.cq-20__w5f .cq-20__probe {
border-style: solid;
}
.cq-20__w5f .cq-20__probe {
border-color: var(--ok);
color: var(--ok);
}
@container cq-20-a (min-width: 8em) {
.cq-20__p1 {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p1 .cq-20__f {
display: none;
}
.cq-20__p1 .cq-20__p {
display: inline;
}
}
@container cq-20-b (min-width: 8em) {
.cq-20__p2b {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
}
@container cq-20-b2 (min-width: 8em) {
.cq-20__p2f {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p2f .cq-20__f {
display: none;
}
.cq-20__p2f .cq-20__p {
display: inline;
}
}
@container cq-20-c (min-width: 8em) {
.cq-20__p3 {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p3 .cq-20__f {
display: none;
}
.cq-20__p3 .cq-20__p {
display: inline;
}
}
@container cq-20-c2 (min-width: 8em) {
.cq-20__p3 {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p3 .cq-20__f {
display: none;
}
.cq-20__p3 .cq-20__p {
display: inline;
}
}
@container (min-width: 14em) {
.cq-20__p4b {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p4b .cq-20__f {
display: none;
}
.cq-20__p4b .cq-20__p {
display: inline;
}
}
@container cq-20-d (min-width: 14em) {
.cq-20__p4f {
border-style: solid;
border-color: var(--ok);
color: var(--ok);
}
.cq-20__p4f .cq-20__f {
display: none;
}
.cq-20__p4f .cq-20__p {
display: inline;
}
}
/* Theme switch (added 2026-08-25). Pure CSS: three radios + :has(), zero
JavaScript. Auto inherits both the OS preference and the site's
[data-theme] stamp; Light and Dark pin the palette and beat both. The
pinned values are the demo's own [data-theme] declarations reused
verbatim, so a toggled demo can never drift from how the same demo
renders under the site theme. */
.cq-20 {
position: relative;
}
.cq-20__theme {
position: absolute;
inset-block-start: 0.875rem;
inset-inline-end: 0.875rem;
z-index: 9;
display: flex;
align-items: center;
gap: 0.125rem;
margin: 0;
padding: 0.1875rem;
border: 1px solid color-mix(in srgb, currentColor 18%, transparent);
border-radius: 999px;
background: color-mix(in srgb, currentColor 8%, transparent);
-webkit-backdrop-filter: blur(6px);
backdrop-filter: blur(6px);
font-family: system-ui, sans-serif;
}
.cq-20__themelegend {
position: absolute;
inline-size: 1px;
block-size: 1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-20__themeinput {
position: absolute;
inline-size: 1px;
block-size: 1px;
margin: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-20__themeopt {
display: inline-flex;
align-items: center;
justify-content: center;
min-block-size: 2.25rem;
min-inline-size: 2.75rem;
padding: 0 0.6875rem;
border-radius: 999px;
font-size: 0.6875rem;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
color: inherit;
opacity: 0.55;
cursor: pointer;
user-select: none;
white-space: nowrap;
}
.cq-20__themeopt:hover {
opacity: 0.85;
}
.cq-20__themeinput:checked + .cq-20__themeopt {
opacity: 1;
background: color-mix(in srgb, currentColor 15%, transparent);
}
.cq-20__themeinput:focus-visible + .cq-20__themeopt {
outline: 2px solid currentColor;
outline-offset: 2px;
}
.cq-20:has(#cq-20-theme-light:checked) {
--bg: #f5f6f8;
--surf: #ffffff;
--surf2: #eef0f3;
--line: #d7dbe1;
--ink: #12161c;
--mut: #5a6472;
--ok: #12805c;
--bad: #b3261e;
}
.cq-20:has(#cq-20-theme-dark:checked) {
--bg: #0b0d10;
--surf: #14171c;
--surf2: #1b1f26;
--line: #262c35;
--ink: #e7ebf0;
--mut: #8a93a1;
--ok: #3ecf8e;
--bad: #ff6a5e;
}
```How this works
Almost every failure is one of five things, and they are ordered here by frequency. First and by a wide margin: container-type is missing on the parent, usually because the query was written against the element the author wanted to style:
/* broken */ /* fixed */
.card { ... } .slot { container-type: inline-size; }
@container (min-width: 20em) {} @container (min-width: 20em) { .card { ... } }Second: the container is the element being styled. A query never styles its own container, so container-type on the card plus rules for that same card inside @container is a no-op. Third: the container has no definite inline size — an inline-level box, a shrink-to-fit float, or an element with display: contents cannot be size-contained, so the declaration is ignored and the query never matches.
Fourth: an unnamed query resolved somewhere nearer than you meant (demo 17's problem), which is why naming every queried container is worth the keyword. Fifth: container-type: size where inline-size was intended, which collapses the block axis and produces a layout bug that looks nothing like a query bug (demo 18).
How to check quickly. Give the suspected container a temporary outline: 2px dashed and confirm it is the element you think it is, and that it has the width you think it has; then confirm the rules inside the query target a descendant of it. In DevTools, an element that establishes a container is badged as one in the Elements panel — if there is no badge, the query has nothing to ask.
Make it yours
- Toggle each fix off in your own copy to reproduce the failure deliberately — the fastest way to learn the symptom of each cause.
- Add your own sixth pair for a failure your codebase hits, such as a container inside
display: contents. - Change the shared threshold (
16em) to see all five probes flip together where they can. - Swap the probe copy for the real component you are debugging; the container plumbing is all that matters.
- Keep the dashed outline rule as a debugging utility class in your own stylesheet.
- Retint the pass and fail chips from
--okand--bad.
Gotchas — read before shipping
container-typeon the element you are styling can never work — the query reads the container and applies to descendants only.- An inline-level container, a
display: contentswrapper or a shrink-to-fit box has no definite inline size to measure, so the declaration is silently ignored. - An unnamed query resolves to the nearest container, which may be a wrapper someone else added; name every container you intend to query.
container-type: sizewithout an externally determined height collapses the block axis, so the bug presents as a layout collapse rather than as a failed query.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16+ | 113+ | 111+ |
Every probe here uses only container-type and @container, Baseline since February 2023 (Chrome 105 / Safari 16 / Firefox 110) — including the container-type: size pair, which demonstrates a mistake rather than depending on a newer feature. The Chrome and Firefox floors quoted come from oklch() in the pass and fail chips, gated by @supports with sRGB hex declared first. In an engine without container query support every probe reports its failed state, which is the honest result: no query is evaluating.