Category: CSS

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

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