Estado de cara al usuario | Programar Plus

Hablemos de estado. Comunicar el estado al usuario, es decir, no la aplicación almacena el estado en los objetos de JavaScript o localStorage. Vamos a hablar sobre cómo informar a nuestros usuarios sobre el estado (piense: si un botón está deshabilitado o no, o si un panel está activo o no), y cómo podemos usar CSS para eso. No vamos a usar estilos en línea o, en la medida de lo posible, selectores de clase, por razones que se aclararán a medida que avanzamos.

¿Aún aquí? Frio. Hagámoslo.

Todos los componentes dinámicos de una aplicación tienen un estado predeterminado para el usuario, y ese estado debe almacenarse y actualizarse a medida que los usuarios interactúan con estos componentes.

Por ejemplo, cuando se presiona un botón, suceden cosas (para eso están los botones). Cuando suceden estas cosas, generalmente se representan de manera visual en la interfaz. El fondo del botón puede cambiar para indicar que se presionó. Si el botón controla otros componentes en la interfaz, es probable que esos componentes cambien visualmente de estilo o, en algunos casos, su visibilidad cambie. Se elimina un elemento, aparece una notificación, se aplica un estilo de error, etc.

Es posible que haya notado que hemos estado mencionando bastante el estado “visual” de los componentes. Ese es exactamente el tipo de problema que he estado encontrando con muchos tutoriales, artículos y conversaciones generales sobre el estado.

La mayoría de las veces, los desarrolladores usan clases “con estado” para administrar el estado de un componente. Pero esto es muy inadecuado, ya que un componente se compone de algo más que su apariencia. Hay una semántica subyacente que debe administrarse junto con la representación visual del componente. La falla en administrar esa semántica subyacente se hace evidente tan pronto como interactúa con ella a través del teclado y/o el lector de pantalla.

Este es un artículo sobre cómo transmitir el estado de manera adecuada para que los usuarios que no pueden ver y que usan el mouse puedan interactuar con nuestras interfaces.

El estado es más que solo cómo se ve

Además de usar CSS para ocultar adecuadamente el contenido de los usuarios videntes y las tecnologías de asistencia, CSS no tiene muchos efectos intencionales en la semántica o el estado accesible de un elemento. Lo que quiero decir con eso está fuera de propiedades como el ‘hablar’ no compatible, el pseudocontenido antes/después y las consultas de medios para cambiar el estilo de los componentes específicamente en función de las preferencias del usuario, como la consulta de medios de movimiento reducido y otras consultas de usuario propuestas. CSS por sí solo no está destinado a cambiar la semántica de un elemento, contenido o transmitir apropiadamente el estado de un elemento de una manera significativa.

¿Por qué menciono todo esto? Porque administrar el estado solo con clases CSS es, en su mayoría, inadecuado para transmitir el estado a todos los usuarios. Al ser un lenguaje con fines de presentación, dando una entrada de una clase de .has-error para cambiar el color del borde a un tono de rojo, no tiene valor semántico. Para todas las preocupaciones de CSS, “Así es como querías diseñar esa entrada. Frio. ¡Te cubro la espalda! Simplemente no me pidas que cambie el estilo hacia arriba en el DOM. Trazo la línea allí, amigo…”

En cambio, para administrar y transmitir el estado, deberíamos actualizar los atributos en los elementos apropiados. Y no, no me refiero a atributos de datos. Esos tampoco significan nada. Si adoptamos este enfoque, en muchos casos ni siquiera necesitaremos clases con estado, fuera de las clases que alternan la configuración de un elemento. display.

¿Olvidamos que podemos diseñar con selectores de atributos?

HTML y ARIA tienen una gran cantidad de atributos que deben usarse para transmitir adecuadamente el estado actual de un componente.

Pensando en usar un .is-disabled clase en tu <button></button> o <input type="text" />? Eso solo lo deshabilitará visualmente. Todavía tendrá que desactivar programáticamente los eventos de clic y teclado para ese elemento. En su lugar, utilice el [disabled] atributo y tendrá un selector de CSS para diseñar su elemento, ¡y el navegador hará todo el trabajo apropiado para deshabilitar ese elemento por usted!

Así que en lugar de:

input.is-disabled { 
  opacity: .65; 
}

Que solo modifica visualmente una entrada, use:

input[disabled] { 
  opacity: .65; 
}

Esto logra el mismo efecto visual que usando el .is-disabled clase, pero en su lugar estamos utilizando el selector de atributos del atributo que necesita establecer para transmitir el estado actual al navegador y a los usuarios. Todo sin tener que hacer ninguno de los trabajos adicionales mencionados anteriormente, con JavaScript, para deshabilitar la entrada, si simplemente estuviéramos alternando una clase.

Ejemplo: Ser “Activo”

Para proporcionar un contexto más profundo, veamos una situación en la que podría usar un .is-active clase. Para diferentes componentes, estar “activo” puede significar cosas completamente diferentes, por lo que puedo apreciar querer usar un nombre de clase único y reutilizable, en lugar de determinar qué atributo debe administrarse para transmitir el estado de manera adecuada. Pero hacer que la administración del estado sea más fácil para los desarrolladores no necesariamente ayuda a los usuarios, así que hagámoslo de la manera correcta.

Enlaces de navegación activos

Primero, veamos cómo declarar el enlace actualmente activo en una navegación. El siguiente Pen tiene dos ejemplos. El primero utilizando un .is-active class para indicar el elemento de navegación actual. El segundo uso aria-current="page".

Vea Pen .is-active vs aria-current=’page’ de Scott (@scottohara) en CodePen.

Si bien ambos se ven exactamente iguales, si usa Jaws 18, Voice Over o NVDA 2017.2 (cuando se lance) al navegar por el ejemplo, escuchará algo como: “Características, página actual”. al interactuar con el ejemplo usando aria-current. Consulte el artículo de Léonie Watson sobre [aria-current] para muchos otros ejemplos de dónde se podría usar este atributo para diseñar, en lugar de un .is-active clase.

Botones activos

Según el propósito del botón, es posible que sea necesario aumentar el estado activo del botón para los usuarios de lectores de pantalla a través de uno de los siguientes atributos ARIA:

  • aria-expanded – indica que el botón controla otro componente en la interfaz y transmite el estado actual de ese componente.
  • aria-pressed – indica que el botón se comporta de manera similar a una casilla de verificación, en el sentido de que su estado alterna entre estar presionado o no presionado.

Sin usar uno de los atributos anteriores, un botón no tiene una forma inherente de comunicar si se ha interactuado con él o no. Eso está totalmente bien si una situación no lo requiere, pero si necesita comunicar que se ha activado un botón, así es como podemos hacerlo usando aria-pressed:

Vea el ejemplo del botón de cambio de lápiz de Scott (@scottohara) en CodePen.

En el ejemplo anterior, tenemos un botón con el que se puede interactuar para agregar un artículo a un carrito de compras. Para indicar cuándo se ha agregado un elemento, en lugar de usar una clase, alternamos el valor booleano de la aria-pressed atributo, y usando el [aria-pressed="true"] como nuestro gancho de estilo para transmitir visualmente el estado activo. Al interactuar con el botón a través de un lector de pantalla, se anunciará como “marcado” o “no marcado”, agregar al carrito, botón de alternancia.

Para profundizar en las consideraciones que se deben tomar al desarrollar botones de alternancia accesibles, no es necesario buscar más allá del artículo Botones de alternancia de Heydon Pickering. Heydon describe, con gran detalle, por qué no es una buena idea cambiar la etiqueta visible del botón, e incluso destaca que es posible que en realidad no desee un botón de alternar, sino que debería considerar usar un interruptor.

Administrar el estado del acordeón

Para nuestro ejemplo final, echemos un vistazo a cómo administraríamos el estado en un componente de acordeón:

Vea el ejemplo de acordeón Pen ARIA de Scott (@scottohara) en CodePen.

Si lee los comentarios en CSS y JavaScript, notará que esta demostración está haciendo algunas cosas.

En primer lugar, el patrón de marcado del acordeón está construido de tal manera que si JavaScript se desactiva por algún motivo, ninguno de los contenidos será inaccesible para esos usuarios, ya que los paneles solo se ocultan si el .js la clase está presente.

En segundo lugar, para eludir la necesidad de <button></button> elementos dentro de cada encabezado del panel de acordeón, en su lugar estamos convirtiendo sus anidados <a>s en “botones”, aplicando el ARIA role="button", y luego agregar toda la funcionalidad de teclado esperada a través del detector de eventos keydown. Además, para garantizar que los usuarios del teclado puedan acceder al “botón”, un tabindex="0" se ha configurado para cada uno de los botones ARIA.

Finalmente, aquí usamos el aria-expanded atributo para comunicar el estado actual del panel de acordeón, de modo que cuando un usuario enfoca el activador de acordeón con un lector de pantalla, anunciará “Encabezado de acordeón, botón contraído (o expandido)”.

Notará que los paneles de acordeón están utilizando un .is-active class para alternar su estado visible. ¡Dios mío! Pero espera, esto es lo único que poder contar solo con CSS para ayudarnos. Si echamos un vistazo más de cerca a los selectores en el trabajo aquí:

.js .accordion__panel {
  border-bottom: 1px solid;
  overflow: hidden;
  padding: 0 1em;
  max-height: 0px;
  transition:
    max-height .2s ease-in-out,
    visibility .2s ease-in-out;
  visibility: hidden;
}

.js .accordion__panel.is-active { 
  max-height: 100vh;
  visibility: visible;
}

El primer selector, el que depende de que JavaScript esté disponible, utiliza visibility: hidden para ocultar de forma inclusiva el contenido del panel tanto a los usuarios videntes como a los usuarios de tecnologías de asistencia. Las propiedades de desbordamiento, altura máxima y transición se configuran para colapsar el panel y prepararlo para crecer hasta su forma expandida, una vez que el .is-active La clase se agrega al panel de acordeón. podría haber alternado display: none o agregó y eliminó programáticamente el hidden atributo de los paneles, en cambio, pero habríamos perdido la capacidad de hacer la transición del panel abierto. Y a todos les gusta una buena transición, ¿verdad?

Para concluir

Lo principal que quiero que se lleve de todo esto es que si solo está alternando clases para administrar visualmente el estado de sus componentes, es probable que no esté transmitiendo adecuadamente ese estado a los usuarios de tecnologías de asistencia.

Necesita estar usando los elementos apropiados (<button></button>¡Los s son tus amigos!), y administrando los atributos apropiados y sus valores para hacer que las experiencias de los usuarios sean realmente accesibles. Claro, podría hacer esas cosas y continuar alternando clases con estado para controlar su estilo. Pero si tenemos que actualizar los atributos y sus valores, y esos también son selectores de CSS válidos, ¿por qué haríamos más trabajo del necesario alternando también las clases?