viernes, 22 de diciembre de 2017

Novedades WAI-ARIA 1.1

Nota 08/06/2023: se ha publicado la nueva versión del estándar WAI-ARIA 1.2. Consulta el artículo: WAI-ARIA 1.2. Novedades de la nueva versión del estándar Accessible Rich Internet Applications (WAI-ARIA), de 6 de junio de 2023

En este artículo enumero y explico detalladamente todas las novedades de WAI-ARIA 1.1 (Accessible Rich Internet Applications (WAI-ARIA) 1.1), la nueva recomendación desde el 14 de diciembre de 2017.

Este artículo parte de la base de que conoces el estándar WAI-ARIA, imprescindible para hacer accesible un sitio web con componentes dinámicos, como carruseles, acordeones, árboles, menús desplegables, ventanas modales, componentes con actualizaciones automáticas, mensajes de validación JavaScript, grids, etc.

Si no estás familiarizado con el estándar, te recomiendo que comiences leyendo el artículo: WAI-ARIA. Introducción, referencias, ejemplos, herramientas

Índice:

Roles

En primer lugar os incluyo la taxonomía de roles de ARIA 1.0 y ARIA 1.1 para pasar después a explicar las diferencias concretas.

Roles ARIA 1.0

Taxonomía de roles en ARIA 1.0. En el pie de foto el enlace al mismo y a su descripción.

Taxonomía de roles de ARIA 1.0: Ver ampliado en ventana nueva - Descripción del diagrama

Roles ARIA 1.1

Taxonomía de roles en ARIA 1.1. En el pie de foto el enlace al mismo y a su descripción.

Taxonomía de roles de ARIA 1.1: Ver ampliado en ventana nueva - Descripción del diagrama

Roles específicos para Live Regions y Windows

En WAI-ARIA 1.0, los roles se clasificaban en:

  • Abstract Roles
  • Widget Roles
  • Document Structure
  • Landmark Roles

En WAI-ARIA 1.1, los roles se clasifican en:

  • Abstract Roles
  • Widget Roles
  • Document Structure
  • Landmark Roles
  • Live Region Roles (nuevo)
  • Window Roles (nuevo)

Como se observa, se añaden dos nuevos tipos de roles: Live Region y Window.

Los roles que luego incluyen NO son nuevos, ya estaban en ARIA 1.0. De todas maneras los repaso a continuación.

Live Region Roles

Una de las novedades de ARIA 1.1 es que se define qué rol puede tener aplicado una "live region", es decir, una zona cuyo contenido se actualiza dinámicamente y que, por tanto, debería estar marcada con aria-live.

Estos roles, que ya existían en ARIA 1.0, son:

  • role="alert": contiene información relevante y debería ser "assertive", por ejemplo:

    <div role="alert" aria-live="assertive">

    Se aplicaría, por ejemplo en un formulario, a la zona en la que se informa al usuario de que se han producido errores tras la validación de los datos introducidos.

  • role="log": contiene información que se agrega en un orden significativo (de tal manera que la información antigua puede desaparecer) y debería ser "polite", por ejemplo:

    <div role="log" aria-live="polite">

    Se aplicaría, por ejemplo, a una zona con los últimos tuits o las últimas noticas, de tal manera que los elementos más antiguos desaparecen.

  • role="marquee": contiene información no relevante que cambia con frecuencia, y debería ser "off", por ejemplo:

    <div role="marquee" aria-live="off">

    Se aplicaría, por ejemplo, a una zona publicitaria.

  • role="status": contiene una advertencia que no es lo suficientemente importante para justificar una alerta, y debería ser "polite", por ejemplo:

    <div role="status" aria-live="polite">

    Se aplicaría, por ejemplo, a una zona donde se vaya indicando el porcentaje de avance en la contestación a un examen de tipo test.

  • role="timer": contiene un contador numérico del tiempo transcurrido o del tiempo restante, y debería ser "off", por ejemplo:

    <div role="timer" aria-live="off">

    Se aplicaría, por ejemplo, a un contador del tiempo restante para que expire la sesión.

Recordemos el significado de que una "live region" sea "assertive" (aria-live="assertive"), "polite" (aria-live="polite") u "off" (aria-live="off"):

  • assertive: significa que los cambios se anuncian de inmediato, independientemente de lo que estés haciendo. Si el lector de pantalla está leyendo un párrafo, por ejemplo, parará en mitad de la lectura para anunciar el cambio.
  • polite: los cambios se anuncian sin interrumpir al usuario, por ejemplo cuando el lector termine de leer aquello que estaba leyendo.
  • off: los cambios no se anuncian al usuario a menos que el foco esté en esa región.

La diferencia entre:

<div role="alert" aria-live="assertive">
<p>Se han detectado 5 errores en el formulario</p>
<div>

y

<div aria-live="assertive">
<p>Se han detectado 5 errores en el formulario</p>
<div>

es que en el primer caso el producto de apoyo, como un lector de pantalla, anuncia:

"Alerta. Se han detectado 5 errores en el formulario"

mientras que en el segundo caso anuncia:

"Se han detectado 5 errores en el formulario".

Incluir el rol de la "live region" permite anunciar al usuario el tipo de aviso que es.

Sin embargo, según las últimas comprobaciones que hice este verano, al menos con NVDA, solo se anunciaba el role="alert" (con la palabra "Alerta").

El resto de roles NO eran anunciados como "contador", "estado", etc., al menos de momento. Volveré a testearlo con NVDA, JAWS y VoiceOver y os cuento.

Os recuerdo que:

  • aria-atomic permite controlar que se anuncie toda la región o solo las partes que han cambiado.
  • aria-relevant permite especificar si queremos que se anuncien solo los elementos añadidos, o solo los eliminados, o solo las modificaciones de texto, o bien todos los cambios.

Para ampliar información sobre las Live Region podéis consultar el artículo: Live Regions y WAI-ARIA. Cómo mejorar la accesibilidad de contenidos que se actualizan automáticamente y ver el ejemplo Ejemplo ARIA: Live Región.

Window Roles

Agrupa dos roles que tampoco son nuevos, ya estaban en ARIA 1.0:

  • role="alertdialog": identifica una ventana modal con un mensaje de alerta.

    A diferencia del role="alert" visto en los Live Region Roles:

    • la ventana role="alertdialog" puede recibir una respuesta del usuario, por ejemplo puede tener un botón para que el usuario indique que ha entendido el mensaje;
    • cuando se muestra la ventana role="alertdialog", el foco inicial se dirige a un elemento dentro del diálogo; sin embargo, con role="alert" el foco no debe moverse.

    El mensaje de alerta debería estar referenciado con aria-describedby.

  • role="dialog": identifica una ventana, normalmente modal, que desciende de la ventana principal (en una página HTML, el BODY)

    Se suele utilizar para que el usuario inserte información o responda a la información.

    Debe tener al menos un elemento que pueda coger el foco, ya que cuando se muestre la ventana, se debe enviar el foco a alguno de sus elementos.

    También debe tener una etiqueta, normalmente con aria-label o aria-labelledby.

Para ampliar información sobre los atributos nombrados, puedes consultar los artículos:

Nuevos roles

Los roles listados en el apartado anterior no son nuevos, solo están organizados de diferente manera.

En WAI-ARIA 1.1 también hay roles nuevos, que son los que voy a enumerar a continuación.

Nuevos roles en Document Structure Roles

  • role="feed": una lista escrolable de artículos cuyo "scroll" puede provocar que los artículos se añadan o eliminen de los extremos de la lista (no en el medio). Por tanto, el "feed" es un contenedor y sus hijos tienen el role="article".

    Por ejemplo, puede usarse para presentar un flujo de noticias, donde cada artículo contiene texto, enlaces, imágenes, comentarios y widgets para compartir y comentar. A medida que escrolas se van cargando nuevas noticias. Como alternativa (y también sería "feed") se puede incluir un "article" en uno u ambos extremos, con un enlace o botón para solicitar que se carguen más noticias.

    Cada artículo debe poder coger el foco, y el elemento que lo coge debe estar a la vista. También se deben proporcionar comandos de teclado para saltar de un "article" a otro dentro del "feed", por si el producto de apoyo no ofrece funciones de navegación para ello.

    Ejemplo de Feed correctamente implementado

  • role="none": es simplemente un sinónimo de role="presentation", al que espera sustituir cuando esté ampliamente soportado.

    La razón de querer sustituir el role="presentation" es la confusión que genera su nombre, que hace que muchos autores lo usen como sinónimo de aria-hidden.

    Un elemento al que se le aplique el rol "none" o "presentation", no es ignorado por el lector de pantalla (salvo en el caso de las imágenes), sino que es anunciado solo como texto.

    Es decir, <h1 role="none">Hola</h1> será anunciado como "Hola" en vez de como "Hola Encabezado de primer nivel".

  • role="figure": al igual que el elemento FIGURE de HTML5, es una sección que suele contener o elementos gráficos o un fragmento de código o un texto de ejemplo.

    Puedes consultar más información en: LONGDESC. Soporte y alternativas (WCAG 2.0, ARIA, HTML5). Apartado del artículo: "Figure"

  • role="term": identifica la palabra o frase que se va a definir, mientras que role="definition" identifica la definición (relacionándolo con aria-labelledby).

    No se debe utilizar con enlaces para evitar problemas a los usuarios de productos de apoyo.

  • role="table" y role="cell": role="table" es una tabla no interactiva, de lo contrario debería utilizarse el rol "grid" o "treegrid".

    role="cell" es el rol a aplicar a lo que serían las celdas (los TD si fuera una tabla estándar) del role="table". Un role="cell" debe estar dentro de un role="row".

    Ver un ejemplo de una tabla simulada con elementos DIV y SPAN a los que se aplican los roles "table", "row" y "cell".

    Ver ejemplos de grids

Nuevos roles en Widget Roles

  • role="switch": es un tipo de checkbox que representa los valores on/off, a diferencia de los valores checked/unchecked.

    El atributo que expresa su estado es aria-checked. No es válido un valor mixto (el agente de usuario lo trataría como "false").

  • role="searchbox": es la caja de texto de un buscador
  • role="separator": este rol está ahora presente en dos tipos de roles, según pueda o no coger el foco.
    • role="separator" (when not focusable): se mantiene el role="separator" como rol de estructura (Document Structure) cuando es un mero separador visible que no puede coger el foco;
    • role="separator" (when focusable): se añade en los Widget Roles cuando es un separador interactivo que puede coger el foco, por ejemplo para modificar el tamaño de las zonas que separa. En estos casos se puede mover dentro de un rango y tiene las propiedades obligatorias: aria-valuemax, aria-valuemin y aria-valuenow.

Cambios en los Landmark Roles

  • role="application": se ha eliminado de los Landmark Roles y ahora se incluye en los Document Structure Roles. Además, ahora admite la propiedad aria-activedescendant.
  • role="region": se ha eliminado de los Document Structure Roles y ahora se incluye en los Landmark Roles. De hecho, hace tiempo que por ejemplo JAWS lista los role="region" dentro del listado de landmarks.

Puedes ampliar información sobre los Landmark Roles en el artículo: Landmark Roles (WAI-ARIA). Navegación más accesible y semántica en 2 minutos.

Otros cambios en roles

  • role="rowgroup": ya no cuelga de "group" sino de "structure".
  • role="heading": su atributo aria-level pasa a ser obligatorio, y su valor por defecto es igual a 2.
  • role="option": su atributo aria-selected pasa a ser obligatorio.
  • role="scrollbar", role="slider" y role="spinbutton": siguen teniendo las propiedades obligatorias: aria-valuemin, aria-valuemax y aria-valuenow, pero ahora se definen sus valores por defecto.
  • role="combobox": se sigue aplicando a un "input", pero cuando está expandido, además de un "listbox", ahora se admite también: "tree", "grid" y "dialog". Ver ejemplo de uso de combobox

Propiedades y estados

aria-details y aria-describedat

Finalmente, ARIA 1.1 NO incluye la propiedad aria-describedat (que tenía la misma función que longdesc) puesto que considera que queda obsoleta con la inclusión de la nueva propiedad aria-details.

aria-details identifica el elemento que proporciona una descripción detallada y extendida del objeto, visible para todos los usuarios.

A diferencia de aria-describedby, el producto de apoyo notificará la disponibilidad de esta descripción y permitirá navegar a ella. La descripción no se leerá plana y seguida como con aria-describedby.

Si se incluyen ambos atributos (aria-describedby y aria-details) deberá prevalecer aria-details.

Consultar ejemplo del uso correcto de aria-details.

aria-kbdshortcuts y aria-keyshortcuts

Finalmente, aria-kbdshortcuts NO se incluye, sino que es reemplazado por aria-keyshortcuts. Este atributo sirve para informar de los atajos de teclado implementados por javascript para un componente, y por lo tanto no es lo mismo que las accesskey.

Sería por ejemplo:

<button id="exUp" aria-keyshortcuts="Alt+ArrowUp">Up< button>

aria-roledescription

aria-roledescription puede ser muy útil, sirve para sobrescribir el nombre que quieres que le dé el producto de apoyo a un rol.

Por ejemplo: <div role="region" aria-roledescription="carrusel"> sería anunciado como "carrusel" en vez de como "región".

Su uso debería limitarse a clarificar el propósito de contenedores con roles como "group" o "region", o a dar una descripción más específica de un widget.

Es importante incidir en que los productos de apoyo se limitarían a anunciarlo con ese nombre, no estás cambiando el rol del elemento.

Además debe cumplir dos condiciones:

  • El elemento al que se aplica debe tener un rol ARIA válido o un rol semántico implícito.
  • Su valor no puede estar vacío o tener solo caracteres en blanco.

aria-errormessage

Identifica el elemento que proporciona un mensaje de error para el objeto, por ejemplo en las validaciones de los formularios.

Por ejemplo:

<label for="startTime">Please enter a start time for the meeting:</label>

<input id="startTime" type="text" aria-errormessage="msgID" value="" aria-invalid="false">

<span id="msgID" aria-live="off" style="visibility:hidden"> Invalid time: the time must be between 9:00 AM and 5:00 PM" </span>

Otras propiedades y estados nuevos:

  • aria-modal: para identificar un elemento modal, como una ventana. Un elemento es modal cuando se impide el uso del resto de la página hasta que el elemento modal pierda el foco o ya no se muestre.
  • aria-placeholder: para indicar el texto por defecto dentro del campo. Ese texto puede ser una sugerencia, pero según las WCAG no debe ser la etiqueta del campo.
  • aria-current: identifica el elemento actual dentro de un contenedor o un conjunto de elementos relacionados.

    Es muy útil para indicar el día actual dentro de un calendario; o, en una paginación, la página actual; o en un proceso por pasos, el paso actual.

    Solo un elemento dentro de un conjunto puede estar marcado con aria-current.

    No debe usarse en sustitución de aria-selected en los widget donde este tiene el mismo significado, por ejemplo en una "tablist".

  • aria-colcount, aria-rowcount, aria-colindex, aria-rowindex, aria-colspan, and aria-rowspan: todas ellas propiedades de los roles: "table", "grid" y "treegrid".

Propiedades y estados que han sufrido modificaciones:

Otras propiedades y estados existían en ARIA 1.0 pero han sufrido modificaciones:

  • aria-orientation: está admitido en más roles y, además de horizontal y vertical, tiene un nuevo valor por defecto: "undefined".
  • aria-haspopup: además de los valores "true" y "false", tiene los valores: "menú", "listbox", "tree", "grid" y "dialog", que indica que el "popup" es, respectivamente, un "menú", "listbox", "tree", "grid" o un "dialog".

    Entendemos por "popup" un elemento emergente interactivo, como un menú o un diálogo, que generalmente aparece como un bloque de contenido por encima de otro contenido. Un tooltip NO se considera un popup en este contexto.

  • aria-readonly: es ahora admitido por más roles, en concreto, por: "checkbox", "combobox", "grid", "gridcell", "listbox", "radiogroup", "slider", "spinbutton" y "textbox".

  • aria-setsize: admite ahora el valor -1 si el número total de elementos es desconocido.
  • aria-posinset y aria-setsize: están admitidos por más roles, en concreto, por: "article", "listitem", "menuitem", "option", "radio" y "tab".
  • aria-busy: cuando se está modificando un elemento, se puede cambiar el estado de esta propiedad para indicar a los productos de apoyo que esperen a que las modificaciones estén completas para exponerlas al usuario.

    La novedad es que ya no hace referencia específicamente a las live region en la descripción de sus valores. Además, se describen con más detalle diferentes casuísticas. Ver requisitos de aria-busy

Propiedades y estados deprecated (obsoletos) en ARIA 1.1

Los elementos obsoletos pueden eliminarse en futuras versiones, pero se recomienda a los agentes de usuario que continúen admitiéndolos por compatibilidad con versiones anteriores.

Hay dos:

  • aria-dropeffect
  • aria-grabbed

Importante recordar ...

Cuando hablamos de WAI-ARIA siempre me gusta recordar varias cosas, entre ellas me parece fundamental:

  1. Cuando puedas utilizar el marcado nativo HTML, hazlo.

    Que puedas marcar un DIV como role="table" o role="img", o anular el marcado semántico de un elemento aplicándole un rol diferente, no significa que debas hacerlo, tiene que estar muy justificado.

  2. Cambia los estados y propiedades en respuesta a los eventos.

    No sirve de nada marcar un elemento como <li role="treeitem" aria-expanded="false"> si cuando el usuario abre y cierra el árbol no cambia el estado de aria-expanded de true a false y viceversa. Este es un fallo que encuentro habitualmente.

  3. Ten en cuenta el soporte de WAI-ARIA por parte de los agentes de usuario y los productos de apoyo.

    El navegador expone los roles, propiedades y estados a la API de accesibilidad, y los productos de apoyo toman esta información para exponérsela a los usuarios.

    Esquema con tres recuadros (Agente de usuario, API de accesibilidad y Productos de apoyo) relacionados con flechas bidereccionales. En el primero recuadro Agente de usuario: navegador (datos y UI) y JavaScript. En el segundo recuadro API de accesibilidad: roles, estados y propiedades, jerarquía implícita del DOM y eventos de estados y propiedades.

    The contract model with accessibility APIs (imagen del W3C)

    Por tanto, no es lo mismo acceder por ejemplo con Explorer y JAWS, que con Chrome y NVDA. Todos los navegadores soportan desde hace tiempo WAI-ARIA (Explorer desde la versión 8).

    Soportado por todos los navegadores menos por UC Browser for Android

    Soporte de WAI-ARIA por parte de los navegadores (caniuse.com)

    También lo soportan los principales productos de apoyo: NVDA desde 2010, JAWS desde la versión 8, Window-Eyes desde la versión 7 o VoiceOver desde OSX 10.5 e iOS4 (aunque el soporte de las live regions ha sido en todos posterior).

    Sin embargo, el soporte concreto de cada rol, propiedad o estado varía, y el soporte de las novedades suele ser siempre menor.

    Recuerda el principio de mejora progresiva e implementa pensando en la compatibilidad actual (y con versiones anteriores) de productos de apoyo y agentes de usuario.

    Ver soporte de las novedades ARIA 1.1 por parte de los principales navegadores


Enlaces de interés:

Artículos relacionados:

Servicios de accesibilidad que ofrezco como consultora freelance

Imagen inicial del artículo de webaccessibile.org

1 comentarios :
Fron End dijo...

Qué buen artículo, felicitaciones!

Publicar un comentario