light-dark() lleva tiempo cambiando colores. Ahora también cambia imágenes: le pasas dos degradados, dos url() o una imagen y none, y el navegador elige una según el esquema de color del elemento. Desde el 14 de septiembre de 2026 funciona en Chrome, Edge, Firefox y Safari, así que ya es Baseline como novedad.
El paisaje de la demo de arriba no tiene archivos de imagen ni JavaScript para el tema. El cielo, el sol que se convierte en luna, las estrellas, las nubes, las colinas y las ventanas encendidas son todos background-image: light-dark(día, noche). El JavaScript de la demo solo hace dos cosas: cambiar el color-scheme de la escena cuando pulsas Día o Noche, y escribir el panel de código.
Por qué importa
El modo oscuro siempre tuvo un hueco. Los colores eran fáciles con light-dark() o con propiedades personalizadas, pero cada vez que una imagen tenía que cambiar (un logo, un degradado de cabecera, una viñeta de lista, un fondo decorativo) volvías a @media (prefers-color-scheme: dark) y duplicabas la regla.
Una media query solo sabe lo que el usuario eligió en su sistema o en el navegador. No sabe que una sección de tu página está forzada a color-scheme: dark. light-dark() se resuelve según el esquema de color del elemento al que se aplica, así que la misma regla funciona bien en una barra lateral oscura dentro de una página clara. Las tres tarjetas de logo de la parte de abajo de la demo comparten una única regla CSS: una tiene color-scheme: light, otra dark y otra light dark, que sigue a tu sistema.
Cómo funciona
La especificación define ahora dos formas. La de colores ya la conoces. En la nueva, cada argumento es una <image> o none:
:root {
color-scheme: light dark;
}
.hero {
background-image: light-dark(
linear-gradient(#38bdf8, #fde68a), /* claro */
linear-gradient(#050816, #4c1d95) /* oscuro */
);
}
El primer argumento se usa cuando el esquema de color del elemento es claro. El segundo, cuando es oscuro. Como recuerda MDN, tienes que activarlo con color-scheme: light dark (normalmente en :root). Si no lo haces, la página sigue en claro y siempre te llega la primera imagen.
Para forzar un esquema en una zona concreta, pon color-scheme en ese elemento. Es justo lo que hace el selector de la demo:
.scene[data-scheme="light"] { color-scheme: light; }
.scene[data-scheme="dark"] { color-scheme: dark; }
none vale en cualquiera de los dos lados. La especificación lo define como una imagen totalmente transparente sin tamaño natural, y en Chrome el valor computado sale como image(rgba(0, 0, 0, 0)). Viene bien para capas que solo deben aparecer en un esquema, como las estrellas:
.stars {
background-image: light-dark(none, radial-gradient(1px 1px at 20px 30px, #fff 50%, transparent 51%));
}
Funciona en cualquier sitio que acepte una imagen, no solo en fondos. El ejemplo de la propia especificación lo usa para las viñetas de una lista:
ul.fancy {
list-style-image: light-dark(url("light.svg"), url("dark.svg"));
}
He probado todos estos fragmentos en Chromium leyendo getComputedStyle() con esquema claro y con esquema oscuro. En todos los casos el navegador devolvió la imagen que esperaba.
El fallo que me costó una capa
En la primera versión de la demo no había estrellas. Había escrito esto:
/* NO funciona: tres imágenes en el lado oscuro */
.stars {
background-image: light-dark(none, radial-gradient(…), radial-gradient(…), radial-gradient(…));
}
Cada lado de light-dark() admite una sola imagen. Una lista separada por comas invalida el valor entero y la propiedad se queda en none. La solución es un light-dark() por capa:
.stars {
background-image:
light-dark(none, var(--star1)),
light-dark(none, var(--star2)),
light-dark(none, var(--star3));
}
Con var() el error cuesta más de ver. El navegador no puede rechazar la declaración al analizarla porque todavía no sabe qué contienen las variables, pero en mi prueba el resultado siguió siendo none.
Soporte en navegadores
Según los datos de compatibilidad de MDN, la forma con imágenes llegó en Chrome y Edge 150, Firefox 150 y Safari 27. La forma con colores es bastante anterior: Chrome 123, Firefox 120 y Safari 17.5. Si en un navegador antiguo solo pruebas con colores, puedes dar por hecho que las imágenes también funcionan y no es así.
La demo comprueba el soporte con:
CSS.supports('background-image', 'light-dark(none, linear-gradient(red, blue))');
Si devuelve false, la demo muestra un aviso y pasa al plan B: los mismos degradados elegidos con un atributo data-resolved. En CSS puro, el plan B puede ser un bloque @supports:
.hero { background-image: url(light.png); }
@supports (background-image: light-dark(none, none)) {
.hero { background-image: light-dark(url(light.png), url(dark.png)); }
}
Un detalle: el fundido que ves al cambiar entre día y noche no es cosa de light-dark(). Viene de una View Transition (document.startViewTransition), y la demo se la salta si tienes activado prefers-reduced-motion.
Qué vigilar
- Navegadores antiguos entre tus visitas. Es Baseline como novedad, no de amplia disponibilidad. Para lo importante, como un logo que desaparece sobre fondo oscuro, deja un plan B.
- La carga de imágenes. Antes de intercambiar así dos imágenes rasterizadas pesadas, mira en el panel Network de las DevTools qué descarga cada navegador y cuándo.
- Dónde usarlo. Logos, ilustraciones y degradados decorativos son los primeros candidatos. Merece la pena revisar todo lo que antes necesitaba una regla duplicada dentro de una media query
prefers-color-scheme.