nearest ancestor → named ancestor: the disambiguation
An unnamed container variant like @md:grid-cols-2 targets the
nearest ancestor carrying class="@container".
That rule breaks down once containers nest — a child buried inside a sidebar
container plus a main container can't tell them apart. A named container
(@container/sidebar) sets container-name: sidebar, and the
variant @sm/sidebar: walks past every intermediate container
(named or not) until it finds the one named sidebar.
| concept | unnamed @container | named @container/{name} |
|---|---|---|
| what gets set | container-type: inline-size |
container-type: inline-size + container-name: {name} |
| child variant | @md:grid-cols-2 |
@md/sidebar:grid-cols-2 |
| resolution target | nearest ancestor with any @container |
nearest ancestor with @container/sidebar specifically |
| nested ambiguity | ❌ child cannot choose between two enclosing containers | ✅ child targets exactly the named one, skipping intermediates |
| typical use | single-container cards, widgets | multi-region layouts (sidebar + main), reusable components in varied slots |
1 · two named containers, two independent queries
The sidebar container is named sidebar; its child uses
@sm/sidebar:grid-cols-2 (fires when sidebar ≥ 384px). The main
container is named main; its child uses @md/main:grid-cols-3
(fires when main ≥ 448px). Drag either slider — only that container's named
query responds. The other is untouched. That independence is the whole point of naming.
@sm/sidebar:grid-cols-2 (≥ 384px)
container-name: —
@md/main:grid-cols-3 (≥ 448px)
main · 1
Responds to @md/main: only.
main · 2
3rd col appears at ≥ 448px.
main · 3
Independent of sidebar.
container-name: —
2 · named queries skip intermediate containers
When containers nest, a named variant walks up the tree ignoring every
ancestor that doesn't carry the matching @container/{name}. So an
unnamed outer container cannot "steal" a query meant for a named inner one.
| variant on child | which ancestor wins | why |
|---|---|---|
@sm:grid-cols-2 (unnamed) |
nearest @container — here the inner one |
"nearest" stops at the first container it meets, whatever the name |
@sm/sidebar:grid-cols-2 |
nearest @container/sidebar — skips the unnamed outer |
name filter; intermediates without the name are transparent to it |
@sm/main:grid-cols-2 (no @container/main above) |
no match — query never fires | if no ancestor carries that name, the variant is dead code |
Implication: a typo in the name (@sm/sidebr:) silently fails —
the query finds no matching container. The gold-check below asserts the exact
container-name value to catch this.
intent → pattern
| intent | pattern | why |
|---|---|---|
| name a container | <div class="@container/sidebar"> |
sets container-name: sidebar + container-type: inline-size |
| target a named container | @sm/sidebar:grid-cols-2 |
fires when the sidebar container ≥ 24rem (384px), regardless of viewport |
| two regions, one component | @container/sidebar + @container/main |
same component dropped in either slot adapts to that slot's width |
| range query on a named container | @max-md/sidebar:hidden |
hide when the named sidebar container < 28rem (448px) |
| named size container (block-axis units) | @container-size/hero |
use cqb/cqh units against a named size container |