Implementando light-dark() para imágenes en CSS: guía técnica
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-schemey 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-schemeaplicado 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-imageo-webkit-mask-imagedonde 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
- ¿Qué es exactamente la extensión de light-dark() para imágenes?
- ¿Puedo mezclar colores e imágenes en la misma llamada a light-dark()?
- ¿Cómo debo implementar fallbacks para navegadores sin soporte?
- ¿Qué propiedades de CSS aceptan
<image>con light-dark()? - ¿Debo seguir usando variables y media queries?
- ¿Cómo afecta esto al rendimiento y al parser de CSS?
- ¿Dónde puedo leer la especificación y herramientas de diagnóstico visual?
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.
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.
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.
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.
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.
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.
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).
