Container queries are a CSS feature that apply styles to an element based on the size of its nearest ancestor query container instead of the browser viewport. They are defined in the W3C CSS Containment Module Level 3 through the container-type property, the @container rule and the cqw and cqi units. The thing people search for most is that the container must opt in with container-type.
You mark an ancestor as a query container with container-type, then write an @container rule that tests that ancestor's size. Descendants matching the rule get the styles, and the browser re-evaluates the rule whenever the container resizes.
@container name (...) query can target. Without a name, the query matches the nearest ancestor that is a query container for that axis.The element that carries container-type can never be styled by its own query. Only its descendants are affected, which is what prevents circular sizing.
.card { container-type: inline-size; container-name: card; }
.title { font-size: 16px; }
@container card (inline-size > 400px) { .title { font-size: 28px; } }
.note { font-size: 10cqi; }
Run in headless Chromium 141 with two .card wrappers, one 300px wide and one 500px wide:
300px card title: 16px
500px card title: 28px
300px card .note (10cqi): 30px
500px card .note (10cqi): 50px
cqw is 1% of a query container's width, and cqi is 1% of its inline size. The full set is cqw, cqh, cqi, cqb, cqmin and cqmax, where cqmin and cqmax pick the smaller or larger of cqi and cqb. If no ancestor qualifies as a container on that axis, the unit falls back to the small viewport size. In the run above, 10cqi with no container on a 1000px viewport computed to 100px.
A media query asks about the viewport or device, so the same card looks identical in a wide main column and a narrow sidebar. A container query asks about the card's own box, so one component can be dropped anywhere and respond to its slot.
@container rule with no ancestor that has container-type: inline-size or size never matches. Add the property to the wrapper, not to the element you want to change.container-type: size applies size containment, so an auto-height container collapses to zero height. Use inline-size unless you really query height.container-type. Put the changing styles on a child.@container sidebar (...) matches nothing if no ancestor has container-name: sidebar.