01 / 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
Container Queries Getting Started
The reference implementation the rest of this collection borrows from: container-type: inline-size on the parent, an @container (min-width: 21em) block in the stylesheet, and the child rules inside it. The same card markup sits in a wide slot and a narrow slot side by side, so one query produces two layouts, and an annotated panel labels each of the three moving parts.
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-01" aria-labelledby="cq-01-h">
<fieldset class="cq-01__theme">
<legend class="cq-01__themelegend">Theme</legend>
<input class="cq-01__themeinput" type="radio" name="cq-01-theme" id="cq-01-theme-auto" checked>
<label class="cq-01__themeopt" for="cq-01-theme-auto">Auto</label>
<input class="cq-01__themeinput" type="radio" name="cq-01-theme" id="cq-01-theme-light">
<label class="cq-01__themeopt" for="cq-01-theme-light">Light</label>
<input class="cq-01__themeinput" type="radio" name="cq-01-theme" id="cq-01-theme-dark">
<label class="cq-01__themeopt" for="cq-01-theme-dark">Dark</label>
</fieldset>
<div class="cq-01__stage">
<header class="cq-01__head">
<p class="cq-01__kicker">Container queries / 01</p>
<h2 class="cq-01__h" id="cq-01-h">One query, two layouts</h2>
<p class="cq-01__lede">The card below is the same markup in both slots. Only the slot width differs.</p>
</header>
<ol class="cq-01__anno">
<li><span class="cq-01__n">1</span><div><b>Opt the parent in</b><code>.slot { container-type: inline-size }</code></div></li>
<li><span class="cq-01__n">2</span><div><b>Ask the question</b><code>@container (min-width: 21em)</code></div></li>
<li><span class="cq-01__n">3</span><div><b>Style the children</b><code>.card { grid-template-columns: 8rem 1fr }</code></div></li>
</ol>
<div class="cq-01__row">
<div class="cq-01__col">
<p class="cq-01__label">Main column <span>wide slot</span></p>
<div class="cq-01__slot cq-01__slot--wide">
<article class="cq-01__card">
<div class="cq-01__thumb" role="img" aria-label="Abstract gradient artwork"></div>
<div class="cq-01__body">
<p class="cq-01__tag">Field notes</p>
<h3 class="cq-01__title">Meridian ships regional failover</h3>
<p class="cq-01__text">Read replicas now promote in under nine seconds without operator input.</p>
<p class="cq-01__meta">Priya Sharma · 6 min read</p>
</div>
</article>
</div>
</div>
<div class="cq-01__col cq-01__col--narrow">
<p class="cq-01__label">Sidebar rail <span>narrow slot</span></p>
<div class="cq-01__slot cq-01__slot--narrow">
<article class="cq-01__card">
<div class="cq-01__thumb" role="img" aria-label="Abstract gradient artwork"></div>
<div class="cq-01__body">
<p class="cq-01__tag">Field notes</p>
<h3 class="cq-01__title">Meridian ships regional failover</h3>
<p class="cq-01__text">Read replicas now promote in under nine seconds without operator input.</p>
<p class="cq-01__meta">Priya Sharma · 6 min read</p>
</div>
</article>
</div>
</div>
</div>
<p class="cq-01__foot">The wide slot is past 21em, so the query matches and the card goes horizontal. The narrow slot is not, so it keeps the stacked default.</p>
</div>
</section><section class="cq-01" aria-labelledby="cq-01-h">
<fieldset class="cq-01__theme">
<legend class="cq-01__themelegend">Theme</legend>
<input class="cq-01__themeinput" type="radio" name="cq-01-theme" id="cq-01-theme-auto" checked>
<label class="cq-01__themeopt" for="cq-01-theme-auto">Auto</label>
<input class="cq-01__themeinput" type="radio" name="cq-01-theme" id="cq-01-theme-light">
<label class="cq-01__themeopt" for="cq-01-theme-light">Light</label>
<input class="cq-01__themeinput" type="radio" name="cq-01-theme" id="cq-01-theme-dark">
<label class="cq-01__themeopt" for="cq-01-theme-dark">Dark</label>
</fieldset>
<div class="cq-01__stage">
<header class="cq-01__head">
<p class="cq-01__kicker">Container queries / 01</p>
<h2 class="cq-01__h" id="cq-01-h">One query, two layouts</h2>
<p class="cq-01__lede">The card below is the same markup in both slots. Only the slot width differs.</p>
</header>
<ol class="cq-01__anno">
<li><span class="cq-01__n">1</span><div><b>Opt the parent in</b><code>.slot { container-type: inline-size }</code></div></li>
<li><span class="cq-01__n">2</span><div><b>Ask the question</b><code>@container (min-width: 21em)</code></div></li>
<li><span class="cq-01__n">3</span><div><b>Style the children</b><code>.card { grid-template-columns: 8rem 1fr }</code></div></li>
</ol>
<div class="cq-01__row">
<div class="cq-01__col">
<p class="cq-01__label">Main column <span>wide slot</span></p>
<div class="cq-01__slot cq-01__slot--wide">
<article class="cq-01__card">
<div class="cq-01__thumb" role="img" aria-label="Abstract gradient artwork"></div>
<div class="cq-01__body">
<p class="cq-01__tag">Field notes</p>
<h3 class="cq-01__title">Meridian ships regional failover</h3>
<p class="cq-01__text">Read replicas now promote in under nine seconds without operator input.</p>
<p class="cq-01__meta">Priya Sharma · 6 min read</p>
</div>
</article>
</div>
</div>
<div class="cq-01__col cq-01__col--narrow">
<p class="cq-01__label">Sidebar rail <span>narrow slot</span></p>
<div class="cq-01__slot cq-01__slot--narrow">
<article class="cq-01__card">
<div class="cq-01__thumb" role="img" aria-label="Abstract gradient artwork"></div>
<div class="cq-01__body">
<p class="cq-01__tag">Field notes</p>
<h3 class="cq-01__title">Meridian ships regional failover</h3>
<p class="cq-01__text">Read replicas now promote in under nine seconds without operator input.</p>
<p class="cq-01__meta">Priya Sharma · 6 min read</p>
</div>
</article>
</div>
</div>
</div>
<p class="cq-01__foot">The wide slot is past 21em, so the query matches and the card goes horizontal. The narrow slot is not, so it keeps the stacked default.</p>
</div>
</section>.cq-01,
.cq-01[data-theme="light"] {
--bg: #f4f5f7;
--surf: #ffffff;
--surf2: #eaecf1;
--line: #d6d9e0;
--ink: #15171c;
--mut: #5d6473;
--acc: #1f57cf;
--accs: #e7eefc;
}
.cq-01[data-theme="dark"] {
--bg: #0f1116;
--surf: #171a21;
--surf2: #1e222b;
--line: #2b303b;
--ink: #eceef3;
--mut: #99a1b3;
--acc: #7fa6ff;
--accs: #1a2340;
}
@media (prefers-color-scheme: dark) {
.cq-01:not([data-theme="light"]) {
--bg: #0f1116;
--surf: #171a21;
--surf2: #1e222b;
--line: #2b303b;
--ink: #eceef3;
--mut: #99a1b3;
--acc: #7fa6ff;
--accs: #1a2340;
}
}
@supports (color: oklch(0.5 0.1 250)) {
.cq-01,
.cq-01[data-theme="light"] {
--acc: oklch(0.5 0.19 264);
--accs: oklch(0.95 0.035 264);
}
.cq-01[data-theme="dark"] {
--acc: oklch(0.78 0.13 264);
--accs: oklch(0.28 0.07 264);
}
@media (prefers-color-scheme: dark) {
.cq-01:not([data-theme="light"]) {
--acc: oklch(0.78 0.13 264);
--accs: oklch(0.28 0.07 264);
}
}
}
.cq-01 {
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;
font-size: 100%;
line-height: 1.5;
}
.cq-01 *,
.cq-01 *::before,
.cq-01 *::after {
box-sizing: border-box;
}
.cq-01__stage {
max-width: 68rem;
margin-inline: auto;
padding: clamp(1rem, 3vw, 2.25rem);
display: grid;
gap: 1.25rem;
}
.cq-01__kicker {
margin: 0 0 0.35rem;
font-size: 0.7rem;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--acc);
}
.cq-01__h {
margin: 0;
font-size: clamp(1.35rem, 1rem + 2cqi, 2rem);
font-weight: 600;
letter-spacing: -0.01em;
text-wrap: balance;
}
.cq-01__lede {
margin: 0.35rem 0 0;
color: var(--mut);
font-size: 0.82rem;
max-width: 46ch;
}
.cq-01__anno {
margin: 0;
padding: 0.75rem;
list-style: none;
display: grid;
gap: 0.5rem;
background: var(--surf);
border: 1px solid var(--line);
border-radius: 6px;
}
.cq-01__anno li {
display: flex;
gap: 0.6rem;
align-items: flex-start;
min-inline-size: 0;
}
.cq-01__anno li + li {
border-top: 1px dashed var(--line);
padding-top: 0.5rem;
}
.cq-01__n {
flex: none;
width: 1.35rem;
height: 1.35rem;
display: grid;
place-items: center;
border-radius: 50%;
background: var(--accs);
color: var(--acc);
font-size: 0.7rem;
font-weight: 700;
}
.cq-01__anno b {
display: block;
font-size: 0.74rem;
letter-spacing: 0.02em;
}
.cq-01__anno code {
display: block;
margin-top: 0.15rem;
font-size: 0.72rem;
color: var(--mut);
overflow-wrap: anywhere;
}
.cq-01__row {
display: flex;
flex-wrap: wrap;
gap: 1rem;
align-items: start;
}
.cq-01__col {
flex: 1 1 22rem;
min-inline-size: 0;
display: grid;
gap: 0.45rem;
}
.cq-01__col--narrow {
flex: 0 1 17rem;
}
.cq-01__label {
margin: 0;
font-size: 0.72rem;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--mut);
display: flex;
gap: 0.4rem;
align-items: baseline;
}
.cq-01__label span {
color: var(--acc);
text-transform: none;
letter-spacing: 0;
}
.cq-01__slot {
container-type: inline-size;
container-name: cq-01-slot;
padding: 0.6rem;
background: var(--surf2);
border: 1px dashed var(--line);
border-radius: 8px;
}
.cq-01__card {
display: grid;
gap: 0.7rem;
padding: 0.7rem;
background: var(--surf);
border: 1px solid var(--line);
border-radius: 6px;
}
.cq-01__thumb {
aspect-ratio: 16 / 9;
border-radius: 4px;
background: linear-gradient(135deg, #1f57cf 0%, #6f8cf5 45%, #b9c9ff 100%);
}
.cq-01__body {
min-inline-size: 0;
display: grid;
gap: 0.3rem;
align-content: start;
}
.cq-01__tag {
margin: 0;
font-size: 0.66rem;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--acc);
}
.cq-01__title {
margin: 0;
font-size: 0.95rem;
font-weight: 600;
line-height: 1.25;
}
.cq-01__text {
margin: 0;
font-size: 0.78rem;
color: var(--mut);
}
.cq-01__meta {
margin: 0.15rem 0 0;
font-size: 0.7rem;
color: var(--mut);
}
@container cq-01-slot (min-width: 21em) {
.cq-01__card {
grid-template-columns: 9rem 1fr;
align-items: start;
padding: 0.85rem;
}
.cq-01__thumb {
aspect-ratio: auto;
block-size: 100%;
min-block-size: 5.5rem;
}
.cq-01__title {
font-size: 1.1rem;
}
}
.cq-01__foot {
margin: 0;
font-size: 0.74rem;
color: var(--mut);
border-top: 1px solid var(--line);
padding-top: 0.75rem;
max-width: 62ch;
}
/* 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-01 {
position: relative;
}
.cq-01__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-01__themelegend {
position: absolute;
inline-size: 1px;
block-size: 1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-01__themeinput {
position: absolute;
inline-size: 1px;
block-size: 1px;
margin: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-01__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-01__themeopt:hover {
opacity: 0.85;
}
.cq-01__themeinput:checked + .cq-01__themeopt {
opacity: 1;
background: color-mix(in srgb, currentColor 15%, transparent);
}
.cq-01__themeinput:focus-visible + .cq-01__themeopt {
outline: 2px solid currentColor;
outline-offset: 2px;
}
.cq-01:has(#cq-01-theme-light:checked) {
--bg: #f4f5f7;
--surf: #ffffff;
--surf2: #eaecf1;
--line: #d6d9e0;
--ink: #15171c;
--mut: #5d6473;
--acc: #1f57cf;
--accs: #e7eefc;
}
.cq-01:has(#cq-01-theme-dark:checked) {
--bg: #0f1116;
--surf: #171a21;
--surf2: #1e222b;
--line: #2b303b;
--ink: #eceef3;
--mut: #99a1b3;
--acc: #7fa6ff;
--accs: #1a2340;
}.cq-01,
.cq-01[data-theme="light"] {
--bg: #f4f5f7;
--surf: #ffffff;
--surf2: #eaecf1;
--line: #d6d9e0;
--ink: #15171c;
--mut: #5d6473;
--acc: #1f57cf;
--accs: #e7eefc;
}
.cq-01[data-theme="dark"] {
--bg: #0f1116;
--surf: #171a21;
--surf2: #1e222b;
--line: #2b303b;
--ink: #eceef3;
--mut: #99a1b3;
--acc: #7fa6ff;
--accs: #1a2340;
}
@media (prefers-color-scheme: dark) {
.cq-01:not([data-theme="light"]) {
--bg: #0f1116;
--surf: #171a21;
--surf2: #1e222b;
--line: #2b303b;
--ink: #eceef3;
--mut: #99a1b3;
--acc: #7fa6ff;
--accs: #1a2340;
}
}
@supports (color: oklch(0.5 0.1 250)) {
.cq-01,
.cq-01[data-theme="light"] {
--acc: oklch(0.5 0.19 264);
--accs: oklch(0.95 0.035 264);
}
.cq-01[data-theme="dark"] {
--acc: oklch(0.78 0.13 264);
--accs: oklch(0.28 0.07 264);
}
@media (prefers-color-scheme: dark) {
.cq-01:not([data-theme="light"]) {
--acc: oklch(0.78 0.13 264);
--accs: oklch(0.28 0.07 264);
}
}
}
.cq-01 {
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;
font-size: 100%;
line-height: 1.5;
}
.cq-01 *,
.cq-01 *::before,
.cq-01 *::after {
box-sizing: border-box;
}
.cq-01__stage {
max-width: 68rem;
margin-inline: auto;
padding: clamp(1rem, 3vw, 2.25rem);
display: grid;
gap: 1.25rem;
}
.cq-01__kicker {
margin: 0 0 0.35rem;
font-size: 0.7rem;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--acc);
}
.cq-01__h {
margin: 0;
font-size: clamp(1.35rem, 1rem + 2cqi, 2rem);
font-weight: 600;
letter-spacing: -0.01em;
text-wrap: balance;
}
.cq-01__lede {
margin: 0.35rem 0 0;
color: var(--mut);
font-size: 0.82rem;
max-width: 46ch;
}
.cq-01__anno {
margin: 0;
padding: 0.75rem;
list-style: none;
display: grid;
gap: 0.5rem;
background: var(--surf);
border: 1px solid var(--line);
border-radius: 6px;
}
.cq-01__anno li {
display: flex;
gap: 0.6rem;
align-items: flex-start;
min-inline-size: 0;
}
.cq-01__anno li + li {
border-top: 1px dashed var(--line);
padding-top: 0.5rem;
}
.cq-01__n {
flex: none;
width: 1.35rem;
height: 1.35rem;
display: grid;
place-items: center;
border-radius: 50%;
background: var(--accs);
color: var(--acc);
font-size: 0.7rem;
font-weight: 700;
}
.cq-01__anno b {
display: block;
font-size: 0.74rem;
letter-spacing: 0.02em;
}
.cq-01__anno code {
display: block;
margin-top: 0.15rem;
font-size: 0.72rem;
color: var(--mut);
overflow-wrap: anywhere;
}
.cq-01__row {
display: flex;
flex-wrap: wrap;
gap: 1rem;
align-items: start;
}
.cq-01__col {
flex: 1 1 22rem;
min-inline-size: 0;
display: grid;
gap: 0.45rem;
}
.cq-01__col--narrow {
flex: 0 1 17rem;
}
.cq-01__label {
margin: 0;
font-size: 0.72rem;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--mut);
display: flex;
gap: 0.4rem;
align-items: baseline;
}
.cq-01__label span {
color: var(--acc);
text-transform: none;
letter-spacing: 0;
}
.cq-01__slot {
container-type: inline-size;
container-name: cq-01-slot;
padding: 0.6rem;
background: var(--surf2);
border: 1px dashed var(--line);
border-radius: 8px;
}
.cq-01__card {
display: grid;
gap: 0.7rem;
padding: 0.7rem;
background: var(--surf);
border: 1px solid var(--line);
border-radius: 6px;
}
.cq-01__thumb {
aspect-ratio: 16 / 9;
border-radius: 4px;
background: linear-gradient(135deg, #1f57cf 0%, #6f8cf5 45%, #b9c9ff 100%);
}
.cq-01__body {
min-inline-size: 0;
display: grid;
gap: 0.3rem;
align-content: start;
}
.cq-01__tag {
margin: 0;
font-size: 0.66rem;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--acc);
}
.cq-01__title {
margin: 0;
font-size: 0.95rem;
font-weight: 600;
line-height: 1.25;
}
.cq-01__text {
margin: 0;
font-size: 0.78rem;
color: var(--mut);
}
.cq-01__meta {
margin: 0.15rem 0 0;
font-size: 0.7rem;
color: var(--mut);
}
@container cq-01-slot (min-width: 21em) {
.cq-01__card {
grid-template-columns: 9rem 1fr;
align-items: start;
padding: 0.85rem;
}
.cq-01__thumb {
aspect-ratio: auto;
block-size: 100%;
min-block-size: 5.5rem;
}
.cq-01__title {
font-size: 1.1rem;
}
}
.cq-01__foot {
margin: 0;
font-size: 0.74rem;
color: var(--mut);
border-top: 1px solid var(--line);
padding-top: 0.75rem;
max-width: 62ch;
}
/* 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-01 {
position: relative;
}
.cq-01__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-01__themelegend {
position: absolute;
inline-size: 1px;
block-size: 1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-01__themeinput {
position: absolute;
inline-size: 1px;
block-size: 1px;
margin: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-01__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-01__themeopt:hover {
opacity: 0.85;
}
.cq-01__themeinput:checked + .cq-01__themeopt {
opacity: 1;
background: color-mix(in srgb, currentColor 15%, transparent);
}
.cq-01__themeinput:focus-visible + .cq-01__themeopt {
outline: 2px solid currentColor;
outline-offset: 2px;
}
.cq-01:has(#cq-01-theme-light:checked) {
--bg: #f4f5f7;
--surf: #ffffff;
--surf2: #eaecf1;
--line: #d6d9e0;
--ink: #15171c;
--mut: #5d6473;
--acc: #1f57cf;
--accs: #e7eefc;
}
.cq-01:has(#cq-01-theme-dark:checked) {
--bg: #0f1116;
--surf: #171a21;
--surf2: #1e222b;
--line: #2b303b;
--ink: #eceef3;
--mut: #99a1b3;
--acc: #7fa6ff;
--accs: #1a2340;
}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: Container Queries Getting Started
Source: https://codefronts.com/layouts/css-container-queries/container-queries-getting-started/
The reference implementation the rest of this collection borrows from: container-type: inline-size on the parent, an @container (min-width: 21em) block in the stylesheet, and the child rules inside it. The same card markup sits in a wide slot and a narrow slot side by side, so one query produces two layouts, and an annotated panel labels each of the three moving parts.
## HTML
```html
<section class="cq-01" aria-labelledby="cq-01-h">
<fieldset class="cq-01__theme">
<legend class="cq-01__themelegend">Theme</legend>
<input class="cq-01__themeinput" type="radio" name="cq-01-theme" id="cq-01-theme-auto" checked>
<label class="cq-01__themeopt" for="cq-01-theme-auto">Auto</label>
<input class="cq-01__themeinput" type="radio" name="cq-01-theme" id="cq-01-theme-light">
<label class="cq-01__themeopt" for="cq-01-theme-light">Light</label>
<input class="cq-01__themeinput" type="radio" name="cq-01-theme" id="cq-01-theme-dark">
<label class="cq-01__themeopt" for="cq-01-theme-dark">Dark</label>
</fieldset>
<div class="cq-01__stage">
<header class="cq-01__head">
<p class="cq-01__kicker">Container queries / 01</p>
<h2 class="cq-01__h" id="cq-01-h">One query, two layouts</h2>
<p class="cq-01__lede">The card below is the same markup in both slots. Only the slot width differs.</p>
</header>
<ol class="cq-01__anno">
<li><span class="cq-01__n">1</span><div><b>Opt the parent in</b><code>.slot { container-type: inline-size }</code></div></li>
<li><span class="cq-01__n">2</span><div><b>Ask the question</b><code>@container (min-width: 21em)</code></div></li>
<li><span class="cq-01__n">3</span><div><b>Style the children</b><code>.card { grid-template-columns: 8rem 1fr }</code></div></li>
</ol>
<div class="cq-01__row">
<div class="cq-01__col">
<p class="cq-01__label">Main column <span>wide slot</span></p>
<div class="cq-01__slot cq-01__slot--wide">
<article class="cq-01__card">
<div class="cq-01__thumb" role="img" aria-label="Abstract gradient artwork"></div>
<div class="cq-01__body">
<p class="cq-01__tag">Field notes</p>
<h3 class="cq-01__title">Meridian ships regional failover</h3>
<p class="cq-01__text">Read replicas now promote in under nine seconds without operator input.</p>
<p class="cq-01__meta">Priya Sharma · 6 min read</p>
</div>
</article>
</div>
</div>
<div class="cq-01__col cq-01__col--narrow">
<p class="cq-01__label">Sidebar rail <span>narrow slot</span></p>
<div class="cq-01__slot cq-01__slot--narrow">
<article class="cq-01__card">
<div class="cq-01__thumb" role="img" aria-label="Abstract gradient artwork"></div>
<div class="cq-01__body">
<p class="cq-01__tag">Field notes</p>
<h3 class="cq-01__title">Meridian ships regional failover</h3>
<p class="cq-01__text">Read replicas now promote in under nine seconds without operator input.</p>
<p class="cq-01__meta">Priya Sharma · 6 min read</p>
</div>
</article>
</div>
</div>
</div>
<p class="cq-01__foot">The wide slot is past 21em, so the query matches and the card goes horizontal. The narrow slot is not, so it keeps the stacked default.</p>
</div>
</section>
```
## CSS
```css
.cq-01,
.cq-01[data-theme="light"] {
--bg: #f4f5f7;
--surf: #ffffff;
--surf2: #eaecf1;
--line: #d6d9e0;
--ink: #15171c;
--mut: #5d6473;
--acc: #1f57cf;
--accs: #e7eefc;
}
.cq-01[data-theme="dark"] {
--bg: #0f1116;
--surf: #171a21;
--surf2: #1e222b;
--line: #2b303b;
--ink: #eceef3;
--mut: #99a1b3;
--acc: #7fa6ff;
--accs: #1a2340;
}
@media (prefers-color-scheme: dark) {
.cq-01:not([data-theme="light"]) {
--bg: #0f1116;
--surf: #171a21;
--surf2: #1e222b;
--line: #2b303b;
--ink: #eceef3;
--mut: #99a1b3;
--acc: #7fa6ff;
--accs: #1a2340;
}
}
@supports (color: oklch(0.5 0.1 250)) {
.cq-01,
.cq-01[data-theme="light"] {
--acc: oklch(0.5 0.19 264);
--accs: oklch(0.95 0.035 264);
}
.cq-01[data-theme="dark"] {
--acc: oklch(0.78 0.13 264);
--accs: oklch(0.28 0.07 264);
}
@media (prefers-color-scheme: dark) {
.cq-01:not([data-theme="light"]) {
--acc: oklch(0.78 0.13 264);
--accs: oklch(0.28 0.07 264);
}
}
}
.cq-01 {
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;
font-size: 100%;
line-height: 1.5;
}
.cq-01 *,
.cq-01 *::before,
.cq-01 *::after {
box-sizing: border-box;
}
.cq-01__stage {
max-width: 68rem;
margin-inline: auto;
padding: clamp(1rem, 3vw, 2.25rem);
display: grid;
gap: 1.25rem;
}
.cq-01__kicker {
margin: 0 0 0.35rem;
font-size: 0.7rem;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--acc);
}
.cq-01__h {
margin: 0;
font-size: clamp(1.35rem, 1rem + 2cqi, 2rem);
font-weight: 600;
letter-spacing: -0.01em;
text-wrap: balance;
}
.cq-01__lede {
margin: 0.35rem 0 0;
color: var(--mut);
font-size: 0.82rem;
max-width: 46ch;
}
.cq-01__anno {
margin: 0;
padding: 0.75rem;
list-style: none;
display: grid;
gap: 0.5rem;
background: var(--surf);
border: 1px solid var(--line);
border-radius: 6px;
}
.cq-01__anno li {
display: flex;
gap: 0.6rem;
align-items: flex-start;
min-inline-size: 0;
}
.cq-01__anno li + li {
border-top: 1px dashed var(--line);
padding-top: 0.5rem;
}
.cq-01__n {
flex: none;
width: 1.35rem;
height: 1.35rem;
display: grid;
place-items: center;
border-radius: 50%;
background: var(--accs);
color: var(--acc);
font-size: 0.7rem;
font-weight: 700;
}
.cq-01__anno b {
display: block;
font-size: 0.74rem;
letter-spacing: 0.02em;
}
.cq-01__anno code {
display: block;
margin-top: 0.15rem;
font-size: 0.72rem;
color: var(--mut);
overflow-wrap: anywhere;
}
.cq-01__row {
display: flex;
flex-wrap: wrap;
gap: 1rem;
align-items: start;
}
.cq-01__col {
flex: 1 1 22rem;
min-inline-size: 0;
display: grid;
gap: 0.45rem;
}
.cq-01__col--narrow {
flex: 0 1 17rem;
}
.cq-01__label {
margin: 0;
font-size: 0.72rem;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--mut);
display: flex;
gap: 0.4rem;
align-items: baseline;
}
.cq-01__label span {
color: var(--acc);
text-transform: none;
letter-spacing: 0;
}
.cq-01__slot {
container-type: inline-size;
container-name: cq-01-slot;
padding: 0.6rem;
background: var(--surf2);
border: 1px dashed var(--line);
border-radius: 8px;
}
.cq-01__card {
display: grid;
gap: 0.7rem;
padding: 0.7rem;
background: var(--surf);
border: 1px solid var(--line);
border-radius: 6px;
}
.cq-01__thumb {
aspect-ratio: 16 / 9;
border-radius: 4px;
background: linear-gradient(135deg, #1f57cf 0%, #6f8cf5 45%, #b9c9ff 100%);
}
.cq-01__body {
min-inline-size: 0;
display: grid;
gap: 0.3rem;
align-content: start;
}
.cq-01__tag {
margin: 0;
font-size: 0.66rem;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--acc);
}
.cq-01__title {
margin: 0;
font-size: 0.95rem;
font-weight: 600;
line-height: 1.25;
}
.cq-01__text {
margin: 0;
font-size: 0.78rem;
color: var(--mut);
}
.cq-01__meta {
margin: 0.15rem 0 0;
font-size: 0.7rem;
color: var(--mut);
}
@container cq-01-slot (min-width: 21em) {
.cq-01__card {
grid-template-columns: 9rem 1fr;
align-items: start;
padding: 0.85rem;
}
.cq-01__thumb {
aspect-ratio: auto;
block-size: 100%;
min-block-size: 5.5rem;
}
.cq-01__title {
font-size: 1.1rem;
}
}
.cq-01__foot {
margin: 0;
font-size: 0.74rem;
color: var(--mut);
border-top: 1px solid var(--line);
padding-top: 0.75rem;
max-width: 62ch;
}
/* 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-01 {
position: relative;
}
.cq-01__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-01__themelegend {
position: absolute;
inline-size: 1px;
block-size: 1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-01__themeinput {
position: absolute;
inline-size: 1px;
block-size: 1px;
margin: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-01__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-01__themeopt:hover {
opacity: 0.85;
}
.cq-01__themeinput:checked + .cq-01__themeopt {
opacity: 1;
background: color-mix(in srgb, currentColor 15%, transparent);
}
.cq-01__themeinput:focus-visible + .cq-01__themeopt {
outline: 2px solid currentColor;
outline-offset: 2px;
}
.cq-01:has(#cq-01-theme-light:checked) {
--bg: #f4f5f7;
--surf: #ffffff;
--surf2: #eaecf1;
--line: #d6d9e0;
--ink: #15171c;
--mut: #5d6473;
--acc: #1f57cf;
--accs: #e7eefc;
}
.cq-01:has(#cq-01-theme-dark:checked) {
--bg: #0f1116;
--surf: #171a21;
--surf2: #1e222b;
--line: #2b303b;
--ink: #eceef3;
--mut: #99a1b3;
--acc: #7fa6ff;
--accs: #1a2340;
}
```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: Container Queries Getting Started
Source: https://codefronts.com/layouts/css-container-queries/container-queries-getting-started/
The reference implementation the rest of this collection borrows from: container-type: inline-size on the parent, an @container (min-width: 21em) block in the stylesheet, and the child rules inside it. The same card markup sits in a wide slot and a narrow slot side by side, so one query produces two layouts, and an annotated panel labels each of the three moving parts.
## HTML
```html
<section class="cq-01" aria-labelledby="cq-01-h">
<fieldset class="cq-01__theme">
<legend class="cq-01__themelegend">Theme</legend>
<input class="cq-01__themeinput" type="radio" name="cq-01-theme" id="cq-01-theme-auto" checked>
<label class="cq-01__themeopt" for="cq-01-theme-auto">Auto</label>
<input class="cq-01__themeinput" type="radio" name="cq-01-theme" id="cq-01-theme-light">
<label class="cq-01__themeopt" for="cq-01-theme-light">Light</label>
<input class="cq-01__themeinput" type="radio" name="cq-01-theme" id="cq-01-theme-dark">
<label class="cq-01__themeopt" for="cq-01-theme-dark">Dark</label>
</fieldset>
<div class="cq-01__stage">
<header class="cq-01__head">
<p class="cq-01__kicker">Container queries / 01</p>
<h2 class="cq-01__h" id="cq-01-h">One query, two layouts</h2>
<p class="cq-01__lede">The card below is the same markup in both slots. Only the slot width differs.</p>
</header>
<ol class="cq-01__anno">
<li><span class="cq-01__n">1</span><div><b>Opt the parent in</b><code>.slot { container-type: inline-size }</code></div></li>
<li><span class="cq-01__n">2</span><div><b>Ask the question</b><code>@container (min-width: 21em)</code></div></li>
<li><span class="cq-01__n">3</span><div><b>Style the children</b><code>.card { grid-template-columns: 8rem 1fr }</code></div></li>
</ol>
<div class="cq-01__row">
<div class="cq-01__col">
<p class="cq-01__label">Main column <span>wide slot</span></p>
<div class="cq-01__slot cq-01__slot--wide">
<article class="cq-01__card">
<div class="cq-01__thumb" role="img" aria-label="Abstract gradient artwork"></div>
<div class="cq-01__body">
<p class="cq-01__tag">Field notes</p>
<h3 class="cq-01__title">Meridian ships regional failover</h3>
<p class="cq-01__text">Read replicas now promote in under nine seconds without operator input.</p>
<p class="cq-01__meta">Priya Sharma · 6 min read</p>
</div>
</article>
</div>
</div>
<div class="cq-01__col cq-01__col--narrow">
<p class="cq-01__label">Sidebar rail <span>narrow slot</span></p>
<div class="cq-01__slot cq-01__slot--narrow">
<article class="cq-01__card">
<div class="cq-01__thumb" role="img" aria-label="Abstract gradient artwork"></div>
<div class="cq-01__body">
<p class="cq-01__tag">Field notes</p>
<h3 class="cq-01__title">Meridian ships regional failover</h3>
<p class="cq-01__text">Read replicas now promote in under nine seconds without operator input.</p>
<p class="cq-01__meta">Priya Sharma · 6 min read</p>
</div>
</article>
</div>
</div>
</div>
<p class="cq-01__foot">The wide slot is past 21em, so the query matches and the card goes horizontal. The narrow slot is not, so it keeps the stacked default.</p>
</div>
</section>
```
## CSS
```css
.cq-01,
.cq-01[data-theme="light"] {
--bg: #f4f5f7;
--surf: #ffffff;
--surf2: #eaecf1;
--line: #d6d9e0;
--ink: #15171c;
--mut: #5d6473;
--acc: #1f57cf;
--accs: #e7eefc;
}
.cq-01[data-theme="dark"] {
--bg: #0f1116;
--surf: #171a21;
--surf2: #1e222b;
--line: #2b303b;
--ink: #eceef3;
--mut: #99a1b3;
--acc: #7fa6ff;
--accs: #1a2340;
}
@media (prefers-color-scheme: dark) {
.cq-01:not([data-theme="light"]) {
--bg: #0f1116;
--surf: #171a21;
--surf2: #1e222b;
--line: #2b303b;
--ink: #eceef3;
--mut: #99a1b3;
--acc: #7fa6ff;
--accs: #1a2340;
}
}
@supports (color: oklch(0.5 0.1 250)) {
.cq-01,
.cq-01[data-theme="light"] {
--acc: oklch(0.5 0.19 264);
--accs: oklch(0.95 0.035 264);
}
.cq-01[data-theme="dark"] {
--acc: oklch(0.78 0.13 264);
--accs: oklch(0.28 0.07 264);
}
@media (prefers-color-scheme: dark) {
.cq-01:not([data-theme="light"]) {
--acc: oklch(0.78 0.13 264);
--accs: oklch(0.28 0.07 264);
}
}
}
.cq-01 {
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;
font-size: 100%;
line-height: 1.5;
}
.cq-01 *,
.cq-01 *::before,
.cq-01 *::after {
box-sizing: border-box;
}
.cq-01__stage {
max-width: 68rem;
margin-inline: auto;
padding: clamp(1rem, 3vw, 2.25rem);
display: grid;
gap: 1.25rem;
}
.cq-01__kicker {
margin: 0 0 0.35rem;
font-size: 0.7rem;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--acc);
}
.cq-01__h {
margin: 0;
font-size: clamp(1.35rem, 1rem + 2cqi, 2rem);
font-weight: 600;
letter-spacing: -0.01em;
text-wrap: balance;
}
.cq-01__lede {
margin: 0.35rem 0 0;
color: var(--mut);
font-size: 0.82rem;
max-width: 46ch;
}
.cq-01__anno {
margin: 0;
padding: 0.75rem;
list-style: none;
display: grid;
gap: 0.5rem;
background: var(--surf);
border: 1px solid var(--line);
border-radius: 6px;
}
.cq-01__anno li {
display: flex;
gap: 0.6rem;
align-items: flex-start;
min-inline-size: 0;
}
.cq-01__anno li + li {
border-top: 1px dashed var(--line);
padding-top: 0.5rem;
}
.cq-01__n {
flex: none;
width: 1.35rem;
height: 1.35rem;
display: grid;
place-items: center;
border-radius: 50%;
background: var(--accs);
color: var(--acc);
font-size: 0.7rem;
font-weight: 700;
}
.cq-01__anno b {
display: block;
font-size: 0.74rem;
letter-spacing: 0.02em;
}
.cq-01__anno code {
display: block;
margin-top: 0.15rem;
font-size: 0.72rem;
color: var(--mut);
overflow-wrap: anywhere;
}
.cq-01__row {
display: flex;
flex-wrap: wrap;
gap: 1rem;
align-items: start;
}
.cq-01__col {
flex: 1 1 22rem;
min-inline-size: 0;
display: grid;
gap: 0.45rem;
}
.cq-01__col--narrow {
flex: 0 1 17rem;
}
.cq-01__label {
margin: 0;
font-size: 0.72rem;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--mut);
display: flex;
gap: 0.4rem;
align-items: baseline;
}
.cq-01__label span {
color: var(--acc);
text-transform: none;
letter-spacing: 0;
}
.cq-01__slot {
container-type: inline-size;
container-name: cq-01-slot;
padding: 0.6rem;
background: var(--surf2);
border: 1px dashed var(--line);
border-radius: 8px;
}
.cq-01__card {
display: grid;
gap: 0.7rem;
padding: 0.7rem;
background: var(--surf);
border: 1px solid var(--line);
border-radius: 6px;
}
.cq-01__thumb {
aspect-ratio: 16 / 9;
border-radius: 4px;
background: linear-gradient(135deg, #1f57cf 0%, #6f8cf5 45%, #b9c9ff 100%);
}
.cq-01__body {
min-inline-size: 0;
display: grid;
gap: 0.3rem;
align-content: start;
}
.cq-01__tag {
margin: 0;
font-size: 0.66rem;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--acc);
}
.cq-01__title {
margin: 0;
font-size: 0.95rem;
font-weight: 600;
line-height: 1.25;
}
.cq-01__text {
margin: 0;
font-size: 0.78rem;
color: var(--mut);
}
.cq-01__meta {
margin: 0.15rem 0 0;
font-size: 0.7rem;
color: var(--mut);
}
@container cq-01-slot (min-width: 21em) {
.cq-01__card {
grid-template-columns: 9rem 1fr;
align-items: start;
padding: 0.85rem;
}
.cq-01__thumb {
aspect-ratio: auto;
block-size: 100%;
min-block-size: 5.5rem;
}
.cq-01__title {
font-size: 1.1rem;
}
}
.cq-01__foot {
margin: 0;
font-size: 0.74rem;
color: var(--mut);
border-top: 1px solid var(--line);
padding-top: 0.75rem;
max-width: 62ch;
}
/* 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-01 {
position: relative;
}
.cq-01__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-01__themelegend {
position: absolute;
inline-size: 1px;
block-size: 1px;
padding: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-01__themeinput {
position: absolute;
inline-size: 1px;
block-size: 1px;
margin: 0;
overflow: hidden;
clip-path: inset(50%);
}
.cq-01__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-01__themeopt:hover {
opacity: 0.85;
}
.cq-01__themeinput:checked + .cq-01__themeopt {
opacity: 1;
background: color-mix(in srgb, currentColor 15%, transparent);
}
.cq-01__themeinput:focus-visible + .cq-01__themeopt {
outline: 2px solid currentColor;
outline-offset: 2px;
}
.cq-01:has(#cq-01-theme-light:checked) {
--bg: #f4f5f7;
--surf: #ffffff;
--surf2: #eaecf1;
--line: #d6d9e0;
--ink: #15171c;
--mut: #5d6473;
--acc: #1f57cf;
--accs: #e7eefc;
}
.cq-01:has(#cq-01-theme-dark:checked) {
--bg: #0f1116;
--surf: #171a21;
--surf2: #1e222b;
--line: #2b303b;
--ink: #eceef3;
--mut: #99a1b3;
--acc: #7fa6ff;
--accs: #1a2340;
}
```How this works
A container query is three separate things, and skipping any one of them is why the first attempt usually fails. First, an ancestor opts into being measured with container-type: inline-size. Second, an @container block asks a question about that ancestor. Third, the rules inside the block target descendants of it:
.cq-01__slot { container-type: inline-size; }
@container (min-width: 21em) {
.cq-01__card { grid-template-columns: 8rem 1fr; }
}The query measures the container and the styles apply to its descendants — never to the container itself. This is the rule that trips everyone. Writing container-type on the card and then trying to restyle that same card inside @container does nothing, because the element that establishes the context is outside the query's reach. If a component needs to restyle itself, the container has to move up one level.
Nothing about the page is baked into the component. The two slots here differ only in width — one is a wide main column, one is a narrow sidebar rail. The card's markup and class names are byte-for-byte identical in both, which is the whole reason this feature exists: a media query would have given both slots the same answer, because the viewport is the same for both.
The trap worth knowing on day one: container-type: inline-size applies size containment on the inline axis, so a container with no definite inline width has nothing to measure and the query silently never matches. A floated or absolutely positioned wrapper that shrink-wraps its contents is the usual culprit; give the container a real width from its own parent's layout and the query starts firing.
Make it yours
- Move the breakpoint by editing the single
21emvalue —emunits in a container query resolve against the container's own font size, so the switch tracks type scale rather than a pixel guess. - Add a second stage with another
@container (min-width: 34em)block for a three-column variant; blocks cascade in source order like any other rules. - Swap
min-widthformax-widthif you prefer to write the narrow case as the exception, or use a range query such as@container (21em <= width < 34em). - Rename the slots and give each a
container-nameto make the comparison explicit once you add nesting — demo 03 covers why that matters. - Change the wide slot's
flex-basisto see the query flip live without touching the query itself. - Recolour by editing the six tokens on the
.cq-01root; the annotation panel inherits its accent from--acc.
Gotchas — read before shipping
- Putting
container-typeon the element you want to restyle is the most common failure in the whole topic. The query reads the container; the rules apply to its descendants. - A container with no definite inline size gives the query nothing to measure, so it never matches. Shrink-wrapping wrappers — floats, absolutely positioned boxes, inline-level elements — are the usual cause.
container-typeestablishes a new containing block for absolutely positioned descendants, so aninset: 0overlay that used to escape the wrapper now stops at it.- Percentage and
emlengths inside the query resolve against the container, not the viewport — a21emthreshold moves if you change the container's font size.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 114+ | 17.5+ | 121+ | 114+ |
Floor set by :has(), oklch(), color-mix(), backdrop-filter, @container, text-wrap as this demo is written. The core technique itself goes back further — Chrome 111+.
container-type: inline-size and the @container at-rule are the real dependency here and they have been Baseline since February 2023 (Chrome 105 / Safari 16 / Firefox 110) — a safe feature to ship rather than an experiment. The stated Chrome and Firefox floors come from oklch(), which is used for the accent ramp behind an @supports test with an sRGB hex declared first. Below the @container floor the block is skipped and both slots keep the stacked single-column card declared before it, which is a usable layout rather than a broken one.