Container queries
Lección 21 de 37 · 4 min
Las media queries le preguntan a la pantalla. Las container queries le preguntan al lugar donde está el componente, así la misma pieza se acomoda sola en una columna angosta o en una ancha.
Preguntarle al contenedor
Pensá en una tarjeta que aparece en la columna principal y también en una barra lateral. Con una media query, las dos ven la misma pantalla de 1280px y se dibujan igual, aunque la barra mida 300px. Lo que la tarjeta necesita saber es cuánto mide su contenedor.
Eso se resuelve en dos pasos: el padre se declara contenedor con container-type: inline-size, y sus descendientes usan una container query (@container) para aplicar reglas según el ancho del contenedor.
.panel {container-type: inline-size;}@container (width >= 400px) {.tarjeta {flex-direction: row;}}
Mové el ancho del contenedor y mirá la tarjeta: desde 400px pone el perfil de la ruta al lado del texto, y desde 480px muestra la descripción. La ventana no cambia, solo el contenedor.
Ruta por la costa
3 días · 42 km
Tres días caminando entre faros y pueblos de pescadores.
.panel {container-type: inline-size;width: 440px;}.tarjeta {display: flex;flex-direction: column;gap: 12px;}.tarjeta-titulo {font-size: clamp(1rem, 0.5rem + 3cqi, 1.5rem);}.tarjeta-texto {display: none;}/* activa */@container (width >= 400px) {.tarjeta {flex-direction: row;}}/* inactiva */@container (width >= 480px) {.tarjeta-texto {display: block;}}
<div class="@container w-110"><article class="flex flex-col gap-3 @min-[400px]:flex-row"><svg class="h-32 min-w-40" viewBox="0 0 100 40" preserveAspectRatio="none">…</svg><div><h3 class="text-[clamp(1rem,0.5rem+3cqi,1.5rem)]">Ruta por la costa</h3><p class="hidden @min-[480px]:block">Tres días caminando entre faros y pueblos de pescadores.</p></div></article></div>
Una container query nunca le aplica estilos al contenedor mismo, solo a lo que tiene adentro. Y como el ancho del contenedor no puede depender de su contenido, tiene que venir de afuera: del padre, de un grid o de un width.
Contenedores con nombre y unidades cq
Sin nombre, @container consulta al contenedor más cercano. Si hay varios anidados y querés uno en particular, ponele nombre con container-name, o usá el atajo container, que lleva el nombre y el tipo separados por una barra:
.sidebar {container: sidebar / inline-size;}@container sidebar (width >= 400px) {.tarjeta {flex-direction: row;}}
Adentro de un contenedor también tenés sus unidades: 1cqi es el 1% de su ancho y 1cqb, el 1% de su alto. Con clamp() dan tipografía fluida, que crece con el contenedor sin pasarse de un mínimo y un máximo:
.tarjeta-titulo {font-size: clamp(1rem, 0.5rem + 3cqi, 1.5rem);}
En la demo, con 280px el título mide 8px + 8.4px = 16.4px; con 400px, 20px; y a partir de unos 533px se queda en 24px. La parte en rem hace que el título siga respetando el tamaño de letra que configuró el usuario.
Tipos de contenedor y cuándo usarlos
container-type define qué se puede consultar:
- Solo el ancho
inline-sizeEs el que vas a usar casi siempre. - Ancho y alto
sizePero el alto tampoco puede salir del contenido, así que sin unheightexplícito el contenedor colapsa a 0. - Por defecto
normalNo permite consultar tamaños.
También existen las style queries, como @container style(--tema: oscuro), que consultan el valor de una custom property. Solo funcionan con custom properties y llegaron a los navegadores más tarde que las de tamaño, así que revisá el soporte antes de depender de ellas.
Usá media queries para lo que depende de la página o del usuario: el layout general, prefers-color-scheme o prefers-reduced-motion. Usá container queries para componentes que se reutilizan en lugares de distinto ancho. Se combinan bien: la media query arma las columnas y la container query acomoda lo que va adentro.
En Tailwind
Tailwind v4 trae container queries de fábrica, sin plugin. La clase @container declara el contenedor y las variantes que empiezan con @ consultan su ancho. La escala va en rem: @sm es 24rem (384px) y @md, 28rem (448px). Para un valor exacto, usá @min-[400px]:.
| CSS | Tailwind |
|---|---|
| container-type: inline-size · size | @container · @container-size |
| container: sidebar / inline-size | @container/sidebar |
| @container (width >= 28rem) · (< 28rem) | @md:* · @max-md:* |
| @container (width >= 400px) | @min-[400px]:* |
| @container sidebar (width >= 28rem) | @md/sidebar:* |
| font-size: clamp(1rem, 0.5rem + 3cqi, 1.5rem) | text-[clamp(1rem,0.5rem+3cqi,1.5rem)] |
<aside class="@container/sidebar"><article class="flex flex-col gap-3 @md/sidebar:flex-row"><img class="h-32 min-w-40 object-cover" src="…" alt="" /><div><h3 class="text-[clamp(1rem,0.5rem+3cqi,1.5rem)]">Ruta por la costa</h3><p class="hidden @lg/sidebar:block">Tres días caminando…</p></div></article></aside>
Container queries
Lesson 21 of 37 · 4 min
Media queries ask the screen. Container queries ask the place where the component sits, so the same piece adapts on its own to a narrow column or a wide one.
Ask the container
Think of a card that shows up in the main column and also in a sidebar. With a media query, both see the same 1280px screen and render the same, even if the sidebar is 300px wide. What the card needs to know is how wide its container is.
That takes two steps: the parent declares itself a container with container-type: inline-size, and its descendants use a container query (@container) to apply rules based on the container's width.
.panel {container-type: inline-size;}@container (width >= 400px) {.card {flex-direction: row;}}
Move the container width and watch the card: from 400px it puts the trail profile beside the text, and from 480px it shows the description. The window doesn't change, only the container.
Coastal trail
3 days · 42 km
Three days walking between lighthouses and fishing villages.
.panel {container-type: inline-size;width: 440px;}.card {display: flex;flex-direction: column;gap: 12px;}.card-title {font-size: clamp(1rem, 0.5rem + 3cqi, 1.5rem);}.card-text {display: none;}/* active */@container (width >= 400px) {.card {flex-direction: row;}}/* inactive */@container (width >= 480px) {.card-text {display: block;}}
<div class="@container w-110"><article class="flex flex-col gap-3 @min-[400px]:flex-row"><svg class="h-32 min-w-40" viewBox="0 0 100 40" preserveAspectRatio="none">…</svg><div><h3 class="text-[clamp(1rem,0.5rem+3cqi,1.5rem)]">Coastal trail</h3><p class="hidden @min-[480px]:block">Three days walking between lighthouses and fishing villages.</p></div></article></div>
A container query never styles the container itself, only what's inside it. And since the container's width can't depend on its content, it has to come from outside: from the parent, a grid or a width.
Named containers and cq units
Without a name, @container queries the nearest container. If several are nested and you want a specific one, name it with container-name, or use the container shorthand, which takes the name and the type separated by a slash:
.sidebar {container: sidebar / inline-size;}@container sidebar (width >= 400px) {.card {flex-direction: row;}}
Inside a container you also get its units: 1cqi is 1% of its width and 1cqb is 1% of its height. With clamp() they give you fluid type that grows with the container without going past a minimum and a maximum:
.card-title {font-size: clamp(1rem, 0.5rem + 3cqi, 1.5rem);}
In the demo, at 280px the title is 8px + 8.4px = 16.4px; at 400px, 20px; and from about 533px on it stays at 24px. The rem part keeps the title respecting the font size the user set.
Container types and when to use them
container-type sets what can be queried:
- Width only
inline-sizeThe one you'll use almost always. - Width and height
sizeBut the height can't come from the content either, so without an explicitheightthe container collapses to 0. - Default
normalIt doesn't allow size queries.
There are also style queries, like @container style(--theme: dark), which query the value of a custom property. They only work with custom properties and reached browsers later than size queries, so check support before relying on them.
Use media queries for what depends on the page or the user: the overall layout, prefers-color-scheme or prefers-reduced-motion. Use container queries for components reused in places of different widths. They work well together: the media query sets up the columns and the container query arranges what goes inside.
In Tailwind
Tailwind v4 ships container queries built in, no plugin. The @container class declares the container and variants starting with @ query its width. The scale is in rem: @sm is 24rem (384px) and @md is 28rem (448px). For an exact value, use @min-[400px]:.
| CSS | Tailwind |
|---|---|
| container-type: inline-size · size | @container · @container-size |
| container: sidebar / inline-size | @container/sidebar |
| @container (width >= 28rem) · (< 28rem) | @md:* · @max-md:* |
| @container (width >= 400px) | @min-[400px]:* |
| @container sidebar (width >= 28rem) | @md/sidebar:* |
| font-size: clamp(1rem, 0.5rem + 3cqi, 1.5rem) | text-[clamp(1rem,0.5rem+3cqi,1.5rem)] |
<aside class="@container/sidebar"><article class="flex flex-col gap-3 @md/sidebar:flex-row"><img class="h-32 min-w-40 object-cover" src="…" alt="" /><div><h3 class="text-[clamp(1rem,0.5rem+3cqi,1.5rem)]">Coastal trail</h3><p class="hidden @lg/sidebar:block">Three days walking…</p></div></article></aside>