container
An at-rule that switches the styles of elements inside a container based on the container's width. Replaces the JavaScript that watched widths with ResizeObserver and toggled classes with plain CSS.
Modern approach
Name the wrapper as a container and switch styles on its width. Because the switch depends on where the component is placed rather than on the viewport, the same component works as is in a sidebar or in the main column.
.card-wrapper {
container: card / inline-size;
}
@container card (width >= 400px) {
.card {
display: grid;
grid-template-columns: 120px 1fr;
}
}
<div class="card-wrapper">
<article class="card">
<img src="thumbnail.jpg" alt="" />
<div>...</div>
</article>
</div>Legacy approach
Media queries only know the viewport width, so switching on the width of where a component is placed required JavaScript. ResizeObserver watched the wrapper’s width and added a class to the card at 400px or wider.
.card.is-wide {
display: grid;
grid-template-columns: 120px 1fr;
}
<div class="card-wrapper">
<article class="card">
<img src="thumbnail.jpg" alt="" />
<div>...</div>
</article>
</div>
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
const card = entry.target.querySelector('.card')
card.classList.toggle('is-wide', entry.contentRect.width >= 400)
}
})
for (const wrapper of document.querySelectorAll('.card-wrapper')) {
observer.observe(wrapper)
}Things to watch when replacing
@containerstyles never apply to the container itself. Put a wrapper around the element you want to switch, and make the wrapper the container.- An element with
inline-sizeno longer takes its width from its content. On aninline-blockor a flex item sized by its content, the width becomes 0. Use it on elements whose width comes from outside, such as block elements. container-type: sizealso stops the height from coming from the content. On elements without an explicit height, useinline-size.- An unnamed
@containerqueries the nearest container. When containers are nested, it can switch on the wrong width, so name the container you mean. - Units based on the container’s width (such as
cqi) are also available. They let font sizes and spacing follow the width of where the component is placed.
Fallback
All five target browsers support it, so you normally don’t need to keep the old technique. If the narrow layout (stacked) is the base style, browsers without support simply keep it stacked. Only if those browsers also need the side-by-side layout, keep the legacy CSS and run the JavaScript only in browsers that don’t support container queries.
// Add the legacy class only in browsers that don't support container queries
if (!CSS.supports('container-type', 'inline-size')) {
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
const card = entry.target.querySelector('.card')
card.classList.toggle('is-wide', entry.contentRect.width >= 400)
}
})
for (const wrapper of document.querySelectorAll('.card-wrapper')) {
observer.observe(wrapper)
}
}Browser Support
Chrome
v105+2022-09-02Edge
v105+2022-09-01Safari
v16+2022-09-12Safari (iOS)
v16+2022-09-12Firefox
v110+2023-02-14