planeta jupiter planeta tierra

if() en CSS: condicionales sin salir de la propiedad

· 9 min de lectura · CSS

Un bloque de código con if que se divide en dos caminos, sí y no
nave extraterrestre
Un bloque de código con if que se divide en dos caminos, sí y no

CSS siempre ha tenido condiciones, pero en bloques aparte: un @media para el móvil, un @supports para lo que el navegador no entiende, una clase modificadora para cada variante. La nueva función if() mete la condición dentro del valor de la propiedad, en una sola línea. En este post vemos cómo se escribe, los tres tipos de condición que acepta, un caso práctico con botones y, muy importante, qué hacer en los navegadores que todavía no la entienden.

Explicación

Imagina que das instrucciones a alguien para preparar un café. La forma de siempre en CSS es escribir dos recetas completas: «receta para cuando hay leche» y «receta para cuando no hay». Si las dos son casi iguales, repites casi todo.

Con if() escribes una sola receta y, en el paso que cambia, dices: «añade leche si hay; si no, un poco más de agua». La condición está justo donde importa, y el resto no se repite.

En código, lo de siempre es esto:

.card {
    padding: 1rem;
}

@media (width >= 768px) {
    .card {
        padding: 2rem;
    }
}

Y con if(), esto:

.card {
    padding: if(media(width >= 768px): 2rem; else: 1rem);
}

El mismo resultado, sin repetir el selector y con las dos opciones una al lado de la otra.

La sintaxis

if() recibe una lista de ramas separadas por punto y coma. Cada rama es una condición, dos puntos y el valor que se usa si se cumple:

propiedad: if(
    condición-1: valor-1;
    condición-2: valor-2;
    else: valor-por-defecto
);
  • Las condiciones se comprueban en orden, y gana la primera que se cumple. Como un if / else if / else de cualquier lenguaje.
  • else siempre se cumple, así que va al final, como último recurso.
  • Si ninguna rama se cumple y no hay else, la propiedad vuelve a su valor inicial (o al heredado), no a lo que hubieras escrito antes. Pon siempre un else.
  • Sin espacio entre if y el paréntesis. if (…) invalida la declaración entera. Es el error más fácil de cometer y el más difícil de ver.

Los tres tipos de condición

media(): según la pantalla

Acepta las mismas características que un @media: anchos, orientación, tipo de puntero, preferencias del usuario…

.title {
    font-size: if(media(width >= 1024px): 3rem; media(width >= 768px): 2.25rem; else: 1.75rem);
}

/* con ratón, botones compactos; con el dedo, más grandes */
.icon-button {
    width: if(media(any-pointer: fine): 30px; else: 44px);
}

.hero {
    animation: if(media(prefers-reduced-motion: reduce): none; else: fade-in 0.4s ease);
}

Fíjate en el primero: tres tamaños en una línea. Para una tipografía que crece poco a poco, sin saltos, sigue siendo mejor clamp(); if() es para cuando quieres saltos concretos.

Lo que no acepta son los tipos de medio, como print o screen: if(media(print): …) no se cumple nunca, ni siquiera al imprimir. Para los estilos de impresión, sigue usando @media print.

supports(): según lo que entiende el navegador

Igual que @supports: usas algo moderno si el navegador lo entiende y otra cosa si no.

.badge {
    color: if(supports(color: oklch(70% 0.15 150)): oklch(70% 0.15 150); else: #3fa86b);
}

Con un matiz importante: solo los navegadores que entienden if() llegan a evaluar el supports(), y todos ellos ya entienden oklch(). Así que, hoy, este ejemplo no protege a nadie. supports() dentro de if() tiene sentido para funcionalidades más nuevas que el propio if(). Para todo lo demás, sigue usando @supports o un valor de respaldo en la línea anterior (lo vemos más abajo).

style(): según una variable CSS

Esta es la novedad de verdad, la que no tenía equivalente sencillo. style() comprueba el valor de una variable CSS (una custom property) del propio elemento:

.button {
    background: if(
        style(--variant: danger): #e5484d;
        style(--variant: success): #30a46c;
        else: #3e63dd
    );
}

Ahora el color depende de cuánto valga --variant. Y esa variable se puede poner en el CSS, en el HTML o desde JavaScript. Hoy solo funciona con variables, no con propiedades normales: style(display: flex) no se cumple aunque el elemento sea flex.

Admite también and, or y not, en las tres condiciones:

.panel {
    border-color: if(
        style((--variant: danger) and (--emphasis: high)): #b42318;
        not style(--variant: danger): transparent;
        else: #e5484d
    );
}

Caso práctico: variantes de un botón

Un botón con tres variantes (normal, peligro y éxito) y dos tamaños. A la antigua, con modificadores de BEM, sería así:

.button {
    padding: 0.5rem 1rem;
    background: #3e63dd;
    color: #fff;
}

.button--danger {
    background: #e5484d;
}

.button--success {
    background: #30a46c;
}

.button--large {
    padding: 0.75rem 1.5rem;
    font-size: 1.125rem;
}

.button:hover {
    background: #3451b2;
}

.button--danger:hover {
    background: #cd2b31;
}

.button--success:hover {
    background: #2b9a66;
}

Funciona, pero cada variante se reparte por varias reglas, y el :hover tiene que repetirlas todas. Además, en el orden correcto: .button:hover tiene que ir antes que los de las variantes, porque pesan lo mismo y gana el último; si lo pones al final, todos los botones se vuelven azules al pasar el ratón. Con if(), cada propiedad lleva sus casos dentro:

.button {
    --variant: default;
    --size: medium;

    padding: if(style(--size: large): 0.75rem 1.5rem; else: 0.5rem 1rem);
    font-size: if(style(--size: large): 1.125rem; else: 1rem);
    color: #fff;
    background: if(
        style(--variant: danger): #e5484d;
        style(--variant: success): #30a46c;
        else: #3e63dd
    );
}

.button:hover {
    background: if(
        style(--variant: danger): #cd2b31;
        style(--variant: success): #2b9a66;
        else: #3451b2
    );
}

Y en el HTML eliges la variante con la variable:

<button class="button">Guardar</button>
<button class="button" style="--variant: danger">Borrar</button>
<button class="button" style="--variant: success; --size: large">Publicar</button>

Si no te gusta poner style en el HTML, puedes seguir usando clases y que cada una solo cambie la variable: .button--danger { --variant: danger; }. La diferencia es que la lógica de los colores queda en un único sitio, en .button, y las clases solo eligen.

Donde más brilla es con JavaScript. Cambiar el estado es tocar una variable, sin añadir ni quitar clases:

saveButton.style.setProperty('--variant', 'success');

Los «if» que ya tenías

if() no viene a sustituir nada: se suma a otras formas de poner condiciones que ya existen y que siguen siendo la herramienta correcta en muchos casos.

  • @media: cuando cambian muchas propiedades a la vez según la pantalla. Si el menú pasa de horizontal a hamburguesa, mejor un bloque que diez if().
  • @supports: lo mismo, cuando lo que cambia con el soporte es un diseño entero (por ejemplo, un layout con subgrid).
  • @container: cuando la condición depende del tamaño de la caja que contiene al elemento, no de la pantalla. Una tarjeta que se ve distinta en la barra lateral y en el contenido principal.
  • :has(): una condición sobre el HTML. «Si esta tarjeta tiene una imagen, ponla en dos columnas»: .card:has(img) { grid-template-columns: 1fr 2fr; }.
Quiero que cambie según… Uso
La pantalla, una o dos propiedades if(media(…))
La pantalla, medio diseño @media
El tamaño de la caja que lo contiene @container
Lo que tiene dentro en el HTML :has()
Una variable (variante, estado, tema) if(style(…))
Si el navegador entiende algo @supports (o if(supports(…)) para lo más nuevo)

Soporte: cómo usarlo sin romper nada

Aquí está la letra pequeña. En el momento de escribir esto, if() funciona en Chrome y Edge desde la versión 137 (y en Opera y demás navegadores basados en Chromium), pero todavía no en Firefox ni en Safari. Antes de usarlo en un proyecto, mira la tabla de compatibilidad actualizada en MDN.

La buena noticia es que no hace falta esperar. Cuando un navegador no entiende una declaración, la ignora y se queda con la anterior. Así que basta con poner antes un valor de respaldo:

.card {
    padding: 1rem; /* para los navegadores que no entienden if() */
    padding: if(media(width >= 768px): 2rem; else: 1rem);
}

Firefox y Safari usan 1rem y Chrome usa el if(). Con media() es fácil: el respaldo es el valor de móvil y en pantallas grandes esos navegadores se quedan con él. Si eso no te vale, de momento sigue con el @media de siempre.

Con style() hay que pensarlo más, porque no tiene un equivalente sencillo: el respaldo será la variante por defecto. En el ejemplo del botón, en Firefox todos los botones saldrían azules. Si eso es aceptable (una mejora visual, no algo que el usuario necesita para entender la página), adelante. Si un botón de «Borrar» tiene que verse rojo, de momento mantén también la clase .button--danger con su color.

Lo que if() no hace

  • No sirve para bloques enteros. Decide el valor de una propiedad. Si cambian ocho, son ocho if(): ahí gana @media.
  • style() no mira propiedades normales. Solo variables CSS: no puedes preguntar «si este elemento es display: flex».
  • No mira al padre. style() dentro de if() comprueba la variable del propio elemento. Como las variables se heredan, si la pones en el padre también la ve el hijo, pero para preguntar por el tamaño del contenedor sigues necesitando @container.
  • No sustituye a la lógica de verdad. Si una decisión depende de datos (si el usuario es administrador, si el pedido está pagado), se toma en el servidor o en JavaScript, y CSS solo recibe el resultado en forma de clase o de variable.

El resumen

Necesito… Uso
Un valor según el ancho de pantalla if(media(width >= 768px): …; else: …)
Un valor según el soporte de algo más nuevo que if() if(supports(…): …; else: …)
Variantes con una variable if(style(--variant: danger): …; else: …)
Combinar condiciones and, or, not
No romper Firefox y Safari Un valor normal en la línea anterior
Cambiar medio diseño @media, @container o :has()

Y ya está

if() pone la condición donde importa: en el valor que cambia, no en un bloque aparte que repite el selector. Para una o dos propiedades que cambian según la pantalla, y sobre todo para las variantes con style(), deja el CSS mucho más corto y fácil de seguir. Úsalo ya con un valor de respaldo delante, y cuando llegue a Firefox y Safari solo tendrás que borrar una línea.

← Volver al blog

Mi hija de 11 años me ayudó a crear el diseño de este portfolio.

Categorías del blog