Capítulo 4. Ejecutar acciones

Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com

Este capítulo entero se centra en un único elemento HTML, el <button>. Hay dos razones por las que doy tanto espacio a los botones.

En primer lugar, el botón es un elemento común; lo encontrarás en la mayoría de las páginas: en un formulario de contacto o de suscripción al boletín, para alternar una navegación móvil o para cerrar un cuadro de diálogo emergente.

En segundo lugar, aunque los desarrolladores web necesitan botones muy a menudo, son malos implementándolos correctamente. Esto no se basa sólo en años de mi experiencia auditando sitios que otros han construido; también hay datos que lo confirman.

Por eso este capítulo se centra en los requisitos más esenciales del elemento botón y en cómo cumplirlos.

4.1 Elige el elemento adecuado

Problema

Si un botón no cumple los requisitos básicos, puedes excluir a uno o varios grupos de usuarios de poder acceder a él o de comprender su finalidad.

La sección de discusión de esta receta proporciona más detalles, pero los criterios esenciales de accesibilidad para un botón son:

  • Debe transmitir su función semántica de botón mediante programación.

  • Tiene un nombre accesible conciso y sencillo (texto visible o una alternativa textual).

  • Comunica su estado (pulsado, expandido, etc.) si es necesario.

  • Se reconoce como un botón.

  • Sus colores deben tener suficiente contraste.

  • Debe ser enfocable y permitir la activación mediante eventos de clic, toque y tecla.

Solución

Utiliza el elemento botón nativo y los atributos HTML y ARIA para ajustar sus características según los requisitos, como se muestra en los Ejemplos 4-1, 4-2 y 4-3.

Ejemplo 4-1. Un botón de envío nativo utilizado en un formulario para enviar datos
<form>
  <label for="email">Email address</label>
  <input type="email" id="email" name="email">

  <button>Sign up</button>
</form>
Ejemplo 4-2. Un botón que ejecuta JavaScript
<button type="button" onclick="print()">
  Print
</button>
Ejemplo 4-3. Un botón comunicando su estado pulsado
<button type="button" aria-pressed="true">
  Mute
</button>

Debate

El elemento botón tiene dos usos principales: enviar un formulario y ejecutar JavaScript cuando un usuario interactúa con él.

Un botón se convierte en un botón de envío cuando estableces su tipo como envío o en el contexto de un formulario, como se muestra en el Ejemplo 4-1. Normalmente utilizas un botón de envío para enviar los datos de un formulario a un servidor.

Si estableces el tipo a botón, el botón no hace nada. Lo haces si quieres ejecutar JavaScript cuando el usuario active el botón. Por ejemplo, al pulsar el botón del Ejemplo 4-2, se abre el diálogo de impresión del navegador. Otros usos comunes son alternar la visibilidad de otros elementos (ver Capítulo 8), abrir diálogos modales (ver Receta 11.3) y ejecutar otras funciones de JavaScript.

Según las WCAG, un botón debe cumplir al menos seis requisitos específicos para ser accesible y proporcionar una gran UX. Veamos cada uno de ellos:

Es debe transmitir su función semántica de botón mediante programación.

Tus botones deben transmitir sus roles semánticos. La opción más segura y fiable es utilizar el elemento <button>, porque tiene un papel button implícito.

Un lector de pantalla anunciará la función del botón junto a su nombre; por ejemplo, "Imprimir, botón".

tiene un nombre accesible (texto visible o una alternativa textual).

Independientemente de que un botón contenga texto, debe tener un nombre accesible. No hay excepciones a esta regla. Si no etiquetas los botones, los usuarios de lectores de pantalla y de voz no sabrán qué hacer con ellos.

Hay distintas formas de etiquetar los botones. La Receta 4.2 describe algunas de ellas, pero lo mejor que puedes hacer en la mayoría de los casos es incluir un texto que sea visible para todos.

En caso necesario, comunica su estado (pulsado, expandido, etc.) a .

Un botón puede tener diferentes estados o controlar el estado de otro elemento: Si es así, debe transmitir su estado o relación mediante atributos ARIA. El botón del Ejemplo 4-3 transmite que algunos medios están silenciados. Puedes encontrar más ejemplos en la Receta 4.5.

Se reconoce como un botón.

Según la ley de Jakob, los usuarios de pasan la mayor parte de su tiempo en otros sitios, lo que significa que los usuarios prefieren que tu sitio funcione igual que todos los demás sitios que ya conocen. En el caso de los botones, esto significa que deben cumplir las expectativas visuales que el usuario tiene de un botón. Si un botón parece un botón, es más fácil que los usuarios, especialmente los que tienen discapacidades cognitivas, comprendan su finalidad. Esto también se aplica a su nombre accesible. Si funciones idénticas tienen nombres accesibles diferentes en páginas distintas, el sitio será más difícil de utilizar.

Sus colores deben tener suficiente contraste.

Sólo como la mayor parte del texto o imágenes de texto de una página web, los botones también deben cumplir unos requisitos mínimos de contraste. El color del texto de un botón debe tener una relación de contraste de al menos 4,5:1 para el texto normal o de 3:1 para el texto a gran escala o en negrita.

Otros colores utilizados en el botón, como el color de fondo o los colores de los indicadores de enfoque, también deben cumplir los requisitos de relación de contraste mínima frente a los colores adyacentes. En otras palabras, el color de fondo del botón o el contorno de un indicador de enfoque o borde también deben tener una relación de contraste de al menos 3:1 frente al fondo, que suele ser un componente principal o la propia página. Los controles de bajo contraste son más difíciles de percibir y pueden pasar completamente desapercibidos para las personas con baja visión.

It debe ser tabbable y permitir la activación mediante eventos de clic, toque y tecla.

Un botón es un elemento interactivo, lo que significa que si puedes hacer clic en él, también debes poder realizar la misma acción utilizando el teclado pulsando Enter o Space. Para ello, debe ser tabulable (accesible mediante la tecla Tab ), lo que es cierto por defecto para el elemento <button>.

La mayoría de los sitios web contienen botones. Intenta seguir las buenas prácticas de este capítulo y evita varias soluciones alternativas comunes que no cumplen los requisitos, enumeradas en el Ejemplo 4-4. Es posible recrear la funcionalidad predeterminada del botón nativo utilizando un elemento diferente, como el <div>, pero normalmente no merece la pena el esfuerzo porque el <button> viene con la mayoría de las funciones descritas en esta receta .

Ejemplo 4-4. Malas prácticas: Alternativas comunes de botones inaccesibles que debes evitar
<!-- Div with a click event -->
<div onclick="[JS function]">
  O'Reilly books and videos
</div>

<!-- Non-focusable button -->
<div role="button" onclick="[JS function]">
  O'Reilly books and videos
</div>

<!-- Link with a click event -->
<a href="javascript: void(0)" onclick="[JS function]">
  Menu
</a>

<!-- Link with a click event -->
<a href="#" onclick="[JS function]">
  Menu
</a>

4.2 Etiquetar claramente los botones

Problema

Botones puede adoptar diversas formas. El contenido envuelto en el botón puede ser texto, un icono o ambos. Cuando no nombras un botón, independientemente de que el diseño lo prevea, los usuarios de lectores de pantalla pueden ser incapaces de saber su finalidad.

Solución

En primer lugar, si el botón contiene texto, ese texto sirve de etiqueta, como se muestra en el Ejemplo 4-5.

Ejemplo 4-5. Botón con el nombre accesible procedente de su contenido
<button type="button">
  Download
</button>

En segundo lugar, si el botón contiene una imagen , el atributo alt de la imagen debe proporcionar la etiqueta, como en el Ejemplo 4-6.

Ejemplo 4-6. Botón con el nombre accesible procedente del atributo alt de la imagen
<button type="button">
  <img src="/images/download.svg" alt="Download" width="26">
</button>

En lugar de un img, también puedes utilizar a un SVG con un elemento <title>. Para compatibilidad entre navegadores, utiliza aria-labelledby en el SVG y crea una referencia al título, como se muestra en el Ejemplo 4-7.

Ejemplo 4-7. Botón con el nombre accesible procedente del SVG
<button type="button">
  <svg viewBox="0 0 39 44" aria-labelledby="title" role="img" width="26">
    <title id="title">Download</title>
    <path d="M19.5 36.5 1.6 26.1v-3.6l16.3 9.4V1.5h3.2v30.4l16.3-9.4v3.6z"/>
    <path d="M1 41.5h37" style="stroke:#000;stroke-width:3;"/>
  </svg>
</button>

También puedes eliminar el gráfico del árbol de accesibilidad definiendo un atributo alt vacío en img o aria-hidden="true" en el SVG. Si haces eso, el botón aún necesita una alternativa de texto, que puedes proporcionar con texto visualmente oculto (véase el Ejemplo 4-8), aria-labelledby, o aria-label (véase el Ejemplo 4-9).

Ejemplo 4-8. El texto oculto visualmente etiqueta el botón
<button type="button">
  <span class="visually-hidden">Download</span>
  <img src="/images/download.svg" alt="" width="26">
</button>

<!-- or -->

<button type="button">
  <span class="visually-hidden">Download</span>
  <svg viewBox="0 0 39 44" aria-hidden="true" width="26">
    <path d="M19.5 36.5 1.6 26.1v-3.6l16.3 9.4V1.5h3.2v30.4l16.3-9.4v3.6z"/>
    <path d="M1 41.5h37" style="stroke:#000;stroke-width:3;"/>
  </svg>
</button>

<style>
  .visually-hidden {
    clip-path: inset(50%);
    height: 1px;
    overflow: hidden;
    position: absolute;
    white-space: nowrap;
    width: 1px;
  }
</style>
Ejemplo 4-9. El atributo aria-label etiqueta el botón
<button type="button" aria-label="Save">
  <svg aria-hidden="true" viewBox="0 0 39 44" width="26">
    <path d="M19.5 36.5 1.6 26.1v-3.6l16.3 9.4V1.5h3.2v30.4l16.3-9.4v3.6z"/>
    <path d="M1 41.5h37" style="stroke:#000;stroke-width:3;"/>
  </svg>
</button>

Eliminar los gráficos anidados del árbol de accesibilidad también es útil cuando combinas un icono con texto, porque el texto elimina la necesidad de una etiqueta adicional para el icono, como se muestra en el Ejemplo 4-10.

Ejemplo 4-10. Combinación de texto e icono
<button type="button">
  Save
  <img src="/images/download.svg" alt="" width="26">
</button>

<button type="button">
  Save
  <svg aria-hidden="true" viewBox="0 0 39 44" width="26">
    <path d="M19.5 36.5 1.6 26.1v-3.6l16.3 9.4V1.5h3.2v30.4l16.3-9.4v3.6z"/>
    <path d="M1 41.5h37" style="stroke:#000;stroke-width:3;"/>
  </svg>
</button>

Debate

Según el informe WebAIM Million 2024, que es el resultado de una evaluación anual automatizada de la accesibilidad de los 1 millón de sitios web más visitados, el 28,2% de los sitios web analizados contenían botones vacíos, lo que lo convierte en uno de los cinco principales problemas detectados por el estudio. Un botón vacío es un botón sin hijos o un botón que contiene gráficos sin etiquetar y sin ninguna otra fuente que lo etiquete.

Existen diferentes métodos para etiquetar botones. El que elijas dependerá de los requisitos. Cuando tengas varias opciones, te recomiendo que sigas el orden de prioridad que describe Adrian Roselli en "Mi prioridad de métodos para etiquetar un control":

  1. Técnicas HTML nativas

  2. aria-labelledby señalando el texto visible existente

  3. Contenido visiblemente oculto

  4. aria-label

Si el botón contiene sólo texto o una combinación de texto e iconos, coloca la etiqueta como texto entre la etiqueta inicial y final del elemento, como se muestra en el Ejemplo 4-5. Si hay un icono que no proporciona información adicional, elimínalo del árbol de accesibilidad para evitar redundancias (véase el Ejemplo 4-10).

Si no hay texto visible, sino sólo una imagen o icono, su texto alternativo puede servir como nombre del botón (ver Ejemplos 4-6 y 4-7). Este tipo de imágenes de se denominan imágenes funcionales. Su texto alternativo no debe describir lo que muestran, sino su finalidad. Como alternativa, puedes utilizar texto oculto visualmente(Ejemplo 4-8), aria-labelledby, o el atributo aria-label (Ejemplo 4-9).

La falta de nombres accesibles es probablemente el problema más común, pero los incorrectos también pueden ser problemáticos. A veces están en el idioma natural equivocado, como el inglés en un sitio francés. A veces contienen variables no resueltas o texto de marcador de posición. Estos problemas suelen surgir con los botones que sólo contienen iconos. Puedes pasar por alto fácilmente el texto oculto visualmente o las etiquetas proporcionadas a través de atributos como aria-label, ya que sólo son visibles en el código, no en la interfaz de usuario renderizada. Debes favorecer los botones con texto visible porque son universalmente comprensibles y menos propensos a errores.

La etiqueta debe ser informativa y concisa. No etiquetes un botón "Haz clic aquí para abrir o cerrar la navegación". "Navegación" es suficiente en .

4.3 Eliminar los Estilos de Botón por Defecto

Problema

Incluso cuando un botón no parece un botón, debe seguir cumpliendo la mayoría de los requisitos descritos en la Receta 4.1. Si no lo hace, podría no ser accesible para los usuarios de teclado y lectores de pantalla. Si eliges un elemento genérico con menos estilos por defecto en lugar del elemento <button>, es posible que los usuarios no puedan acceder al botón falso ni entender su finalidad.

Solución

Si quieres utilizar un botón pero no quieres que parezca un botón, debes seguir utilizando el elemento <button> pero eliminando los estilos de botón por defecto. CSS ofrece tres estrategias eficaces para eliminar los estilos por defecto de los botones, como se muestra en los Ejemplos 4-11, 4-12 y 4-13.

Ejemplo 4-11. Eliminar o restablecer propiedades manualmente
button {
  background: none;
  border: 0.1em solid transparent; 1
  font: inherit;
  padding: 0;
}
1

Muestra un contorno en modo de colores forzados, que puede ser útil.

Ejemplo 4-12. Restablecer todas las propiedades de los botones a su valor inicial
button {
  all: initial;
}

button:focus-visible {
  outline: 0.1em solid;
  outline-offset: 0.1em;
}
Ejemplo 4-13. Restablecer todas las propiedades de los botones a su valor inicial, excepto las heredables
button {
  all: unset;
}

button:focus-visible {
  outline: 0.1em solid;
  outline-offset: 0.1em;
}

Debate

Los elementos de botón de los sistemas operativos y las páginas web tienen una forma y un estilo particulares por defecto: un rectángulo con un borde, un color de fondo y algo de relleno entre el texto y el borde. Cuando das estilo a una página, normalmente te ciñes a estas características por defecto. Cambias los valores por defecto y tal vez añades propiedades, como se muestra en el Ejemplo 4-14. También puedes tener versiones de ese botón que varíen en tamaño, color y forma.

Ejemplo 4-14. Estilos personalizados para botones
button {
  --_l: 0.47;

  background: oklch(var(--_l) 0.05 195.6);
  color: #fff;
  font-size: 1.2rem;
  font-family: inherit;
  padding: 0.4em 0.9em;
  border-radius: 3px;
  border: 0;
  min-inline-size: 7rem;
}

button:is(:hover, :focus-visible) {
  --_l: 0.27; 1
}
1

El color de fondo se oscurece en hover y focus-visible.

Algunos botones no parecen botones porque sólo consisten en un icono. La forma de implementar un botón de este tipo es crucial. Uno de los problemas de accesibilidad más comunes en la mayoría de los sitios web que audito son los botones falsos: muchos desarrolladores asumen que si un control no parece un botón, no tiene por qué ser un elemento <button>. Su razonamiento es: si no hay estilos de botón en primer lugar, no tienes que eliminarlos. Esto a menudo da lugar a un código como el del Ejemplo 4-15, que parece inofensivo pero supone una diferencia considerable para la accesibilidad.

Ejemplo 4-15. Mala práctica: Un botón div falso
<div class="button" aria-label="Navigation">
  <svg width="24" height="24" aria-hidden="true">
    <path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z">
  </svg>
</div>

<script>
const button = document.querySelector(".button");
button.addEventListener("click", (e) => {
  console.log("do something");
});
</script>

Hay varios problemas con este "botón":"

  • No es enfocable mediante el teclado.

  • Aunque fuera enfocable, la activación mediante Intro o Espacio no funcionaría.

  • Los lectores de pantalla no lo anuncian como un botón.

  • Algunos lectores de pantalla no lo anuncian en absoluto.

  • No debes utilizar aria-label en elementos genéricos porque no es válido nombrarlos.

El hecho de que este "botón" no sea enfocable afecta a los usuarios de teclado y de lectores de pantalla, pero estos últimos pueden utilizar el cursor virtual para acceder al botón falso. Si se adjunta un receptor de eventos de clic a div, algunos lectores de pantalla te indicarán que puedes hacer clic en él. Sin embargo, si miras en la Tabla 4-1, verás que utilizar un div o cualquier otro elemento no interactivo no es fiable. Si necesitas eliminar los estilos predeterminados de los botones, ceñirte al elemento <button> y restablecer los estilos mediante CSS es la opción más segura.

Tabla 4-1. Prueba de lector de pantalla: accesibilidad de un botón div
Lector de pantalla Navegador Narración con lector de pantalla

NVDA

Firefox

Navegación

JAWS

Cromo

N/A

Narrador

Perímetro

N/A

TalkBack

Cromo

Navegación

VoiceOver macOS

Safari

Navegación, grupo vacío

VoiceOver iOS

Safari

N/A

No sería viable utilizar un elemento genérico en lugar de un botón o poner escuchadores de eventos en elementos no interactivos. Necesitas un elemento interactivo si el usuario puede interactuar con un componente.

Las cuatro líneas de CSS que ves en el Ejemplo 4-11 son todo lo que necesitas para eliminar los estilos de agente de usuario por defecto de los botones, suponiendo que no hayas añadido más reglas, como hicimos en el Ejemplo 4-14. En ese caso, también tendrías que restablecer esos estilos. Ése es el problema de este enfoque manual: debes hacer un seguimiento de los cambios en tus estilos predeterminados personalizados y ajustar los estilos de restablecimiento en consecuencia, o bien restablecer todas las propiedades posibles de antemano.

El enfoque ilustrado en los Ejemplos 4-12 y 4-13 es mucho más eficiente. Puedes utilizar la propiedad all en CSS para restablecer todas las propiedades de un elemento, excepto unicode-bidi, direction, y las Propiedades Personalizadas CSS. Dependiendo de tus necesidades, puedes establecer su valor en initial o unset (ver Figura 4-1).

The button looks like regular text written in a serif font.
Figura 4-1. Un botón con el estilo por defecto, un botón con todas las propiedades establecidas en initial, y un botón con todas las propiedades establecidas en unset

La palabra clave initial establece todos los valores de las propiedades en su valor inicial. Cada propiedad tiene un valor inicial, definido en la tabla de definición de la propiedad. Por ejemplo, si miras la propiedad color en la especificación, el valor inicial definido en la tabla de definición es CanvasText. Ten en cuenta que el valor inicial no es el valor por defecto de la propiedad definido en el agente de usuario.

La palabra clave unset restablece una propiedad a su valor heredado, si la propiedad hereda naturalmente de su padre, y a su valor inicial en caso contrario. Eso puede ser útil si quieres mantener propiedades específicas como font-family o color.

En cualquier caso, puede que tengas que recuperar los estilos focus y hover, porque all también los restablece. La cuestión es que aunque un botón no parezca un botón, debe comportarse como .

4.4 Añadir estados y propiedades

Problema

Cuando los usuarios del lector de pantalla utilizan botones para controlar otros elementos de la página o ajustes del sitio, esos botones deben proporcionar tanta información como sea posible. Eso puede incluir el tipo de botón, el tipo de elemento asociado o el estado de un botón o elemento controlado. Si falta esa información, es mucho más complicado -o a veces incluso imposible- saber si el usuario ha activado el botón correctamente y qué ocurre cuando lo hace.

Solución

Un botón que conmuta la visibilidad de otro elemento necesita comunicar si el elemento está expandido, como se muestra en en el Ejemplo 4-16.

Ejemplo 4-16. El atributo aria-expanded del botón que comunica si la navegación es visible
<nav>
  <button aria-expanded="false" aria-controls="main_nav"> 1
    Navigation
  </button>

  <ul id="main_nav"></ul>
</nav>

<style>
  [aria-expanded="false"] + ul { 2
    display: none;
  }
</style>

<script>
const button = document.querySelector("button");

button.addEventListener("click", (e) => {  3
  const isExpanded = button.getAttribute("aria-expanded") === "true";
  button.setAttribute("aria-expanded", !isExpanded);
});
</script>
1

aria-expanded debe estar en el botón y no en el elemento expandible.

2

La lista se oculta en función del valor del atributo.

3

El evento de clic en el botón cambia el valor del atributo aria-expanded.

Un botón que activa o desactiva un ajuste debe comunicar si está activo, como se muestra en el Ejemplo 4-17.

Ejemplo 4-17. Un botón comunicando su estado pulsado
<button type="button" aria-pressed="true"> 1
  Add to favourites
</button>

<script>
const button = document.querySelector("button");

button.addEventListener("click", (e) => {
  const isPressed = button.getAttribute("aria-pressed") === "true";
  button.setAttribute("aria-pressed", !isPressed);
});
</script>
1

El botón indica que algo se ha añadido como favorito.

Un botón que conmuta un ajuste debe comunicar si está activo. La Figura 4-2 y el Ejemplo 4-18 muestran un botón interruptor que comunica su estado mediante el atributo aria-checked.

A green button with rounded corners. A white circle inside it, aligned to the right indicating is switched on. Text 'functional cookies' outside, on the right of the button.
Figura 4-2. Un interruptor basculante
Ejemplo 4-18. Un botón interruptor
<button role="switch" aria-checked="false">Functional cookies</button>

<script>
const button = document.querySelector("button");

button.addEventListener("click", (e) => {
  const isChecked = button.getAttribute("aria-checked") === "true";
  button.setAttribute("aria-checked", !isChecked);
});
</script>

<style>
  button {
    --toggle-offset: 0.125em;
    --toggle-height: 1.6em;
    --toggle-background: oklab(0.82 0 0);

    all: unset; 1
    align-items: center;
    display: flex;
    gap: 0.5em;
    position: relative;
  }

  button::before { 2
    background: var(--toggle-background);
    border-radius: 4em;
    content: "";
    display: inline-block;
    height: var(--toggle-height);
    transition: background 0.3s, box-shadow 0.3s;
    width: 3em;
  }

  button::after { 3
    --_size: calc(var(--toggle-height) - (var(--toggle-offset) * 2));

    background: #FFF;
    border-radius: 50%;
    content: "";
    height: var(--_size);
    left: var(--toggle-offset);
    position: absolute;
    transition: translate 0.3s;
    top: var(--toggle-offset);
    width: var(--_size);
  }

  button:focus-visible::before { 4
    outline: 2px solid;
    outline-offset: 2px;
  }

  button:is(:focus-visible, :hover)::before {  5
    box-shadow: 0px 0px 3px 1px rgb(0 0 0 / 0.3);
  }

  [aria-checked="true"] { 6
    --toggle-background: oklab(0.7 -0.18 0.17);
  }

  button[aria-checked="true"]::after {  7
    translate: 100% 0;
  }
</style>
1

Restablece los estilos de botón por defecto.

2

Pseudoelemento para el fondo del interruptor.

3

Pseudoelemento para el indicador móvil del interruptor.

4

Añade estilos de enfoque personalizados.

5

Muestra una sombra de caja en :hover y :focus-visible.

6

Cambia el color de fondo del interruptor a verde cuando está activo.

7

Mueve el indicador al extremo del interruptor cuando está activo.

Un botón puede comunicar su estado y qué tipo de elemento controla, como se muestra en el Ejemplo 4-19.

Ejemplo 4-19. Un botón que controla un menú
<button
  type="button"
  aria-haspopup="menu" 1
  aria-expanded="false" 2
  id="button_settings"
>
  Settings
</button>

<ul role="menu" aria-labelledby="button_settings" hidden> 3
  <li role="none">
    <button role="menuitem">Print</button>
  </li>
  <li role="none">
    <button role="menuitem">Save</button>
  </li>
</ul>

<style>
  [aria-expanded="true"] + ul { 4
    display: block;
  }
</style>

<script>
const button = document.querySelector("button");

button.addEventListener("click", (e) => { 5
  const isExpanded = button.getAttribute("aria-expanded") === "true";
  button.setAttribute("aria-expanded", !isExpanded);
});
</script>
1

Indica que el botón controla un menú.

2

Indica que el menú controlado está colapsado.

3

El menú está oculto por defecto.

4

La lista se oculta en función del valor del atributo.

5

El evento de clic en el botón cambia el valor del atributo aria-expanded.

Debate

Muchos atributos de la especificación ARIA proporcionan estados o propiedades a los elementos. Cuando construyas widgets JavaScript personalizados, utilizarás algunos de ellos con frecuencia. La propiedad arial-label, por ejemplo, da a un elemento un nombre accesible, mientras que el estado aria-hidden elimina un elemento del árbol de accesibilidad.

Esta receta se centra en cuatro atributos utilizados habitualmente con los botones.

El estado ampliado

En puedes utilizar el atributoaria-expanded de un elemento botón para indicar si un elemento de agrupación que controla está expandido o colapsado.

Nota

El elemento controlado puede ser prácticamente cualquier elemento, pero suele ser un elemento de agrupación como <div>, <p>, o <ul>.

En el Ejemplo 4-16, puedes ver que el botón tiene el atributo y comunica que el elemento asociado está expandido. Los usuarios deben entender por el texto del botón qué elemento controla el botón. Por eso debes evitar textos genéricos como "mostrar/ocultar". El atributo puede ser útil para navegaciones fly-in (ver Receta 7.5), para submenús en navegaciones anidadas (ver Receta 7.7) y para widgets de divulgación (ver Receta 8.3).

La función del botón es comunicar si el elemento controlado está expandido. Debes seguir tres reglas esenciales cuando lo apliques:

  • Estableces el atributo en el elemento que realiza el control (el botón), no en el elemento controlado (el grupo).

  • La mera presencia del atributo no es suficiente; debes establecerlo en "verdadero" o "falso".

  • El atributo debe estar presente y establecido antes de que el usuario interactúe con el botón. Si estableces el valor "false", significa que el elemento controlado está colapsado. Si no estableces el atributo, el botón no controla nada.

Como puedes ver en el Ejemplo 4-16, el botón tiene otro atributo ARIA: aria-controls.

La propiedad controles

El atributoaria-controls identifica el elemento que controla el botón. El valor es una lista de una o más referencias id. Con el atributo presente, un lector de pantalla puede identificar una relación entre un botón y otro elemento. JAWS no anuncia automáticamente esta relación, pero puedes utilizar el atajo de teclado JAWSKEY + ALT + M para saltar directamente al elemento controlado.

Para los widgets de divulgación, este atributo no está bien soportado (JAWS es el único lector de pantalla que lo utiliza), y no está claro cuánta importancia piensan darle los vendedores de lectores de pantalla. Sin embargo, no hace daño. En el caso de NVDA, se trata de una cuestión abierta desde 2018; un debate abierto en el repositorio GitHub de ARIA data de 2019. Utilizarlo o no depende de ti. Hasta que haya una recomendación oficial a favor o en contra, lo utilizaré en los siguientes capítulos.

Estado prensado

El atributoaria-pressed indica el estado "pulsado" actual de los botones basculantes.

Un botón de conmutación es similar a una casilla de verificación, pero no exactamente igual. Aparte del estilo, la diferencia más significativa es que una casilla de verificación sólo transmite un estado (marcada/desmarcada/mixta), mientras que un botón realiza una acción. Cuando los usuarios pulsan un botón de conmutación, esperan que ocurra algo. Al pulsar el botón del Ejemplo 4-17 se alterna el valor del atributo aria-pressed y cambia el estado del botón Añadir a favoritos. Los cambios del lado del cliente como ése requieren que trabajes en un entorno que dependa de JavaScript, porque el estado pulsado debe cambiar al hacer clic. Si ese no es el caso y quieres mejorar el control progresivamente, utiliza en su lugar una casilla de verificación. Adrian Roselli describe en profundidad las diferencias entre las casillas de verificación y los botones basculantes en "Basculantes con poca ingeniería " y "Basculantes con poca ingeniería también".

Estado comprobado

El atributoaria-checked indica el estado "marcado" actual de las casillas de verificación, botones de radio y otros widgets.

No debes utilizar aria-checked en un botón con el rol button, pero puedes utilizarlo en un interruptor (véase el Ejemplo 4-18). Un conmutador es un tipo de casilla de verificación que representa valores de activado/desactivado en lugar de valores de marcado/desmarcado/mixto. Proporciona aproximadamente la misma funcionalidad que una casilla de verificación o un botón de conmutación, pero puedes distinguirlos para los lectores de pantalla de forma coherente con su aspecto en pantalla. Los conmutadores son un patrón problemático en términos de experiencia de usuario. Hablo un poco de por qué en la Receta 9.1. Si decides utilizar conmutadores, pruébalos a fondo con los usuarios, incluidos los que utilizan lectores de pantalla.

propiedad haspopup

El atributoaria-haspopup indica que un botón controla otro elemento emergente interactivo. La mayoría de los lectores de pantalla también anuncian el tipo de elemento emergente . El atributo admite siete valores: true, false, menu, dialog, grid, listbox y tree, que indican que el elemento referenciado tiene la función respectiva. true es lo mismo que menu.

Dependiendo del software lector de pantalla que utilices, si enfocas el botón del Ejemplo 4-19, anunciará algo como "Configuración, botón, menú" (JAWS) o "Configuración, botón emergente, menú emergente" (VoiceOver). El valor que utilices debe cumplir su promesa: si el valor es menú, el papel del elemento controlado también debe ser menú y funcionar en consecuencia. JAWS, por ejemplo, también anunciará las instrucciones adecuadas cuando esté presente un atributo con un valor específico. Para verdadero y menú, anuncia: "Pulsa Espacio para activar el menú. Luego navega con las teclas de flecha". Para listbox, árbol y cuadrícula"Para activarlo, pulsa Intro".

Los valores verdadero y menú están bien soportados en todos los lectores de pantalla. Sin embargo, TalkBack y Narrator no admiten rejilla, diálogo, cuadro de lista ni árbol.

4.5 No desactives los botones

Problema

Desactivar los botones de puede causar más problemas a los usuarios que beneficios.

Falta información

Cuando haces clic en un botón desactivado, no ocurre nada. El botón no explica qué va mal ni te ayuda a solucionar el problema. No proporciona ninguna respuesta útil. Si el usuario cree que sus respuestas son correctas, no proporcionar información puede hacer que la interfaz de usuario parezca rota.

Falta de enfoque

Los botones deshabilitados de no son enfocables, por lo que los usuarios de lectores de pantalla que utilicen la tecla Tab para navegar podrían no saber siquiera que hay un botón. Si el estilo del botón no es obvio, puede confundir a los usuarios de teclado que intenten enfocar el botón.

Contraste bajo

La regla de contraste mínimo de las WCAG no se aplica a los controles para discapacitados, pero a menudo son difíciles de leer, sobre todo para las personas con baja visión.

Engaño

No siempre es evidente que los botones están desactivados. Algunos usuarios intentarán hacer clic en ellos; si no ocurre nada, pueden sentirse irritados, confusos o decepcionados. Esto puede ocurrir porque el diseño no es nítido o porque los botones desactivados suelen contener palabras de llamada a la acción como "enviar" o "pedir", que atraen a los usuarios para que hagan clic en ellos.

Solución

No desactives los botones. Los usuarios siempre deben poder interactuar con ellos y obtener retroalimentación.

Debate

El objetivo de desactivar los botones es evitar los clics prematuros y dificultar que los usuarios cometan errores al rellenar los formularios. Los desarrolladores utilizan esta técnica para indicar que algo importante está mal o falta, y debe arreglarse antes de que el usuario pueda continuar con el siguiente paso. Suena bien, pero un botón desactivado no es la mejor solución. Es un patrón defectuoso. Un botón puede desactivarse por muchas razones, pero su uso obliga al usuario a averiguar qué ha ido mal.

En lugar de desactivar los botones, hay varias medidas que puedes tomar para evitar errores por adelantado:

  • Utiliza etiquetas claras para tus campos de entrada.

  • Añade pistas y descripciones cuando la etiqueta por sí sola no sea suficientemente clara.

  • Divide los formularios complejos en varios pasos o páginas para reducir la carga cognitiva.

  • Activa siempre los botones y valida la entrada al enviar.

  • Dar mensajes de error claros.

Cuando el usuario pulse el botón, proporciónale una lista de errores que apunten al campo correspondiente, o desplaza el foco al campo erróneo si sólo hay uno (consulta la Receta 9.4 para más detalles en ).

Get Recetario de accesibilidad web now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.