Cuándo usar CSS `if()` vs. consultas de estilo de contenedor
Compara CSS if() y container style queries: cuándo usar cada una, errores de sintaxis, rango y compatibilidad de navegadores en 2026.
Usa la función CSS if() cuando un único elemento deba decidir su propio valor de forma inline — tokens de tematización, estados de componentes, ajustes puntuales de propiedades responsivas. Recurre a una consulta de estilo de contenedor (@container style(...)) cuando el estilo de un elemento padre deba controlar un conjunto de reglas a lo largo de múltiples descendientes. El modelo mental en una línea: if() produce un valor condicional local al elemento; una consulta de estilo de contenedor establece un contexto, aplicando bloques de reglas condicionales desde un padre hacia sus hijos.
Ambos son mecanismos de CSS condicional que leen propiedades personalizadas, ambos utilizan la misma primitiva de consulta style(), y sus capacidades se solapan lo suficiente como para que elegir el incorrecto genere código incómodo. Este artículo resuelve ese solapamiento: qué hace cada uno, la diferencia mecánica que determina cuál usar, los dos problemas de sintaxis que más confunden (los puntos y coma y la notación de dos puntos frente a igualdad), la sintaxis de rangos presente en ambos, una tabla de decisión por escenario, y cómo publicar cualquiera de los dos dado que el soporte de navegadores está dividido en tres grupos a mediados de 2026.
Puntos clave
if()lee propiedades personalizadas declaradas en la misma regla sobre el mismo elemento y devuelve un valor;@container style()siempre mira hacia arriba en la cascada y aplica bloques de reglas completos a los descendientes.- Dentro de
if(), los puntos y coma (no las comas) separan los pares condición–valor, yelseproporciona el valor de reserva; si ninguna condición coincide y no hayelse, la función devuelve un valor garantizadamente inválido que se resuelve al valor inicial de la propiedad. - Dentro de
style(), la notación de dos puntos (style(--n: 3)) realiza comparación de tokens/cadenas sin matemáticas, mientras que la notación con operador de comparación (style(--n = 3)) analiza ambos lados numéricamente y evalúacalc()primero. - A mediados de 2026, el soporte diverge: las consultas de estilo de contenedor básicas están disponibles en los cuatro motores principales (Chrome 111+, Edge 111+, Safari 18+, Firefox 151+), mientras que
if()y la sintaxis de rangos habilitada por defecto siguen siendo exclusivas de Chromium. - Dado que
if()es exclusivo de Chromium, cada declaraciónif()necesita una declaración de reserva de la misma propiedad antes de ella, o una guardia@supports.
Qué es cada mecanismo, en esencia
La función if() es un condicional a nivel de valor que se coloca dentro de cualquier propiedad. Su argumento es una lista de pares condición–valor separados por puntos y coma, donde cada condición se separa de su valor mediante dos puntos, y un else opcional proporciona el valor por defecto:
.badge {
color: if(
style(--variant: danger): white;
style(--variant: muted): #555;
else: black;
);
}
Cada condición es uno de tres tipos de consulta:
style()comprueba una propiedad personalizada en el elemento actual;media()ejecuta una media query inline;supports()ejecuta una consulta de características.
La referencia de MDN documenta los tres con la misma gramática de puntos y coma — por ejemplo, flex-direction: if(media(orientation: landscape): row; else: column;) cambia una única propiedad según el resultado de la media query.
Una consulta de estilo de contenedor es una regla at-rule @container cuya condición es una o más notaciones style() que leen las propiedades personalizadas de un elemento contenedor. Envuelve un bloque de reglas en lugar de producir un único valor:
.card { --status: active; }
@container style(--status: active) {
.card-title { font-weight: 700; }
.card-icon { opacity: 1; }
.card-meta { color: green; }
}
Según la guía de consultas de contenedor de MDN, todo elemento es un contenedor de estilo por defecto — a diferencia de las consultas de tamaño, no es necesario establecer un container-type para las consultas de estilo. Esto elimina la principal fricción de configuración que los desarrolladores esperan de las consultas de contenedor.
La distinción fundamental que determina el uso
Discover how at OpenReplay.com.
La diferencia mecánica es el alcance de la búsqueda. if() lee propiedades personalizadas declaradas en la misma regla sobre el mismo elemento y es autocontenido, mientras que @container style() siempre mira hacia arriba en la cascada hacia un contenedor ancestro. MDN lo afirma directamente: usar una consulta de estilo dentro de if() permite apuntar a un elemento basándose en si una propiedad personalizada está definida en él, en lugar de comprobar estilos en un padre. Ambos se presentan explícitamente como complementarios, no redundantes.
Esa diferencia en el alcance de búsqueda tiene tres consecuencias prácticas:
-
Granularidad de salida.
if()establece exactamente un valor.@containeraplica un bloque de reglas completo — múltiples declaraciones en múltiples selectores — cuando su condición es verdadera. Como indica MDN, conif()solo se pueden establecer valores de propiedades individuales, mientras que las consultas@containeraplican condicionalmente conjuntos completos de reglas. -
Dirección.
if()puede leer una propiedad declarada en el mismo elemento que está estilizando.@containerno puede leer el valor de su propio elemento para estilizarlo; lee el de un ancestro y estiliza a los descendientes. -
Alcance. Solo las consultas de estilo de contenedor pueden delimitar el alcance mediante
container-name. Al nombrar un contenedor, la consulta se evalúa únicamente contra ese contenedor; si el contenedor nombrado no define la propiedad consultada, el bloque simplemente no se ejecuta.if()no tiene un control de alcance equivalente — siempre se resuelve contra el elemento en el que se encuentra.
Así, el criterio de desempate en la zona de solapamiento es: si una propiedad en un elemento debe cambiar, if() implica menos indirección. Si varios descendientes deben reaccionar a un valor del padre, @container expresa eso con una única condición en lugar de repetir if() en cada hijo.
Los problemas de sintaxis que más confunden
Los puntos y coma, no las comas, separan los pares en if(). Esto confunde a todos los que vienen de JavaScript o de funciones CSS separadas por comas como rgb(). La gramática es if(condición-1: valor-1; condición-2: valor-2; else: reserva), siendo el punto y coma tras el último par opcional. Tampoco debe haber espacio entre if y el paréntesis de apertura, o toda la declaración será inválida. Y cuando ninguna condición coincide y se omitió else, if() devuelve un valor garantizadamente inválido — en el contexto de una propiedad normal, esto se resuelve al valor inicial de la propiedad, lo que raramente es lo deseado. Incluye siempre else.
La distinción entre dos puntos e igualdad dentro de style() es el detalle sutil y de alto valor que casi nadie explica. Tanto if() como las consultas de estilo de contenedor aceptan dos notaciones con comportamientos genuinamente diferentes, ahora documentadas en MDN.
La notación simple (dos puntos) realiza comparación de tokens/cadenas de valores calculados y no ejecuta matemáticas. La notación de rango (operador) — usando =, <, <=, >, o >= — resuelve cada lado, analiza ambos como tipo numérico, evalúa cualquier calc() y compara numéricamente. La consecuencia, tomada directamente del ejemplo de MDN:
.box { --n: calc(6 / 2); }
/* FALSO: el valor calculado de --n es la cadena "calc(6/2)",
que no es el token "3" */
@container style(--n: 3) { /* … */ }
/* VERDADERO: ambos lados se analizan como <number>, calc(6/2) se calcula como 3,
y 3 = 3 */
@container style(--n = 3) { /* … */ }
Así, style(--n: 3) falla contra --n: calc(6/2), mientras que style(--n = 3) tiene éxito. Hay dos reglas más a tener en cuenta. Con la notación simple, el nombre de la propiedad personalizada debe estar a la izquierda sin var() — style(var(--n): 3) es inválido — mientras que la notación de rango acepta un nombre, var(), un literal o calc() en cualquier lado y en cualquier orden. Además, los valores equivalentes (como blue y #0000ff) solo coinciden con la notación de dos puntos si la propiedad está registrada con @property y un descriptor syntax. La conclusión: usa la notación de dos puntos para comparación de palabras clave/cadenas (style(--theme: dark)), y la notación con operador para comparación numérica (style(--columns >= 3)).
La sintaxis de rangos está presente en ambos
La sintaxis de rangos amplía ambos mecanismos más allá de la coincidencia exacta hacia la comparación numérica, tomando operandos de propiedades personalizadas, attr() o valores literales. Según las notas de la versión Chrome 142, una comparación solo se resuelve cuando ambos lados se resuelven al mismo tipo de dato, limitado a siete tipos numéricos: <length>, <number>, <percentage>, <angle>, <time>, <frequency> y <resolution>. La forma de intervalo con tres valores también funciona — @container style(0 < --n < 10) — con ambos comparadores apuntando en la misma dirección.
La sintaxis de rangos es lo que hace que patrones como el de una insignia de notificación sean limpios. Lee un contador de un atributo y bifurca según umbrales:
@container style(--count > 99) {
.badge::after { content: "99+"; }
}
Dado que la notación de rango analiza numéricamente, style(--s = new) es falso para una palabra clave como new, mientras que style(--s: new) es verdadero — otra razón por la que la elección de notación es determinante.
Cuál usar, según el escenario
| Escenario | Usar | Por qué |
|---|---|---|
Token de tema → una propiedad (p. ej., color desde --theme) | if() | Un elemento decide su propio valor de forma inline |
| El estado de un componente cambia varios estilos de hijos (activo/error) | @container style() | Un valor del padre controla un bloque de reglas en los descendientes |
| Ajuste responsivo de una única propiedad | if() con media() | Lógica localizada por propiedad; no se necesita bloque @media |
| Umbrales de insignia de contador de notificaciones | Cualquiera, mediante sintaxis de rangos | @container si los elementos hermanos también reaccionan; if() si solo cambia la insignia |
| Texto con conciencia de contraste sobre un padre tematizado | @container style() | Los hijos leen el tema del padre y se reestizan juntos |
Valor condicionado por características (p. ej., lch() con reserva) | if() con supports() | Intercambio de un único valor según soporte |
| Cambio de maquetación con múltiples propiedades en un breakpoint | @media (ninguno de los dos) | Ambos son la herramienta incorrecta para cambios grandes de maquetación |
La regla general: un valor condicional en un elemento es trabajo de if(); un contexto condicional que reestiliza un subárbol es trabajo de una consulta de contenedor.
Publicar en 2026
El soporte de navegadores se divide en tres grupos, y la antigua visión de “el CSS condicional es exclusivo de Chromium” ya no es precisa. Soporte de navegadores a junio de 2026:
| Característica | Chrome / Edge | Safari | Firefox |
|---|---|---|---|
| Consultas de estilo de contenedor (propiedades personalizadas, sintaxis simple) | 111+ | 18+ | 151+ |
Función if() | 137+ | No disponible | No disponible |
Sintaxis de rangos en style() / if() | 142+ (habilitada por defecto) | No disponible | 151, detrás de layout.css.attr.enabled |
Las consultas de estilo de contenedor básicas son ahora genuinamente multiplataforma. Llegaron a Chrome y Edge 111, alcanzaron WebKit en Safari 18.0, y llegaron a Firefox 151, publicado el 19 de mayo de 2026, que añadió soporte para consultas style() de @container basadas en propiedades personalizadas de un contenedor. La función if(), en cambio, sigue siendo exclusiva de Chromium — MDN la marca como experimental con disponibilidad limitada — y la sintaxis de rangos está habilitada por defecto solo en Chromium. Firefox 151 incluye la sintaxis de rangos de style() de @container pero deshabilitada por defecto, detrás de la preferencia layout.css.attr.enabled.
La consecuencia práctica: if() no degrada de forma elegante, así que escribe primero una declaración por defecto y deja que los navegadores compatibles la sobreescriban, o envuelve la regla en una guardia @supports. Un patrón cubre ambos mecanismos:
/* reserva primero — todos los navegadores aplican esto */
.card-title { font-weight: 400; }
/* mejora progresiva: solo los motores compatibles con consultas de estilo aplican esto */
@supports (container-type: normal) {
@container style(--status: active) {
.card-title { font-weight: 700; }
}
}
/* if(): valor estático primero, luego la sobreescritura condicional */
.badge { padding: 0.25em; }
.badge { padding: if(style(--size: lg): 0.5em; else: 0.25em); }
Dado que if() y la sintaxis de rangos habilitada por defecto siguen la ruta de reserva en Firefox y Safari — navegadores que un desarrollador que usa Chrome raramente abre durante el desarrollo — la reproducción de sesiones entre navegadores es una técnica útil para observar realmente cómo se renderiza la rama de reserva en el navegador de un usuario real, en lugar de asumir que se ve correctamente. Para los patrones más profundos de calc() y anidamiento dentro de if(), el análisis orientado a decisiones de LogRocket va más lejos de lo que hay espacio aquí.
Elige según la intención, no la novedad: un valor condicional en un elemento es if(); un contexto condicional que reestiliza un subárbol desde un padre es una consulta de estilo de contenedor. Publica cualquiera de los dos con una reserva hoy, vuelve a comprobar la matriz de soporte antes de confiar en if() en producción, y los dos cubrirán cada vez más el espacio de CSS condicional que antes requería hacks de propiedades personalizadas y JavaScript.
Preguntas frecuentes
¿Pueden las consultas de estilo de contenedor leer la propiedad personalizada del propio elemento, como hace if()?
No. Una consulta de estilo de contenedor siempre mira hacia arriba en la cascada hacia un contenedor ancestro y aplica sus reglas a los descendientes, por lo que no puede leer una propiedad personalizada del mismo elemento que estilizaría. La función if() es lo opuesto: lee propiedades personalizadas declaradas en la misma regla sobre el mismo elemento y resuelve un valor de forma inline. Si necesitas que un elemento bifurque según su propia propiedad, usa if(); si el valor de un padre debe reestilizar a sus hijos, usa una consulta de estilo de contenedor.
¿Las consultas de estilo de contenedor requieren establecer un container-type, como hacen las consultas de tamaño?
No. Todo elemento es un contenedor de estilo por defecto, por lo que las consultas de estilo sobre propiedades personalizadas funcionan sin declarar container-type. Esto difiere de las consultas de tamaño de contenedor, que sí requieren container-type (como inline-size) en el ancestro consultado antes de que cualquier condición de tamaño pueda resolverse. Esto está documentado en la guía de consultas de contenedor de MDN, y elimina el principal paso de configuración que los desarrolladores esperan al usar por primera vez la regla at-rule @container.
¿Por qué mi consulta de estilo con calc() nunca coincide?
Porque la notación simple de dos puntos realiza comparación de tokens y cadenas sin matemáticas. Una propiedad establecida como calc(6 / 2) se calcula como la cadena 'calc(6/2)', que nunca coincide con el token 3, por lo que style(--n: 3) devuelve falso. Para comparar numéricamente, usa la notación con operador de comparación, style(--n = 3), que resuelve cada lado, evalúa calc(), analiza ambos como tipo numérico y compara numéricamente, devolviendo verdadero. Usa la forma de dos puntos para comparación de palabras clave y la forma con operador para comparación numérica.
¿if() degrada de forma elegante en Safari y Firefox?
No. A mediados de 2026, if() solo está disponible en navegadores Chromium (Chrome y Edge 137+), por lo que Safari y Firefox ignoran cualquier declaración que lo contenga y vuelven al valor que se aplicaba antes. Escribe primero una declaración estática por defecto y deja que los navegadores compatibles la sobreescriban, o envuelve la regla condicional en una guardia @supports. Las consultas de estilo de contenedor básicas son más seguras entre navegadores, estando disponibles en Chrome 111+, Edge 111+, Safari 18+ y Firefox 151+, pero la sintaxis de rangos habilitada por defecto sigue siendo exclusiva de Chromium.
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Star on GitHub12k