Preferencias de usuario

Detectar preferencias del usuario (dark mode, animaciones reducidas...)


Hay que saber que las media queries tienen un apartado especial donde podemos utilizar preferencias de usuario. Las preferencias de usuario son reglas @media especiales que nos permiten dar estilo según la preferencia establecida por el usuario en su sistema operativo, adaptándose a su caso particular.

Veamos las reglas de preferencias de usuario que existen:

Preferencia de usuario Valores ¿Qué detecta?
prefers-color-scheme dark | light El esquema de colores (modo claro o oscuro).
prefers-reduced-motion reduce | no-preference Animaciones reducidas (sin o con animaciones).
prefers-reduced-data reduce | no-preference Ahorro de datos (consumo reducido de datos).
prefers-reduced-transparency reduce | no-preference Transparencias reducidas (sin o con transparencias).
prefers-contrast more | less | custom | no-preference Contraste preferido (Mayor o menor contraste).
forced-colors active | none Forzar colores (requiere prefers-contrast a custom).

Echemos un vistazo detenidamente a cada una de ellas.

Dark mode / Light mode

Una de las características más recurrentes en interfaces de usuario es la posibilidad de elegir un dark mode o dark theme, es decir, un sistema que permita al usuario seleccionar un tema claro (generalmente con fondo blanco) o un tema oscuro (generalmente con fondo negro).

Aunque podemos hacer esto de forma manual, existe una regla @media especial denominada prefers-color-scheme donde podemos detectar si el usuario tiene preferencia por uno de estos dos valores (establecido en las opciones del sistema operativo) y actuar en consecuencia.

Las reglas de preferencias de usuario se utilizan como una @media query normal, indicando el valor en cuestión y aplicándole estilos CSS. Observa el siguiente ejemplo, donde usamos la preferencia de usuario prefers-color-scheme asignada al valor dark:

@media (prefers-color-scheme: dark) {
  :root {
    --foreground-color: white;
    --background-color: black;
  }
}

body {
  background: var(--background-color, white);
  color: var(--foreground-color, black);
}
<p>Hola a todos, esto es un texto de ejemplo.</p>

Observa que hemos establecido para toda la página, con la pseudoclase :root, las variables CSS --foreground-color y --background-color sólo para aquellos usuarios que tengan establecido en el sistema operativo que prefieren interfaces de usuario en modo oscuro. Los valores que es posible indicar son light o dark.

El soporte de dicha propiedad es bastante bueno en la actualidad, incluso en dispositivos móviles, por lo que se puede utilizar con seguridad.

Movimiento reducido

Las interfaces modernas en la actualidad suelen apostar por diseños con animaciones y transiciones que hacen más agradables los cambios de estado y acciones específicas en una web. Sin embargo, por cuestiones de accesibilidad estas animaciones también pueden suponer molestias a usuarios que son especialmente sensibles a este tipo de estímulos.

En CSS tenemos una característica que permite notificar al desarrollador web si un usuario ha elegido en su sistema que prefiere eliminar o desactivar este tipo de animaciones o transiciones, mediante prefers-reduced-motion, la cuál tiene los valores no-preference o reduce:

@media (prefers-reduced-motion: reduce) {
  :root {
    --animation-timing: 0s;
  }
}

.container {
  display: flex;
  background: steelblue;

  & .item {
    --size: 100px;

    width: var(--size);
    height: var(--size);
    box-shadow: 0 0 10px 5px #000a inset;
    background: red;
    border-radius: 50%;
    transition: transform var(--animation-timing, 2s);
  }

  &:hover .item {
    transform: translate(500px, 0);
  }
}
<div class="container">
  <p>Mueve el ratón sobre este elemento azul</p>
  <div class="item"></div>
</div>

En el ejemplo anterior, utilizamos la variable --animation-timing para indicar el tiempo dedicado para la animación. Si el usuario tiene la preferencia de no usar animaciones, tendrá establecido 0s y no habrá animación, sino que se verá el movimiento instantáneamente. Si el usuario las tiene habilitadas, se utilizará 2s de animación, por lo que si se apreciará.

El soporte de esta característica también es bastante bueno, por lo que puede ser utilizado de forma segura:

Ancho de banda reducido

De la misma forma que en ejemplos anteriores, el usuario puede preferir usar su ancho de banda disponible de forma reducida, evitando así descargas que consuman gran cantidad de datos, con su correspondiente gasto que en ciertas situaciones puede ser un ancho de banda límitado.

La característica prefers-reduced-data nos permite recuperar del sistema o navegador del usuario la opción reduce o no-preference para saber que preferencia tiene seleccionada. De esta forma podemos crear estilos donde se establezcan imágenes de bajo tamaño, o incluso utilizar gradientes o colores sólidos en lugar de imágenes.

@media (prefers-reduced-data: reduce) {
  :root {
    --preferred-background: linear-gradient(120deg, steelblue, blue, black);
  }
}

body {
  background: var(--preferred-background, url(/assets/background.png));
}

Efectos de transparencia reducidas

De la misma forma que las preferencias anteriores, podemos indicar en nuestro sistema que preferimos interfaces de usuario donde no se utilicen transparencias o elementos traslúcidos, ya que muchas veces dificultan la visión o lectura, y puede resultar molestos.

Con esta media query, deberíamos poder modificar la opacidad de los elementos para retirar la opacidad. Como siempre, se recomienda usar variables para que sea más sencillo.

@media (prefers-reduced-transparency: reduce) {
  :root {
    --opacity: 100%;
  }
}

body {
  background: linear-gradient(200deg, black, indigo, hotpink);
  height: 80vh;
}

.container {
  background: rgb(0% 0% 100% / var(--opacity, 15%));
  padding: 0.5rem 2rem;
  color: white;
}
<div class="container">
  <p>Hola a todos, esto es un ejemplo de reducción de transparencias.</p>
</div>

Preferencia de contraste

Por último, las preferencias de usuario prefers-contrast permiten al usuario establecer que tipo de contraste prefiere. Existen cuatro opciones disponibles:

  • less: Prefiere interfaces con menor contraste.
  • more: Prefiere interfaces con mayor contraste.
  • custom: Prefiere un conjunto de colores personalizado por las palabras clave del sistema.
  • no-preference: El usuario no ha indicado ninguna preferencia especial.

@media (prefers-contrast: custom) {
  :root {
    background: canvas;
    color: canvastext;
  }
}

En este ejemplo, estamos usando los colores del sistema.

Por otro lado, mediante la preferencia forced-colors con valor active podemos indicar al navegador que fuerce a utilizar unos colores personalizados si prefers-contrast está establecido a custom.

@media (forced-colors: active) {
  .container {
    border: 3px #888 solid;
  }
}

En el caso de tener forced-colors a none, se utilizará el conjunto de colores indicado por el sistema, como se mencionó en el apartado anterior.

¿Quién soy yo?

Soy Manz, vivo en Tenerife (España) y soy streamer partner en Twitch y profesor. Me apasiona el universo de la programación web, el diseño y desarrollo web y la tecnología en general. Aunque soy full-stack, mi pasión es el front-end, la terminal y crear cosas divertidas y locas.

Puedes encontrar más sobre mi en Manz.dev