Implementando light-dark() para imágenes en CSS: guía técnica

light-dark-css-imagenes

light-dark() para imágenes: guía práctica

Tiempo estimado de lectura: 4 min

  • light-dark() ahora soporta <image>, permitiendo alternar assets (logos, fondos, máscaras) según esquema de color sin media queries.
  • Los dos argumentos de light-dark() deben ser del mismo tipo: ambos <color> o ambos <image>; mezclar tipos invalida la regla.
  • Usa la cascada para fallbacks: declara primero un background estándar y luego light-dark() para motores modernos.
  • Beneficios: cohesión del componente, respuesta local al color-scheme y menor deuda técnica frente a variables globales dispersas.

Introducción

Back in 2023, I wrote about the future of CSS color switching using the then-novel light-dark() function. It was a game-changer for colors, allowing us to ditch the repetitive @media (prefers-color-scheme: …) blocks for simple property declarations.

But there was one glaring limitation: it only works for colors. If you wanted to swap out a background image, a mask, or a logo based on the user’s color scheme, you were stuck doing things the “old” way.

Well, I have good news. The spec has been updated, and light-dark() is being extended to support images.

Back in 2023, I wrote about the future of CSS color switching using the then-novel light-dark() function. It was a game-changer for colors, allowing us to ditch the repetitive @media (prefers-color-scheme: …) blocks for simple property declarations.

Pero había una limitación clara: light-dark() solo aceptaba <color>. Si querías alternar una imagen de fondo, una máscara o un logo según el esquema de color, todavía te tocaba escribir variables globales y media queries dispersas. La buena noticia: la especificación ha avanzado y ahora light-dark() acepta <image>. Esto cambia la arquitectura de temas en CSS y merece una guía práctica con criterio.

Resumen rápido (lectores con prisa)

Qué es: Una extensión de light-dark() que acepta <image>, permitiendo elegir assets según esquema de color.

Cuándo usarlo: Cuando necesites alternar logos, fondos o máscaras sin depender de variables globales o JS.

Por qué importa: Reduce fragmentación de estilos y permite decisiones locales basadas en color-scheme.

Cómo funciona: Declara light-dark(url(light.png), url(dark.png)) en la propiedad que espera un <image>; ambos argumentos deben ser <image>.

Qué cambia — ejemplo y patrón recomendado

Antes (patrón clásico con variables y media queries)

:root { --bg-image: url(light-pattern.png); }

@media (prefers-color-scheme: dark) {
  :root { --bg-image: url(dark-pattern.png); }
}

.element { background-image: var(--bg-image); }

Ahora (light-dark() para imágenes)

.element {
  color-scheme: dark; /* override local si hace falta */
  background-image: light-dark(url(light-pattern.png), url(dark-pattern.png));
}

Ventajas prácticas

  • Cohesión: la decisión de qué asset usar vive en el mismo selector que lo consume.
  • Respuesta local: respeta color-scheme aplicado al nodo o a sus ancestros.
  • Portabilidad: el componente se mueve sin necesidad de variables globales.

Restricciones técnicas y por qué importan

Regla crítica: los dos argumentos de light-dark() deben ser del mismo tipo. Es decir, o dos <color> o dos <image>. No mezcles tipos:

/* válido */
color: light-dark(#000, #fff);
background-image: light-dark(url(light.png), url(dark.png));

/* inválido — el parser lo rechazará */
background-image: light-dark(url(light.png), #1a1a2e);

¿Por qué? El parser de CSS valida la sintaxis frente al tipo que la propiedad espera. background-image espera un <image>. Si uno de los argumentos es un <color>, la declaración crea ambigüedad en el AST y el motor invalida la regla. Esta restricción protege rendimiento y predictibilidad del renderizado.

Progressive enhancement y fallbacks reales

Soporte por navegador aún está en despliegue. La estrategia correcta es usar la cascada y un fallback explícito:

.element {
  /* Fallback básico */
  background-image: url(light-pattern.png);

  /* Moderno: sobreescribe si se soporta */
  background-image: light-dark(url(light-pattern.png), url(dark-pattern.png));
}

Si necesitas cobertura adicional para navegadores antiguos, combina con @media (prefers-color-scheme: dark) como fallback secundario, pero reserva light-dark() como la fuente de verdad cuando el motor lo soporte.

Casos de uso donde esto realmente aporta valor

  • Logos adaptativos (SVG/PNG): menos JS, menos duplicación de DOM.
  • Patrones de fondo decorativos que deben cambiar con el tema.
  • mask-image o -webkit-mask-image donde el comportamiento depende del contraste.
  • Sistemas de diseño a escala: reduce la deuda técnica y facilita la revisión de cambios de tema.

Criterio para equipos

  • Documenta la convención: cuándo usar light-dark(), cuándo preferir variables.
  • Revisa en code reviews la homogeneidad de tipos (no mezclar color/imagen).
  • Añade un test visual de regresión (navegación entre temas) en CI si el producto necesita alta fiabilidad.
  • Implementa el fallback mostrado y degrada con gracia: el objetivo es evitar roturas visuales en navegadores sin soporte.

Conclusión

La evolución de light-dark() hacia <image> es un paso práctico hacia interfaces menos dependientes de JS y con menos “bolsas” de estilos dispersos. No es una revolución espectacular, pero sí una mejora arquitectónica que reduce deuda técnica y hace los componentes más robustos y portables. Implementa con criterio, añade fallbacks y actualiza tus patrones de diseño: el cambio trae limpieza, y eso en producción siempre paga.

FAQ

Respuesta:

Es la actualización de la especificación que permite pasar <image> como argumentos a light-dark(), de modo que propiedades que esperan imágenes (por ejemplo, background-image) puedan alternar assets según el esquema de color.

Respuesta:

No. Los dos argumentos deben ser del mismo tipo. Mezclar un <color> con un <image> es sintácticamente inválido y el parser rechazará la regla.

Respuesta:

Usa la cascada: declara primero un fallback (por ejemplo, background-image: url(light-pattern.png);) y luego sobreescribe con light-dark(). Como fallback secundario puedes mantener @media (prefers-color-scheme: dark) si necesitas más compatibilidad.

Respuesta:

Cualquier propiedad que espere un <image> puede aprovechar la extensión, por ejemplo background-image, mask-image y sus prefijos relevantes como -webkit-mask-image.

Respuesta:

Sí, en escenarios donde la compatibilidad es crítica o donde la convención del equipo requiere variables globales. Pero para componentes portables y locales, light-dark() ofrece una alternativa más cohesionada.

Respuesta:

La restricción de tipo evita ambigüedades en el AST y ayuda al motor a validar y optimizar reglas. El impacto en rendimiento es mínimo si se usa correctamente; la ventaja es mayor predictibilidad del renderizado.

Respuesta:

La especificación relevante es CSS Color Level 5 (W3C). Para diagnóstico visual en Chrome, referencia Chrome DevTools (sección Memory para diagnóstico visual, no confundir con rendimiento).

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *