🇺🇸 EN
·5 min read·↗·🇺🇸 EN

Cómo hacer tus proyectos de Angular más accesibles

Portada principal del artículo: Cómo hacer tus proyectos de Angular más accesibles

Cuando construimos aplicaciones, solemos enfocarnos en la entrega antes que en cubrir otros aspectos como la accesibilidad y el testing (aunque del testing hablaré en otro post). Hoy quiero hablar sobre accesibilidad. La mayoría de las veces pensamos que la accesibilidad es solo para ayudar a personas con discapacidad a usar nuestro producto, pero en realidad mejora la experiencia para todos los usuarios.

En diciembre estuve dedicando tiempo a aprender sobre accesibilidad, y te recomiendo mucho tomar estos cursos gratuitos:

Este fin de semana me tomé el tiempo de poner a prueba lo que aprendí construyendo un pequeño formulario que incluye accesibilidad desde el inicio, abarcando la configuración del formulario y su validación. ¡Vamos a ello!

Quería crear el formulario "Carta a Santa" donde mi hijo pudiera enviar su nombre, correo electrónico y un mensaje a Santa Claus, pero quería construir un formulario accesible, con validaciones claras y comprensibles, y una notificación cuando el mensaje se haya enviado con éxito.

Al final, logré un formulario como este:


El objetivo principal de un formulario es recopilar información de los usuarios. Si los formularios no son accesibles, excluimos a una parte importante de las personas, como usuarios con dificultades visuales o motoras, personas afectadas por un accidente temporal o con las manos ocupadas.

Comencé mejorando la accesibilidad usando elementos semánticos de HTML como <header>, <main> y <footer>, y organizando el contenido de forma jerárquica con encabezados (<h1> a <h6>). Esto ayuda a que los lectores de pantalla naveguen la página correctamente y también mejora el SEO.

<header>
  <h1>Write Your Letter to Santa</h1>
</header>
<main>
  <h2>Fill Out the Form</h2>
</main>
<footer>
</footer>

El objetivo principal de un formulario es recopilar información de los usuarios. Si los formularios no son accesibles, excluimos a una parte importante de las personas, como usuarios con dificultades visuales o motoras, personas afectadas por un accidente temporal o con las manos ocupadas.

Comencé mejorando la accesibilidad usando elementos semánticos de HTML como <header>, <main> y <footer>, y organizando el contenido de forma jerárquica con encabezados (<h1> a <h6>). Esto ayuda a que los lectores de pantalla naveguen la página correctamente y también mejora el SEO.

<label for="name">Name</label>
<input id="name" name="name" required type="text" />

Pero los formularios son más que solo inputs y labels; necesitan validaciones o mensajes de error que solo aparezcan cuando sean relevantes, como después de haber interactuado con un campo (touched) o cuando el formulario se envía. Pero, ¿cómo le notifico al usuario con un mensaje?

Primero, las notificaciones deben ser anunciadas por lectores de pantalla usando aria-live, con un diseño visualmente claro para quienes no usan tecnologías de asistencia y evitando interrumpir al usuario innecesariamente.

Aprende más sobre aria-live y roles

Existen diferentes roles ARIA para usar en las notificaciones según escenarios específicos: usa role="alert" para mensajes críticos o de alta prioridad, como errores (interrumpe al usuario automáticamente y anuncia el contenido de inmediato), o role="status" para actualizaciones no críticas, como confirmaciones o cambios de estado.

    <div class="error" role="alert">
      <p>Name is required.</p>
    </div>

Pero, ¿cómo se anuncian esos mensajes? Tienes assertive, que interrumpe el mensaje actual, o polite, que espera a ser anunciado por el lector de pantalla sin interrumpir.

@if ((nameCtrl.invalid && nameCtrl.touched) || (nameCtrl.invalid && isSubmitted)) {
  <div class="error" role="alert">
    <small>Name is required.</small>
  </div>
}

Por ejemplo, aria-live="polite" espera para anunciar el contenido hasta que termine la tarea actual. Es perfecto para notificaciones como cuando un mensaje ha sido enviado:

    @if (messageSent) {
      <div class="notifications" role="status" aria-live="polite">
        <p>{{ message }}</p>
      </div>
    }

Pero, ¿qué pasa cuando quiero contenido solo para lectores de pantalla? Podemos crear una clase de utilidad como .visually-hidden para el contenido que solo deba ser accesible para lectores de pantalla y no visible para usuarios videntes.

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

Esto ayuda a asegurar que la información esencial se transmita a los usuarios que dependen de lectores de pantalla sin afectar la interfaz visual.

<span class="visually-hidden">Error:</span>
<p>Name is required.</p>

Otro punto clave es el color. Con un contraste adecuado, el texto debe tener suficiente contraste respecto a su fondo, y la forma más fácil de comprobarlo es usando la extensión color-contrast-checker.

input.ng-invalid.ng-touched {
  border-color: #e74c3c; 
  background-color: #fdecea; 
}

¡Perfecto, ya podemos tener nuestro formulario listo con accesibilidad! Pero espera un segundo: ¿qué pasa si mañana @Jörgen de Groot agrega una nueva funcionalidad? ¿Cómo puedo controlar que no rompa la accesibilidad?

Aquí ESLint es tu amigo. Primero, agrégalo usando los schematics:

ng add @angular-eslint/schematics

ESLint ofrece un conjunto de reglas de accesibilidad como accessibility-label-has-associated-control, que asegura que cada elemento label tenga un control de formulario asociado (similar a accessibility-label-for, pero más estricto).

<!-- Incorrecto -->
<label>Email</label>
 
<!-- Correcto -->
<label for="email">Email</label>
<input id="email" type="email">

O accessibility-table-scope, que requiere que los encabezados de tablas de datos (<th>) tengan un atributo scope, mejorando la navegación para lectores de pantalla.

<!-- Incorrecto -->
<th>Name</th>
 
<!-- Correcto -->
<th scope="col">Name</th>

Te animo a leer más sobre las reglas de accesibilidad en ESLint y agregar estas reglas a tu archivo (.eslintrc.json), ajustando la severidad ("warn", "error", etc.) según lo que necesites:

{
  "overrides": [
    {
      "files": ["*.component.html"],
      "extends": ["plugin:@angular-eslint/template/recommended"],
      "rules": {
         "@angular-eslint/template/accessibility-elements-content": "error",
        "@angular-eslint/template/accessibility-valid-aria": "error",
        "@angular-eslint/template/accessibility-label-has-associated-control": "warn",
        "@angular-eslint/template/accessibility-table-scope": "error",
        "@angular-eslint/template/accessibility-role-supports-aria": "warn",
        "@angular-eslint/template/accessibility-click-events-have-key-events": "warn",
        "@angular-eslint/template/accessibility-no-positive-tabindex": "error",
        "@angular-eslint/template/accessibility-media-has-caption": "warn",
        "@angular-eslint/template/accessibility-valid-tabindex": "error",
        "@angular-eslint/template/accessibility-input-label": "warn"
        "@angular-eslint/template/accessibility-role-has-required-aria": "error"
      }
    }
  ]
}

Después de ejecutar npm run lint, ¡tachán! Ya tenemos un linter cuidando nuestro código.

Resumen

Espero que cuando comiences tu próximo proyecto tengas en mente estos consejos para simplificar la accesibilidad y preocuparte por mejorar nuestras aplicaciones para todos los usuarios.

Foto de Alphabag en Unsplash

Artículos Relacionados

Compartir este artículo

Si esta guía te resultó útil, compártela con tu equipo o con otros desarrolladores.


Software real. Lecciones reales.

Comparto las lecciones que aprendí por las malas, para que puedas evitarlas o estar preparado cuando ocurran.

User avatar
User avatar
User avatar
User avatar
+13K

Únete a 13,800+ desarrolladores y lectores.

Cero spam. Cancela tu suscripción en cualquier momento.