12k
All articles

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.

OpenReplay Team
OpenReplay Team
Cuándo usar CSS `if()` vs. consultas de estilo de contenedor

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, y else proporciona el valor de reserva; si ninguna condición coincide y no hay else, 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úa calc() 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ón if() 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

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:

  1. Granularidad de salida. if() establece exactamente un valor. @container aplica un bloque de reglas completo — múltiples declaraciones en múltiples selectores — cuando su condición es verdadera. Como indica MDN, con if() solo se pueden establecer valores de propiedades individuales, mientras que las consultas @container aplican condicionalmente conjuntos completos de reglas.

  2. Dirección. if() puede leer una propiedad declarada en el mismo elemento que está estilizando. @container no puede leer el valor de su propio elemento para estilizarlo; lee el de un ancestro y estiliza a los descendientes.

  3. 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

EscenarioUsarPor 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 propiedadif() con media()Lógica localizada por propiedad; no se necesita bloque @media
Umbrales de insignia de contador de notificacionesCualquiera, 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ísticaChrome / EdgeSafariFirefox
Consultas de estilo de contenedor (propiedades personalizadas, sintaxis simple)111+18+151+
Función if()137+No disponibleNo disponible
Sintaxis de rangos en style() / if()142+ (habilitada por defecto)No disponible151, 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.

Open-source session replay

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

We use cookies to improve your experience. By using our site, you accept cookies.