Creando Arte CSS Accesible

For an English version of this article, visit this other DEV post

El arte y los dibujos CSS ha existido básicamente desde que se creo CSS. Son una manera perfecta de practicar y aprender y son interesantes como retos de programación. Pero tienen un gran problema: el arte CSS no es accesible.

En este post no vamos a ver cómo crear dibujos e ilustraciones en CSS (hay muchos de esos posts). En lugar de eso, nos centraremos en consejos y buenas prácticas para hacer que los dibujos CSS sean más accesibles para todos.

Después de aplicar estas técnicas, tus imágenes en CSS se llevarán mejor con los lectores de pantalla, personas con necesidades específicos de colores, gente con trastornos vestibulares o vértigo... y todo sin impactar negativamente tu arte o tu creatividad. Todos ganan.

Para ver un ejemplo con los consejos de este artículo, visita esta ilustración CSS. Y sin más preámbulos, vamos a ver las recomendaciones:

  1. Identifica el dibujo CSS como una imagen
  2. Añade texto alternativo
  3. Elige entre dibujar a la perfección o responsivamente
  4. Usa elementos HTML semánticos
  5. Precaución con las animaciones
  6. Respeta las elecciones del colores
  7. Especifica la relación de aspecto

Identifica el dibujo CSS como una imagen

Una de las cosas más importantes es identificar tu arte CSS como una image. Eso se puede hacer añadiendo un rol ARIA de img al contenedor principal del dibujo:

<div role="img">
  <!-- HTML de la imagen -->
</div>

Al añadir este rol ARIA, las tecnologías de asistencia anunciarán que el contenedor del arte CSS es una imagen cuando lleguen a él.

Añade texto alternativo

Ahora que los lectores de pantalla anuncian el arte CSS como una imagen, es important proveer una descripción o texto alternativo tal y como lo tendría una imagen normal. Algo equivalente al atributo alt.

Podemos lograr esto al añadir el atributo aria-label al dibujo y añadiendo el texto alternativo ahí:

<div aria-label="El texto alternativo iría aquí">
  <!-- HTML de la imagen -->
</div>

Esto funcionará pero, en el caso improbable (pero posible) de que el CSS no se cargue correctamente, puede no ser suficiente para hacer lo que queremos. Por eso prefiero usar mejor el atributo aria-labelledby:

<div aria-labelledby="alt-imagen">
  <div id="alt-imagen">El texto alternativo va aquí</div>
  <!-- HTML de la imagen -->
</div>

Esta solución incluye la etiqueta como texto, lo que permite esconderla usando cualquier técnica accesible. Hay muchas posibilidades para ello. Una sencilla se puede encontrar en la página web de The A11Y Project:

#alt-imagen {
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

De este modo, si el CSS no se carga, el texto alternativo se mostrará parecido a como se mostraría el texto alternativo de un <img /> nativo si la imagen no se carga correctamente en la página.

Elige entre dibujar a la perfección o responsivamente

Algo a tener en cuenta antes incluso de empezar a codificar nada: ¿queremos que el dibujo sea pixel-perfect? ¿O queremos que sea algo responsivo y que cambie de tamaño (al coste de que igual no se ve del todo bien)? Por supuesto, las respuesta tendrán consecuencias y decidirán sobre cómo debemos crear el arte CSS.

Si queremos alcanzar resultados perfectos, deberíamos usar unidades absolutas de medida como px, cm, pt, etc. De este modo nuestra imagen tendrá un tamaño fijo y no escalará del todo bien (aunque mejor que las imágenes raster ya que lo nuestro será un dibujo vectorial), pero podremos usar cualquier propiedad y valor CSS.

Por otro lado, utilizaremos unidades relativas de medida como %, vmin, em, etc. si queremos que sea escalable.

Para que sea responsivo debemos tener en cuenta otras consideraciones:

  • El resultado será escalable y responsivo... o al menos, tendrá la capacidad de serlo.
  • Tendremos que tener cuidado con algunas propiedades CSS que no funcionan bien con algunas unidades relativas (p.e. box-shadow o border).
  • Debemos evitar valores CSS que no usan unidades relativas (p.e. clip-path puede tener valores relativos con polygon() pero no con path()).

No hay nada malo con uno u otro enfoque. Para dibujos artísticos seguramente optemos por escalabilidad, mientras que para cosas más prácticas como iconos o fondos, optar por algo más pixel perfecto será conveniente.

Usa elementos HTML semánticos

HTML5 provee muchos elementos semánticos. No hace falta usar <div> para todas las partes de nuestro dibujo y especialmente no para el contenedor principal. La pregunta debería ser "¿qué elemento HTML debemos usar?"

Nota del autor: comprendo que mi elección de elementos semánticos puede ser discutible. Intentaré ser lo más neutral/objetivo posible.

Hay un par de elementos que parecen ideales en este caso: <article> y <figure>/<figcaption>. Mientras que el segundo parece una elección obvia, el primero tiene algunas ventajas semánticas, como veremos pronto.

Por un lado, tenemos <figure>. Un contenido autónomo con un subtítulo opcional (<figcaption>) que puede usarse para el texto alternativo:

<figure aria-labelledby="alt-imagen">
  <figcaption id="alt-imagen">Aquí va el texto alternativo</figcaption>
  <!-- HTML de la imagen -->
</figure>

Esta elección parece venir como un guante para lo que queremos, pero no debemos olvidar <article>: una composición completa, autónoma e independiente, con la intención de ser reusada y distribuida. ¡Precisamente lo que es el dibujo CSS!

<article>
  <!-- HTML de la imagen -->
</article>

¿Qué elemento HTML semántico elegir? Eso dependerá del autor y de cómo quiere que su arte CSS sea presentado a los usuarios.

Alguna gente ha mencionado que no puede haber un <article> dentro de otro <article>, pero eso no es correcto. Un artículo puede contener otro artículo. Cuando hay artículos anidados, el artículo interior debe estar relacionado con el artículo exterior.

El uso de <article> da mucho juego también porque permite una incorporación más natural de otros elementos HTML semánticos como encabezados para el título/texto alternativo de la ilustración, <address> para el contacto del autor, o <time> para la fecha de publicación:

<article aria-labelledby="image-alt" 
         aria-describedby="image-info">
  <!-- Todo el `<header>` se escondería de forma accesible -->
  <header>
    <h2 id="image-alt">Título/Texto alternativo</h2>
    <p id="image-info">
      <address>
        Creado por
        <a href="https://twitter.com/alvaro_montoro">
          Alvaro Montoro
        </a>
      </address>
      el
      <time datetime="2021-06-10">10 de Junio de 2021</time>
    </p>
  </header>
  <!-- HTML de la imagen -->
</article>

Precaución con las animaciones

Algunas veces los dibujos CSS incluyen algún tipo de animación: un perro que mueve la cola, una persona moviéndose y parpadeando, un avión que vuela y da vueltas...

Cuando añadimos animaciones a nuestro arte CSS, debemos tener en cuenta que no a todo el mundo le gusta ver las animaciones o que pueden sufrir de algún trastorno que se vea acentuado por ellas (p.e. los trastornos vestibulares o el vértigo).

Tenemos que proporcionar algún modo de para las animaciones o reemplazarlas con algo diferente. Para nuestra suerte, CSS ofrece la media query prefers-reduced-motion que permite a los desarrolladores hacer precisamente eso.

Por ejemplo, podemos deshabilitar una animación haciendo algo como esto (suponiendo que la clase "animado" está en todos los elementos que se animan):

@media (prefers-reduced-motion) {
  .animado {
    animation: none;
  }
}

También tenemos que tener en cuenta que no todas las animaciones son iguales y no todas causarán problemas. En lugar de cancelar todas las animaciones, podemos plantearnos cambiarlas por otras más apropiadas o ajustando los tiempos para que no sean tan bruscas.

Respeta las elecciones del colores

Algunos sistemas operativos permiten a los usuarios seleccionar opciones de accesibilidad y, en algunos casos, CSS puede identificar esos valores usando algunas media queries y propiedades.

Algunos de los métodos que vamos a comentario no están soportados por los navegadores, pero pueden usarse para mejorar nuestro arte CSS una vez que el soporte esté más extendido.

Las media queries son:

  • prefers-contrast: para indicar si el usuario quiere un contraste más alto o más bajo.
  • prefers-color-scheme: para detectar si el usuario prefiere un tema oscuro o claro (muy común para mostrar el modo noche/día.)
  • forced colors: especifica si un usuario ha elegido una paleta de colores limitada.
@media (prefers-contrast: more) {
  /* more: contrastes más altos, bordes, sin transparencias... */
  /* less: contrastes de color más bajos */
}

@media (prefers-colors-scheme: dark) {
  /* dark: dibujos con más contraste frente a fondos oscuros */
  /* light: dibujos con más contraste frente a fondos claros */
}

@media (forced-colors: active) {
  /* sobreescribir propiedades como box-shadow, añadir bordes, etc. */
}

El uso de propiedades personalizadas en CSS (también llamadas variables CSS) hará más fácil el uso de esas media queries ya que sólo habrá que redefinir los valores de las variables.

En casos más extremos, puede que queremos evitar algunas propiedades CSS. Por ejemplo, el valor de box-shadow se fuerza a none en el modo de colores forzados, y tendríamos que buscar alternativas.

Especifica la relación de aspecto

Una propiedad más reciente que puede ser útil para crear arte CSS responsivo es aspect-ratio. Con ella podemos definir la relación de aspecto preferida para la imagen, que el navegador usará para calcular el tamaño y aplicarlo automáticamente:

/* Este dibujo CSS será cuadrado */
#miArteCSS {
  aspect-ratio: 1 / 1;
}

/* Este dibujo CSS será el doble de alto que de ancho */
#miArteCSS {
  aspect-ratio: 1 / 2;
}

Esto es algo muy conveniente para los desarrolladores, pero también tiene un impacto en la accesibilidad porque asegura que nuestros dibujos CSS no se verán estirados o deformados. Y tiene un buen soporte por parte de los navegadores.

Este artículo es una extensión de la presentación que hice durante un meetup de SydCSS. Puedes encontrar el video en Youtube.

25