Volver a las notas

CSS style queries: una variable, todos los estilos

28 de octubre de 2026·
csscontainerqueriescustompropertiesbaseline
Demo en vivoAbrir a pantalla completa

Ya puedes escribir @container style(--status: warn) y que el CSS reaccione al valor de una custom property en todos los navegadores principales. Las container style queries son Baseline desde el 19 de mayo de 2026, cuando salió Firefox 151 con soporte, que se sumó a Chrome y Edge 111 y a Safari 18.

Las container queries de tamaño se llevaron toda la atención. Las de estilo son la mitad discreta de la misma función, y para mí la más sorprendente: en lugar de preguntar "¿cuánto mide mi contenedor?", una regla pregunta "¿qué valor tiene mi contenedor en esta variable?". Eso convierte una custom property en un interruptor que el CSS sabe leer.

Qué problema resuelve

Piensa en una tarjeta de estado con tres estados: operativo, degradado y caído. Cada estado cambia el borde, el brillo, el icono, el texto de la insignia y el color de una gráfica. Lo habitual es una clase modificadora o un atributo data-status, y un selector por estado para cada pieza de la tarjeta.

Con una style query, el estado vive en una sola custom property y cualquier descendiente puede preguntar por ella:

<div class="card" style="--status: warn">
  <span class="badge">Degraded</span>
</div>
.badge { background: rgb(0, 212, 170); }

@container style(--status: warn) {
  .badge { background: rgb(251, 191, 36); }
}

Ese es todo el truco de la demo de arriba. Cada tarjeta lleva style="--status: ok" y lo único que hace el JavaScript al pulsar es card.style.setProperty('--status', next). El color, el icono ▲/✕, el texto de la insignia (con content: var(--badge)), la latencia, la barra de progreso y el anillo que late son CSS reaccionando a ese valor. La vista del tablero usa una segunda variable, --view: list, en el elemento del tablero, y hasta la línea resaltada del panel de código es una style query que lee --last.

Lo que lo diferencia de un selector de atributo es que las custom properties se heredan y entran en la cascada. El valor puede venir de un padre, de una clase de tema más arriba, de una media query o de un estilo en línea, y cualquier elemento por debajo puede consultarlo sin saber dónde se definió.

Cómo funciona

Algunas reglas que he comprobado en MDN y en la especificación, y luego en el navegador:

  • No hace falta container-type. Todos los elementos son contenedores de estilo. container-type solo es necesario para las consultas de tamaño.
  • Un elemento consulta a sus ancestros, no a sí mismo. La consulta se evalúa contra un contenedor ancestro, así que en el ejemplo .badge puede reaccionar a --status, pero una regla que intente dar estilo a .card a partir de su propio --status no encaja. Por eso cada tarjeta de la demo tiene un .body interior que lleva los estilos visuales.
  • Quédate con las custom properties. La especificación también admite propiedades normales, como style(color: red), pero la guía de MDN dice que las custom properties son la única característica de estilo con soporte por ahora, y esa consulta no encajó en el Chromium con el que he probado.
  • Una consulta sin valor comprueba "¿está definida?". style(--flag) es verdadera cuando el valor computado es distinto del valor inicial. En una custom property sin registrar, eso significa cualquier valor.

La trampa: la comparación simple compara tokens

style(--status: warn) compara el valor computado de la propiedad con el valor que has escrito. En una custom property sin registrar, ese valor computado es básicamente el texto tal cual, así que --accent: #00f no encaja con style(--accent: blue), y --n: calc(6/2) no encaja con style(--n: 3). He probado los dos casos en Chromium y ninguno encaja.

Si necesitas que valores equivalentes coincidan, registra la propiedad con un tipo:

@property --accent {
  syntax: "<color>";
  inherits: true;
  initial-value: black;
}

/* Ahora --accent: #00f también encaja */
@container style(--accent: blue) {
  .icon { color: var(--accent); }
}

Para estados tipo palabra clave, como ok, warn y down, la comparación simple es justo lo que quieres y no necesitas @property para nada.

Soporte de navegadores y otras trampas

  • Soporte: Chrome y Edge 111, Safari 18, Firefox 151. Es Baseline "recién disponible" desde el 19 de mayo de 2026, así que mira tu público antes de quitar un plan B.
  • Safari y :root: los datos de compatibilidad de MDN indican que en Safari el elemento raíz del documento no puede ser contenedor. Si defines una variable en :root y la consultas, los hijos de <body> funcionan, porque <body> hereda el valor y hace de contenedor. Una regla que dé estilo al propio <body> a partir de ella no encajará.
  • Detección de soporte: @supports no puede comprobar una at-rule como esta. En la demo uso una sonda mínima: un elemento oculto define --sq: yes, una style query le da a su hijo 7 px de ancho y el JavaScript mira el ancho computado. Si no coincide, la página añade una clase y pasa a selectores de atributo data-status que definen las mismas variables.
  • La sintaxis de rango no es Baseline. MDN documenta una forma más nueva con operadores de comparación, como style(--columns >= 3), que compara valores numéricamente. Los datos de compatibilidad de MDN indican Chrome 142 y todavía sin soporte en Safari, así que la he dejado fuera de la demo.

Qué vigilar

  • La sintaxis de rango en todos los motores. Las comparaciones numéricas como style(0 < --n < 10) es donde las style queries se vuelven de verdad expresivas. Cuando Safari las tenga, mucho código del tipo "pon una clase desde JavaScript cuando el número pase de un umbral" sobrará.
  • Consultar propiedades normales. style(display: flex) o style(font-weight: bold) están en la especificación, pero todavía no puedes contar con ellas en todos los navegadores. Cuando lleguen, los componentes podrán adaptarse a cómo está estilado su padre, no solo a variables que definas a propósito.
  • Sistemas de diseño. Una sola variable --variant o --tone en un componente, con style queries dentro, es una alternativa limpia a las pilas de clases modificadoras. Espero ver mucho más ese patrón ahora que funciona en todas partes.

Fuentes