17 / 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
Nested Container Contexts
A container inside a container, and the resolution rule that governs it: an unnamed @container matches the nearest ancestor that establishes a context, so a nested container shadows its parent for unnamed queries below it. The diagram shows the surprise first — a query meant for the outer container answering from the inner — then the fix.
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-17" aria-labelledby="cq-17-h">
<fieldset class="cq-17__theme">
<legend class="cq-17__themelegend">Theme</legend>
<input class="cq-17__themeinput" type="radio" name="cq-17-theme" id="cq-17-theme-auto" checked>
<label class="cq-17__themeopt" for="cq-17-theme-auto">Auto</label>
<input class="cq-17__themeinput" type="radio" name="cq-17-theme" id="cq-17-theme-light">
<label class="cq-17__themeopt" for="cq-17-theme-light">Light</label>
<input class="cq-17__themeinput" type="radio" name="cq-17-theme" id="cq-17-theme-dark">
<label class="cq-17__themeopt" for="cq-17-theme-dark">Dark</label>
</fieldset>
<div class="cq-17__stage">
<header class="cq-17__head">
<h2 class="cq-17__h" id="cq-17-h">Resolution stops at the nearest container</h2>
<p class="cq-17__lede">Both panels have the same ancestor chain. The left query is unnamed, so it never sees the shell.</p>
</header>
<ol class="cq-17__chain">
<li><span class="cq-17__lv">shell</span><code>container: cq-17-shell / inline-size</code><b>wide</b></li>
<li><span class="cq-17__lv cq-17__lv--b">panel</span><code>container-type: inline-size</code><b>17em</b></li>
<li><span class="cq-17__lv cq-17__lv--c">row</span><code>the element being styled</code><b>—</b></li>
</ol>
<div class="cq-17__row">
<section class="cq-17__shell" aria-label="Unnamed query">
<p class="cq-17__cap">unnamed · <code>@container (min-width: 22em)</code></p>
<div class="cq-17__panel">
<p class="cq-17__pw">panel · 17em — the nearest container</p>
<div class="cq-17__grid cq-17__grid--anon">
<div class="cq-17__cellbox">Sensor 4A</div>
<div class="cq-17__cellbox">Sensor 4B</div>
</div>
<p class="cq-17__verdict cq-17__verdict--bad">resolved against panel — 17em is under 22em, so no match</p>
</div>
</section>
<section class="cq-17__shell" aria-label="Named query">
<p class="cq-17__cap">named · <code>@container cq-17-shell (min-width: 22em)</code></p>
<div class="cq-17__panel">
<p class="cq-17__pw">panel · 17em — skipped by name</p>
<div class="cq-17__grid cq-17__grid--named">
<div class="cq-17__cellbox">Sensor 4A</div>
<div class="cq-17__cellbox">Sensor 4B</div>
</div>
<p class="cq-17__verdict cq-17__verdict--good">resolved against cq-17-shell — matched</p>
</div>
</section>
</div>
<p class="cq-17__note">Units nest the same way: <code>cqi</code> resolves against the nearest container, and units cannot be named.</p>
</div>
</section><section class="cq-17" aria-labelledby="cq-17-h">
<fieldset class="cq-17__theme">
<legend class="cq-17__themelegend">Theme</legend>
<input class="cq-17__themeinput" type="radio" name="cq-17-theme" id="cq-17-theme-auto" checked>
<label class="cq-17__themeopt" for="cq-17-theme-auto">Auto</label>
<input class="cq-17__themeinput" type="radio" name="cq-17-theme" id="cq-17-theme-light">
<label class="cq-17__themeopt" for="cq-17-theme-light">Light</label>
<input class="cq-17__themeinput" type="radio" name="cq-17-theme" id="cq-17-theme-dark">
<label class="cq-17__themeopt" for="cq-17-theme-dark">Dark</label>
</fieldset>
<div class="cq-17__stage">
<header class="cq-17__head">
<h2 class="cq-17__h" id="cq-17-h">Resolution stops at the nearest container</h2>
<p class="cq-17__lede">Both panels have the same ancestor chain. The left query is unnamed, so it never sees the shell.</p>
</header>
<ol class="cq-17__chain">
<li><span class="cq-17__lv">shell</span><code>container: cq-17-shell / inline-size</code><b>wide</b></li>
<li><span class="cq-17__lv cq-17__lv--b">panel</span><code>container-type: inline-size</code><b>17em</b></li>
<li><span class="cq-17__lv cq-17__lv--c">row</span><code>the element being styled</code><b>—</b></li>
</ol>
<div class="cq-17__row">
<section class="cq-17__shell" aria-label="Unnamed query">
<p class="cq-17__cap">unnamed · <code>@container (min-width: 22em)</code></p>
<div class="cq-17__panel">
<p class="cq-17__pw">panel · 17em — the nearest container</p>
<div class="cq-17__grid cq-17__grid--anon">
<div class="cq-17__cellbox">Sensor 4A</div>
<div class="cq-17__cellbox">Sensor 4B</div>
</div>
<p class="cq-17__verdict cq-17__verdict--bad">resolved against panel — 17em is under 22em, so no match</p>
</div>
</section>
<section class="cq-17__shell" aria-label="Named query">
<p class="cq-17__cap">named · <code>@container cq-17-shell (min-width: 22em)</code></p>
<div class="cq-17__panel">
<p class="cq-17__pw">panel · 17em — skipped by name</p>
<div class="cq-17__grid cq-17__grid--named">
<div class="cq-17__cellbox">Sensor 4A</div>
<div class="cq-17__cellbox">Sensor 4B</div>
</div>
<p class="cq-17__verdict cq-17__verdict--good">resolved against cq-17-shell — matched</p>
</div>
</section>
</div>
<p class="cq-17__note">Units nest the same way: <code>cqi</code> resolves against the nearest container, and units cannot be named.</p>
</div>
</section>.cq-17,
.cq-17[data-theme="light"] {
--bg: #f4f4f8;
--surf: #fbfbfe;
--surf2: #eceaf4;
--line: #c9c6d8;
--ink: #17161f;
--mut: #5d5a70;
--acc: #0f8f95;
--bad: #a83247;
}
.cq-17[data-theme="dark"] {
--bg: #111018;
--surf: #1a1924;
--surf2: #22202e;
--line: #34314a;
--ink: #ecebf5;
--mut: #9a97b0;
--acc: #4fd6d6;
--bad: #ff7b92;
}
@media (prefers-color-scheme: dark) {
.cq-17:not([data-theme="light"]) {
--bg: #111018;
--surf: #1a1924;
--surf2: #22202e;
--line: #34314a;
--ink: #ecebf5;
--mut: #9a97b0;
--acc: #4fd6d6;
--bad: #ff7b92;
}
}
@supports (color: oklch(0.5 0.1 250)) {
.cq-17,
.cq-17[data-theme="light"] {
--acc: oklch(0.58 0.11 195);
--bad: oklch(0.5 0.17 12);
}
.cq-17[data-theme="dark"] {
--acc: oklch(0.83 0.12 190);
--bad: oklch(0.72 0.16 12);
}
@media (prefers-color-scheme: dark) {
.cq-17:not([data-theme="light"]) {
--acc: oklch(0.83 0.12 190);
--bad: oklch(0.72 0.16 12);
}
}
}
.cq-17 {
width: 100%;
min-height: 100vh;
display: block;
box-sizing: border-box;
background: var(--bg);
color: var(--ink);
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
line-height: 1.4;
}
.cq-17 *,
.cq-17 *::before,
.cq-17 *::after {
box-sizing: border-box;
}
.cq-17__stage {
max-width: 68rem;
margin-inline: auto;
padding: clamp(0.9rem, 3vw, 2.25rem);
display: grid;
gap: 0.9rem;
}
.cq-17__h {
margin: 0;
font-size: clamp(1.2rem, 1rem + 1.5vw, 1.75rem);
font-weight: 650;
letter-spacing: -0.01em;
}
.cq-17__lede {
margin: 0.3rem 0 0;
font-size: 0.84rem;
color: var(--mut);
max-width: 58ch;
}
.cq-17__chain {
list-style: none;
margin: 0;
padding: 0.6rem;
display: grid;
gap: 0.35rem;
background: var(--surf2);
border: 1px solid var(--line);
border-radius: 6px;
}
.cq-17__chain li {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
align-items: center;
font-size: 0.74rem;
color: var(--mut);
min-inline-size: 0;
}
.cq-17__chain li + li {
padding-inline-start: 1.1rem;
}
.cq-17__chain li + li::before {
content: "\21B3";
margin-inline-start: -1.1rem;
color: var(--acc);
}
.cq-17__chain code {
font-family: ui-monospace, Menlo, monospace;
font-size: 0.72rem;
color: var(--ink);
overflow-wrap: anywhere;
}
.cq-17__chain b {
margin-inline-start: auto;
font-variant-numeric: tabular-nums;
color: var(--acc);
}
.cq-17__lv {
padding: 0.1rem 0.4rem;
border-radius: 3px;
background: color-mix(in srgb, var(--acc) 18%, transparent);
color: var(--acc);
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.cq-17__lv--b {
background: color-mix(in srgb, var(--acc) 10%, transparent);
}
.cq-17__lv--c {
background: var(--line);
color: var(--mut);
}
.cq-17__row {
display: flex;
flex-wrap: wrap;
gap: 0.9rem;
}
.cq-17__shell {
container: cq-17-shell / inline-size;
flex: 1 1 20rem;
min-inline-size: 0;
padding: 0.7rem;
background: var(--surf);
border: 1px dashed var(--acc);
border-radius: 6px;
}
.cq-17__cap {
margin: 0 0 0.6rem;
font-size: 0.72rem;
color: var(--mut);
}
.cq-17__cap code {
font-family: ui-monospace, Menlo, monospace;
color: var(--ink);
overflow-wrap: anywhere;
}
.cq-17__panel {
container-type: inline-size;
max-inline-size: 17rem;
padding: 0.6rem;
background: var(--surf2);
border: 1px dashed var(--line);
border-radius: 4px;
}
.cq-17__pw {
margin: 0 0 0.5rem;
font-family: ui-monospace, Menlo, monospace;
font-size: 0.64rem;
color: var(--mut);
}
.cq-17__grid {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 0.4rem;
}
.cq-17__cellbox {
padding: 0.5rem;
background: var(--surf);
border: 1px solid var(--line);
border-radius: 3px;
font-size: 0.78rem;
font-variant-numeric: tabular-nums;
}
.cq-17__verdict {
margin: 0.55rem 0 0;
font-size: 0.7rem;
font-weight: 600;
padding: 0.3rem 0.4rem;
border-radius: 3px;
}
.cq-17__verdict--bad {
color: var(--bad);
background: color-mix(in srgb, var(--bad) 12%, transparent);
}
.cq-17__verdict--good {
color: var(--acc);
background: color-mix(in srgb, var(--acc) 14%, transparent);
}
@container (min-width: 22em) {
.cq-17__grid--anon {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@container cq-17-shell (min-width: 22em) {
.cq-17__grid--named {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
.cq-17__note {
margin: 0;
padding-top: 0.75rem;
border-top: 1px solid var(--line);
font-size: 0.76rem;
color: var(--mut);
}
.cq-17__note code {
font-family: ui-monospace, Menlo, monospace;
}
/* 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. */
.cq-17 {
position: relative;
}
.cq-17__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-17__themelegend {
position: absolute;
inline-size: 1px;
block-size: 1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-17__themeinput {
position: absolute;
inline-size: 1px;
block-size: 1px;
margin: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-17__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-17__themeopt:hover {
opacity: 0.85;
}
.cq-17__themeinput:checked + .cq-17__themeopt {
opacity: 1;
background: color-mix(in srgb, currentColor 15%, transparent);
}
.cq-17__themeinput:focus-visible + .cq-17__themeopt {
outline: 2px solid currentColor;
outline-offset: 2px;
}
.cq-17:has(#cq-17-theme-light:checked) {
--bg: #f4f4f8;
--surf: #fbfbfe;
--surf2: #eceaf4;
--line: #c9c6d8;
--ink: #17161f;
--mut: #5d5a70;
--acc: #0f8f95;
--bad: #a83247;
}
.cq-17:has(#cq-17-theme-dark:checked) {
--bg: #111018;
--surf: #1a1924;
--surf2: #22202e;
--line: #34314a;
--ink: #ecebf5;
--mut: #9a97b0;
--acc: #4fd6d6;
--bad: #ff7b92;
}.cq-17,
.cq-17[data-theme="light"] {
--bg: #f4f4f8;
--surf: #fbfbfe;
--surf2: #eceaf4;
--line: #c9c6d8;
--ink: #17161f;
--mut: #5d5a70;
--acc: #0f8f95;
--bad: #a83247;
}
.cq-17[data-theme="dark"] {
--bg: #111018;
--surf: #1a1924;
--surf2: #22202e;
--line: #34314a;
--ink: #ecebf5;
--mut: #9a97b0;
--acc: #4fd6d6;
--bad: #ff7b92;
}
@media (prefers-color-scheme: dark) {
.cq-17:not([data-theme="light"]) {
--bg: #111018;
--surf: #1a1924;
--surf2: #22202e;
--line: #34314a;
--ink: #ecebf5;
--mut: #9a97b0;
--acc: #4fd6d6;
--bad: #ff7b92;
}
}
@supports (color: oklch(0.5 0.1 250)) {
.cq-17,
.cq-17[data-theme="light"] {
--acc: oklch(0.58 0.11 195);
--bad: oklch(0.5 0.17 12);
}
.cq-17[data-theme="dark"] {
--acc: oklch(0.83 0.12 190);
--bad: oklch(0.72 0.16 12);
}
@media (prefers-color-scheme: dark) {
.cq-17:not([data-theme="light"]) {
--acc: oklch(0.83 0.12 190);
--bad: oklch(0.72 0.16 12);
}
}
}
.cq-17 {
width: 100%;
min-height: 100vh;
display: block;
box-sizing: border-box;
background: var(--bg);
color: var(--ink);
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
line-height: 1.4;
}
.cq-17 *,
.cq-17 *::before,
.cq-17 *::after {
box-sizing: border-box;
}
.cq-17__stage {
max-width: 68rem;
margin-inline: auto;
padding: clamp(0.9rem, 3vw, 2.25rem);
display: grid;
gap: 0.9rem;
}
.cq-17__h {
margin: 0;
font-size: clamp(1.2rem, 1rem + 1.5vw, 1.75rem);
font-weight: 650;
letter-spacing: -0.01em;
}
.cq-17__lede {
margin: 0.3rem 0 0;
font-size: 0.84rem;
color: var(--mut);
max-width: 58ch;
}
.cq-17__chain {
list-style: none;
margin: 0;
padding: 0.6rem;
display: grid;
gap: 0.35rem;
background: var(--surf2);
border: 1px solid var(--line);
border-radius: 6px;
}
.cq-17__chain li {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
align-items: center;
font-size: 0.74rem;
color: var(--mut);
min-inline-size: 0;
}
.cq-17__chain li + li {
padding-inline-start: 1.1rem;
}
.cq-17__chain li + li::before {
content: "\21B3";
margin-inline-start: -1.1rem;
color: var(--acc);
}
.cq-17__chain code {
font-family: ui-monospace, Menlo, monospace;
font-size: 0.72rem;
color: var(--ink);
overflow-wrap: anywhere;
}
.cq-17__chain b {
margin-inline-start: auto;
font-variant-numeric: tabular-nums;
color: var(--acc);
}
.cq-17__lv {
padding: 0.1rem 0.4rem;
border-radius: 3px;
background: color-mix(in srgb, var(--acc) 18%, transparent);
color: var(--acc);
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.cq-17__lv--b {
background: color-mix(in srgb, var(--acc) 10%, transparent);
}
.cq-17__lv--c {
background: var(--line);
color: var(--mut);
}
.cq-17__row {
display: flex;
flex-wrap: wrap;
gap: 0.9rem;
}
.cq-17__shell {
container: cq-17-shell / inline-size;
flex: 1 1 20rem;
min-inline-size: 0;
padding: 0.7rem;
background: var(--surf);
border: 1px dashed var(--acc);
border-radius: 6px;
}
.cq-17__cap {
margin: 0 0 0.6rem;
font-size: 0.72rem;
color: var(--mut);
}
.cq-17__cap code {
font-family: ui-monospace, Menlo, monospace;
color: var(--ink);
overflow-wrap: anywhere;
}
.cq-17__panel {
container-type: inline-size;
max-inline-size: 17rem;
padding: 0.6rem;
background: var(--surf2);
border: 1px dashed var(--line);
border-radius: 4px;
}
.cq-17__pw {
margin: 0 0 0.5rem;
font-family: ui-monospace, Menlo, monospace;
font-size: 0.64rem;
color: var(--mut);
}
.cq-17__grid {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 0.4rem;
}
.cq-17__cellbox {
padding: 0.5rem;
background: var(--surf);
border: 1px solid var(--line);
border-radius: 3px;
font-size: 0.78rem;
font-variant-numeric: tabular-nums;
}
.cq-17__verdict {
margin: 0.55rem 0 0;
font-size: 0.7rem;
font-weight: 600;
padding: 0.3rem 0.4rem;
border-radius: 3px;
}
.cq-17__verdict--bad {
color: var(--bad);
background: color-mix(in srgb, var(--bad) 12%, transparent);
}
.cq-17__verdict--good {
color: var(--acc);
background: color-mix(in srgb, var(--acc) 14%, transparent);
}
@container (min-width: 22em) {
.cq-17__grid--anon {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@container cq-17-shell (min-width: 22em) {
.cq-17__grid--named {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
.cq-17__note {
margin: 0;
padding-top: 0.75rem;
border-top: 1px solid var(--line);
font-size: 0.76rem;
color: var(--mut);
}
.cq-17__note code {
font-family: ui-monospace, Menlo, monospace;
}
/* 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. */
.cq-17 {
position: relative;
}
.cq-17__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-17__themelegend {
position: absolute;
inline-size: 1px;
block-size: 1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-17__themeinput {
position: absolute;
inline-size: 1px;
block-size: 1px;
margin: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-17__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-17__themeopt:hover {
opacity: 0.85;
}
.cq-17__themeinput:checked + .cq-17__themeopt {
opacity: 1;
background: color-mix(in srgb, currentColor 15%, transparent);
}
.cq-17__themeinput:focus-visible + .cq-17__themeopt {
outline: 2px solid currentColor;
outline-offset: 2px;
}
.cq-17:has(#cq-17-theme-light:checked) {
--bg: #f4f4f8;
--surf: #fbfbfe;
--surf2: #eceaf4;
--line: #c9c6d8;
--ink: #17161f;
--mut: #5d5a70;
--acc: #0f8f95;
--bad: #a83247;
}
.cq-17:has(#cq-17-theme-dark:checked) {
--bg: #111018;
--surf: #1a1924;
--surf2: #22202e;
--line: #34314a;
--ink: #ecebf5;
--mut: #9a97b0;
--acc: #4fd6d6;
--bad: #ff7b92;
}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: Nested Container Contexts
Source: https://codefronts.com/layouts/css-container-queries/nested-container-contexts/
A container inside a container, and the resolution rule that governs it: an unnamed @container matches the nearest ancestor that establishes a context, so a nested container shadows its parent for unnamed queries below it. The diagram shows the surprise first — a query meant for the outer container answering from the inner — then the fix.
## HTML
```html
<section class="cq-17" aria-labelledby="cq-17-h">
<fieldset class="cq-17__theme">
<legend class="cq-17__themelegend">Theme</legend>
<input class="cq-17__themeinput" type="radio" name="cq-17-theme" id="cq-17-theme-auto" checked>
<label class="cq-17__themeopt" for="cq-17-theme-auto">Auto</label>
<input class="cq-17__themeinput" type="radio" name="cq-17-theme" id="cq-17-theme-light">
<label class="cq-17__themeopt" for="cq-17-theme-light">Light</label>
<input class="cq-17__themeinput" type="radio" name="cq-17-theme" id="cq-17-theme-dark">
<label class="cq-17__themeopt" for="cq-17-theme-dark">Dark</label>
</fieldset>
<div class="cq-17__stage">
<header class="cq-17__head">
<h2 class="cq-17__h" id="cq-17-h">Resolution stops at the nearest container</h2>
<p class="cq-17__lede">Both panels have the same ancestor chain. The left query is unnamed, so it never sees the shell.</p>
</header>
<ol class="cq-17__chain">
<li><span class="cq-17__lv">shell</span><code>container: cq-17-shell / inline-size</code><b>wide</b></li>
<li><span class="cq-17__lv cq-17__lv--b">panel</span><code>container-type: inline-size</code><b>17em</b></li>
<li><span class="cq-17__lv cq-17__lv--c">row</span><code>the element being styled</code><b>—</b></li>
</ol>
<div class="cq-17__row">
<section class="cq-17__shell" aria-label="Unnamed query">
<p class="cq-17__cap">unnamed · <code>@container (min-width: 22em)</code></p>
<div class="cq-17__panel">
<p class="cq-17__pw">panel · 17em — the nearest container</p>
<div class="cq-17__grid cq-17__grid--anon">
<div class="cq-17__cellbox">Sensor 4A</div>
<div class="cq-17__cellbox">Sensor 4B</div>
</div>
<p class="cq-17__verdict cq-17__verdict--bad">resolved against panel — 17em is under 22em, so no match</p>
</div>
</section>
<section class="cq-17__shell" aria-label="Named query">
<p class="cq-17__cap">named · <code>@container cq-17-shell (min-width: 22em)</code></p>
<div class="cq-17__panel">
<p class="cq-17__pw">panel · 17em — skipped by name</p>
<div class="cq-17__grid cq-17__grid--named">
<div class="cq-17__cellbox">Sensor 4A</div>
<div class="cq-17__cellbox">Sensor 4B</div>
</div>
<p class="cq-17__verdict cq-17__verdict--good">resolved against cq-17-shell — matched</p>
</div>
</section>
</div>
<p class="cq-17__note">Units nest the same way: <code>cqi</code> resolves against the nearest container, and units cannot be named.</p>
</div>
</section>
```
## CSS
```css
.cq-17,
.cq-17[data-theme="light"] {
--bg: #f4f4f8;
--surf: #fbfbfe;
--surf2: #eceaf4;
--line: #c9c6d8;
--ink: #17161f;
--mut: #5d5a70;
--acc: #0f8f95;
--bad: #a83247;
}
.cq-17[data-theme="dark"] {
--bg: #111018;
--surf: #1a1924;
--surf2: #22202e;
--line: #34314a;
--ink: #ecebf5;
--mut: #9a97b0;
--acc: #4fd6d6;
--bad: #ff7b92;
}
@media (prefers-color-scheme: dark) {
.cq-17:not([data-theme="light"]) {
--bg: #111018;
--surf: #1a1924;
--surf2: #22202e;
--line: #34314a;
--ink: #ecebf5;
--mut: #9a97b0;
--acc: #4fd6d6;
--bad: #ff7b92;
}
}
@supports (color: oklch(0.5 0.1 250)) {
.cq-17,
.cq-17[data-theme="light"] {
--acc: oklch(0.58 0.11 195);
--bad: oklch(0.5 0.17 12);
}
.cq-17[data-theme="dark"] {
--acc: oklch(0.83 0.12 190);
--bad: oklch(0.72 0.16 12);
}
@media (prefers-color-scheme: dark) {
.cq-17:not([data-theme="light"]) {
--acc: oklch(0.83 0.12 190);
--bad: oklch(0.72 0.16 12);
}
}
}
.cq-17 {
width: 100%;
min-height: 100vh;
display: block;
box-sizing: border-box;
background: var(--bg);
color: var(--ink);
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
line-height: 1.4;
}
.cq-17 *,
.cq-17 *::before,
.cq-17 *::after {
box-sizing: border-box;
}
.cq-17__stage {
max-width: 68rem;
margin-inline: auto;
padding: clamp(0.9rem, 3vw, 2.25rem);
display: grid;
gap: 0.9rem;
}
.cq-17__h {
margin: 0;
font-size: clamp(1.2rem, 1rem + 1.5vw, 1.75rem);
font-weight: 650;
letter-spacing: -0.01em;
}
.cq-17__lede {
margin: 0.3rem 0 0;
font-size: 0.84rem;
color: var(--mut);
max-width: 58ch;
}
.cq-17__chain {
list-style: none;
margin: 0;
padding: 0.6rem;
display: grid;
gap: 0.35rem;
background: var(--surf2);
border: 1px solid var(--line);
border-radius: 6px;
}
.cq-17__chain li {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
align-items: center;
font-size: 0.74rem;
color: var(--mut);
min-inline-size: 0;
}
.cq-17__chain li + li {
padding-inline-start: 1.1rem;
}
.cq-17__chain li + li::before {
content: "\21B3";
margin-inline-start: -1.1rem;
color: var(--acc);
}
.cq-17__chain code {
font-family: ui-monospace, Menlo, monospace;
font-size: 0.72rem;
color: var(--ink);
overflow-wrap: anywhere;
}
.cq-17__chain b {
margin-inline-start: auto;
font-variant-numeric: tabular-nums;
color: var(--acc);
}
.cq-17__lv {
padding: 0.1rem 0.4rem;
border-radius: 3px;
background: color-mix(in srgb, var(--acc) 18%, transparent);
color: var(--acc);
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.cq-17__lv--b {
background: color-mix(in srgb, var(--acc) 10%, transparent);
}
.cq-17__lv--c {
background: var(--line);
color: var(--mut);
}
.cq-17__row {
display: flex;
flex-wrap: wrap;
gap: 0.9rem;
}
.cq-17__shell {
container: cq-17-shell / inline-size;
flex: 1 1 20rem;
min-inline-size: 0;
padding: 0.7rem;
background: var(--surf);
border: 1px dashed var(--acc);
border-radius: 6px;
}
.cq-17__cap {
margin: 0 0 0.6rem;
font-size: 0.72rem;
color: var(--mut);
}
.cq-17__cap code {
font-family: ui-monospace, Menlo, monospace;
color: var(--ink);
overflow-wrap: anywhere;
}
.cq-17__panel {
container-type: inline-size;
max-inline-size: 17rem;
padding: 0.6rem;
background: var(--surf2);
border: 1px dashed var(--line);
border-radius: 4px;
}
.cq-17__pw {
margin: 0 0 0.5rem;
font-family: ui-monospace, Menlo, monospace;
font-size: 0.64rem;
color: var(--mut);
}
.cq-17__grid {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 0.4rem;
}
.cq-17__cellbox {
padding: 0.5rem;
background: var(--surf);
border: 1px solid var(--line);
border-radius: 3px;
font-size: 0.78rem;
font-variant-numeric: tabular-nums;
}
.cq-17__verdict {
margin: 0.55rem 0 0;
font-size: 0.7rem;
font-weight: 600;
padding: 0.3rem 0.4rem;
border-radius: 3px;
}
.cq-17__verdict--bad {
color: var(--bad);
background: color-mix(in srgb, var(--bad) 12%, transparent);
}
.cq-17__verdict--good {
color: var(--acc);
background: color-mix(in srgb, var(--acc) 14%, transparent);
}
@container (min-width: 22em) {
.cq-17__grid--anon {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@container cq-17-shell (min-width: 22em) {
.cq-17__grid--named {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
.cq-17__note {
margin: 0;
padding-top: 0.75rem;
border-top: 1px solid var(--line);
font-size: 0.76rem;
color: var(--mut);
}
.cq-17__note code {
font-family: ui-monospace, Menlo, monospace;
}
/* 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. */
.cq-17 {
position: relative;
}
.cq-17__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-17__themelegend {
position: absolute;
inline-size: 1px;
block-size: 1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-17__themeinput {
position: absolute;
inline-size: 1px;
block-size: 1px;
margin: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-17__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-17__themeopt:hover {
opacity: 0.85;
}
.cq-17__themeinput:checked + .cq-17__themeopt {
opacity: 1;
background: color-mix(in srgb, currentColor 15%, transparent);
}
.cq-17__themeinput:focus-visible + .cq-17__themeopt {
outline: 2px solid currentColor;
outline-offset: 2px;
}
.cq-17:has(#cq-17-theme-light:checked) {
--bg: #f4f4f8;
--surf: #fbfbfe;
--surf2: #eceaf4;
--line: #c9c6d8;
--ink: #17161f;
--mut: #5d5a70;
--acc: #0f8f95;
--bad: #a83247;
}
.cq-17:has(#cq-17-theme-dark:checked) {
--bg: #111018;
--surf: #1a1924;
--surf2: #22202e;
--line: #34314a;
--ink: #ecebf5;
--mut: #9a97b0;
--acc: #4fd6d6;
--bad: #ff7b92;
}
```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: Nested Container Contexts
Source: https://codefronts.com/layouts/css-container-queries/nested-container-contexts/
A container inside a container, and the resolution rule that governs it: an unnamed @container matches the nearest ancestor that establishes a context, so a nested container shadows its parent for unnamed queries below it. The diagram shows the surprise first — a query meant for the outer container answering from the inner — then the fix.
## HTML
```html
<section class="cq-17" aria-labelledby="cq-17-h">
<fieldset class="cq-17__theme">
<legend class="cq-17__themelegend">Theme</legend>
<input class="cq-17__themeinput" type="radio" name="cq-17-theme" id="cq-17-theme-auto" checked>
<label class="cq-17__themeopt" for="cq-17-theme-auto">Auto</label>
<input class="cq-17__themeinput" type="radio" name="cq-17-theme" id="cq-17-theme-light">
<label class="cq-17__themeopt" for="cq-17-theme-light">Light</label>
<input class="cq-17__themeinput" type="radio" name="cq-17-theme" id="cq-17-theme-dark">
<label class="cq-17__themeopt" for="cq-17-theme-dark">Dark</label>
</fieldset>
<div class="cq-17__stage">
<header class="cq-17__head">
<h2 class="cq-17__h" id="cq-17-h">Resolution stops at the nearest container</h2>
<p class="cq-17__lede">Both panels have the same ancestor chain. The left query is unnamed, so it never sees the shell.</p>
</header>
<ol class="cq-17__chain">
<li><span class="cq-17__lv">shell</span><code>container: cq-17-shell / inline-size</code><b>wide</b></li>
<li><span class="cq-17__lv cq-17__lv--b">panel</span><code>container-type: inline-size</code><b>17em</b></li>
<li><span class="cq-17__lv cq-17__lv--c">row</span><code>the element being styled</code><b>—</b></li>
</ol>
<div class="cq-17__row">
<section class="cq-17__shell" aria-label="Unnamed query">
<p class="cq-17__cap">unnamed · <code>@container (min-width: 22em)</code></p>
<div class="cq-17__panel">
<p class="cq-17__pw">panel · 17em — the nearest container</p>
<div class="cq-17__grid cq-17__grid--anon">
<div class="cq-17__cellbox">Sensor 4A</div>
<div class="cq-17__cellbox">Sensor 4B</div>
</div>
<p class="cq-17__verdict cq-17__verdict--bad">resolved against panel — 17em is under 22em, so no match</p>
</div>
</section>
<section class="cq-17__shell" aria-label="Named query">
<p class="cq-17__cap">named · <code>@container cq-17-shell (min-width: 22em)</code></p>
<div class="cq-17__panel">
<p class="cq-17__pw">panel · 17em — skipped by name</p>
<div class="cq-17__grid cq-17__grid--named">
<div class="cq-17__cellbox">Sensor 4A</div>
<div class="cq-17__cellbox">Sensor 4B</div>
</div>
<p class="cq-17__verdict cq-17__verdict--good">resolved against cq-17-shell — matched</p>
</div>
</section>
</div>
<p class="cq-17__note">Units nest the same way: <code>cqi</code> resolves against the nearest container, and units cannot be named.</p>
</div>
</section>
```
## CSS
```css
.cq-17,
.cq-17[data-theme="light"] {
--bg: #f4f4f8;
--surf: #fbfbfe;
--surf2: #eceaf4;
--line: #c9c6d8;
--ink: #17161f;
--mut: #5d5a70;
--acc: #0f8f95;
--bad: #a83247;
}
.cq-17[data-theme="dark"] {
--bg: #111018;
--surf: #1a1924;
--surf2: #22202e;
--line: #34314a;
--ink: #ecebf5;
--mut: #9a97b0;
--acc: #4fd6d6;
--bad: #ff7b92;
}
@media (prefers-color-scheme: dark) {
.cq-17:not([data-theme="light"]) {
--bg: #111018;
--surf: #1a1924;
--surf2: #22202e;
--line: #34314a;
--ink: #ecebf5;
--mut: #9a97b0;
--acc: #4fd6d6;
--bad: #ff7b92;
}
}
@supports (color: oklch(0.5 0.1 250)) {
.cq-17,
.cq-17[data-theme="light"] {
--acc: oklch(0.58 0.11 195);
--bad: oklch(0.5 0.17 12);
}
.cq-17[data-theme="dark"] {
--acc: oklch(0.83 0.12 190);
--bad: oklch(0.72 0.16 12);
}
@media (prefers-color-scheme: dark) {
.cq-17:not([data-theme="light"]) {
--acc: oklch(0.83 0.12 190);
--bad: oklch(0.72 0.16 12);
}
}
}
.cq-17 {
width: 100%;
min-height: 100vh;
display: block;
box-sizing: border-box;
background: var(--bg);
color: var(--ink);
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
line-height: 1.4;
}
.cq-17 *,
.cq-17 *::before,
.cq-17 *::after {
box-sizing: border-box;
}
.cq-17__stage {
max-width: 68rem;
margin-inline: auto;
padding: clamp(0.9rem, 3vw, 2.25rem);
display: grid;
gap: 0.9rem;
}
.cq-17__h {
margin: 0;
font-size: clamp(1.2rem, 1rem + 1.5vw, 1.75rem);
font-weight: 650;
letter-spacing: -0.01em;
}
.cq-17__lede {
margin: 0.3rem 0 0;
font-size: 0.84rem;
color: var(--mut);
max-width: 58ch;
}
.cq-17__chain {
list-style: none;
margin: 0;
padding: 0.6rem;
display: grid;
gap: 0.35rem;
background: var(--surf2);
border: 1px solid var(--line);
border-radius: 6px;
}
.cq-17__chain li {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
align-items: center;
font-size: 0.74rem;
color: var(--mut);
min-inline-size: 0;
}
.cq-17__chain li + li {
padding-inline-start: 1.1rem;
}
.cq-17__chain li + li::before {
content: "\21B3";
margin-inline-start: -1.1rem;
color: var(--acc);
}
.cq-17__chain code {
font-family: ui-monospace, Menlo, monospace;
font-size: 0.72rem;
color: var(--ink);
overflow-wrap: anywhere;
}
.cq-17__chain b {
margin-inline-start: auto;
font-variant-numeric: tabular-nums;
color: var(--acc);
}
.cq-17__lv {
padding: 0.1rem 0.4rem;
border-radius: 3px;
background: color-mix(in srgb, var(--acc) 18%, transparent);
color: var(--acc);
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.cq-17__lv--b {
background: color-mix(in srgb, var(--acc) 10%, transparent);
}
.cq-17__lv--c {
background: var(--line);
color: var(--mut);
}
.cq-17__row {
display: flex;
flex-wrap: wrap;
gap: 0.9rem;
}
.cq-17__shell {
container: cq-17-shell / inline-size;
flex: 1 1 20rem;
min-inline-size: 0;
padding: 0.7rem;
background: var(--surf);
border: 1px dashed var(--acc);
border-radius: 6px;
}
.cq-17__cap {
margin: 0 0 0.6rem;
font-size: 0.72rem;
color: var(--mut);
}
.cq-17__cap code {
font-family: ui-monospace, Menlo, monospace;
color: var(--ink);
overflow-wrap: anywhere;
}
.cq-17__panel {
container-type: inline-size;
max-inline-size: 17rem;
padding: 0.6rem;
background: var(--surf2);
border: 1px dashed var(--line);
border-radius: 4px;
}
.cq-17__pw {
margin: 0 0 0.5rem;
font-family: ui-monospace, Menlo, monospace;
font-size: 0.64rem;
color: var(--mut);
}
.cq-17__grid {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: 0.4rem;
}
.cq-17__cellbox {
padding: 0.5rem;
background: var(--surf);
border: 1px solid var(--line);
border-radius: 3px;
font-size: 0.78rem;
font-variant-numeric: tabular-nums;
}
.cq-17__verdict {
margin: 0.55rem 0 0;
font-size: 0.7rem;
font-weight: 600;
padding: 0.3rem 0.4rem;
border-radius: 3px;
}
.cq-17__verdict--bad {
color: var(--bad);
background: color-mix(in srgb, var(--bad) 12%, transparent);
}
.cq-17__verdict--good {
color: var(--acc);
background: color-mix(in srgb, var(--acc) 14%, transparent);
}
@container (min-width: 22em) {
.cq-17__grid--anon {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@container cq-17-shell (min-width: 22em) {
.cq-17__grid--named {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
.cq-17__note {
margin: 0;
padding-top: 0.75rem;
border-top: 1px solid var(--line);
font-size: 0.76rem;
color: var(--mut);
}
.cq-17__note code {
font-family: ui-monospace, Menlo, monospace;
}
/* 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. */
.cq-17 {
position: relative;
}
.cq-17__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-17__themelegend {
position: absolute;
inline-size: 1px;
block-size: 1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-17__themeinput {
position: absolute;
inline-size: 1px;
block-size: 1px;
margin: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-17__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-17__themeopt:hover {
opacity: 0.85;
}
.cq-17__themeinput:checked + .cq-17__themeopt {
opacity: 1;
background: color-mix(in srgb, currentColor 15%, transparent);
}
.cq-17__themeinput:focus-visible + .cq-17__themeopt {
outline: 2px solid currentColor;
outline-offset: 2px;
}
.cq-17:has(#cq-17-theme-light:checked) {
--bg: #f4f4f8;
--surf: #fbfbfe;
--surf2: #eceaf4;
--line: #c9c6d8;
--ink: #17161f;
--mut: #5d5a70;
--acc: #0f8f95;
--bad: #a83247;
}
.cq-17:has(#cq-17-theme-dark:checked) {
--bg: #111018;
--surf: #1a1924;
--surf2: #22202e;
--line: #34314a;
--ink: #ecebf5;
--mut: #9a97b0;
--acc: #4fd6d6;
--bad: #ff7b92;
}
```How this works
Resolution walks up and stops at the first container it finds. That is the whole rule, and it is why adding a container anywhere in a tree can change the behaviour of components far below it:
.shell { container: cq-17-shell / inline-size; } /* the wide one */
.panel { container-type: inline-size; } /* 17rem — shadows .shell */
@container (min-width: 22em) { .row { grid-template-columns: 1fr 1fr; } } /* asks .panel */
@container cq-17-shell (min-width: 22em) { .row { grid-template-columns: 1fr 1fr; } } /* asks .shell */The nested container is not doing anything wrong. It is a legitimate container for its own children; the problem is that unnamed queries have no way to say which ancestor they meant. In a component library this shows up when a consumer wraps your component in their own container and your unnamed queries silently start measuring their wrapper instead of yours.
Naming is the fix, and it is cheap. Give every container you intend to query a name, prefix it per component, and query by name. The cost is one keyword per declaration; the benefit is that inserting a container between two levels stops being a breaking change.
The trap: nesting also affects cq units. cqi resolves against the nearest container too, so a nested container rescales every container-relative length below it — type that was 4% of a 900px shell becomes 4% of a 270px panel. Named queries do not help here, because units have no name syntax; if a length must track a specific ancestor, keep that ancestor the nearest container for the subtree that needs it.
Make it yours
- Delete the nested container's
container-typeto watch the unnamed query start resolving against the shell — the failure in reverse. - Name the inner container as well and query each explicitly, which is the state a mature component library ends up in.
- Add a third level to see resolution stop at the nearest one every time, regardless of depth.
- Change the printed widths on each level to test your own thresholds against a real chain.
- Query
cq-17-shelland the inner container in the same block to build a layout that needs both to be true. - Retint the levels from
--acc; each depth is a mix of the accent and the surface.
Gotchas — read before shipping
- An unnamed
@containeralways resolves to the nearest ancestor container, so inserting a container above your component silently changes what your queries measure. cqi,cqband friends resolve against the nearest container too, and there is no way to name a unit — nesting rescales container-relative lengths whether you wanted it to or not.- A query naming a container that is not an ancestor of the styled element never matches; the name must be on an ancestor, not a sibling.
- Nested containment multiplies the positioning side effects: each level is a new containing block for absolutely positioned descendants inside it.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16+ | 113+ | 111+ |
Nested container resolution and container-name are part of the original container query feature, Baseline since February 2023 (Chrome 105 / Safari 16 / Firefox 110); the Chrome and Firefox floors quoted come from oklch() in the teal ramp, gated by @supports with an sRGB hex declared first. Without container query support both diagram panels render their single-column default, so the comparison disappears but the annotated chain and the code still read correctly.