Saltar al contenido

Accesibilidad

La accesibilidad web (también conocida como a11y) se refiere a la práctica de crear sitios web que puedan ser utilizados por cualquier persona, ya sea una persona con una discapacidad, una conexión lenta, hardware obsoleto o defectuoso o simplemente alguien en un entorno desfavorable. Por ejemplo, añadir subtítulos a un video ayudaría tanto a los usuarios sordos o con problemas de audición como a los usuarios que se encuentran en un ambiente ruidoso y no pueden escuchar su teléfono. De manera similar, asegurarse de que el texto no tenga un contraste demasiado bajo ayudará tanto a los usuarios con baja visión como a los usuarios que intentan usar su teléfono bajo la luz brillante del sol.

¿Listo para empezar pero no sabes por dónde?

Consulta la guía de planificación y gestión de la accesibilidad web proporcionada por el World Wide Web Consortium (W3C).

Debes añadir un enlace en la parte superior de cada página que vaya directamente al área de contenido principal para que los usuarios puedan omitir el contenido que se repite en varias páginas web.

Normalmente, esto se hace en la parte superior de App.vue, ya que será el primer elemento enfocable en todas tus páginas:

template
<span ref="backToTop" tabindex="-1" />
<ul class="skip-links">
  <li>
    <a href="#main" ref="skipLink" class="skip-link">Skip to main content</a>
  </li>
</ul>

Para ocultar el enlace a menos que esté enfocado, puedes añadir el siguiente estilo:

css
.skip-links {
  list-style: none;
}
.skip-link {
  white-space: nowrap;
  margin: 1em auto;
  top: 0;
  position: fixed;
  left: 50%;
  margin-left: -72px;
  opacity: 0;
}
.skip-link:focus {
  opacity: 1;
  background-color: white;
  padding: 0.5em;
  border: 1px solid black;
}

Una vez que un usuario cambia de ruta, devuelve el enfoque al principio de la página, justo antes del enlace para omitir. Esto se puede lograr llamando al método focus en la ref de template backToTop (asumiendo el uso de vue-router):

vue
<script>
export default {
  watch: {
    $route() {
      this.$refs.backToTop.focus()
    }
  }
}
</script>
vue
<script setup>
import { ref, watch } from 'vue'
import { useRoute } from 'vue-router'

const route = useRoute()
const backToTop = ref()

watch(
  () => route.path,
  () => {
    backToTop.value.focus()
  }
)
</script>

Leer documentación sobre enlaces para omitir al contenido principal

Estructura del Contenido

Uno de los aspectos más importantes de la accesibilidad es asegurarse de que el diseño pueda soportar una implementación accesible. El diseño debe considerar no solo el contraste de color, la selección de fuentes, el tamaño del texto y el idioma, sino también cómo se estructura el contenido en la aplicación.

Encabezados

Los usuarios pueden navegar por una aplicación a través de los encabezados. Tener encabezados descriptivos para cada sección de tu aplicación facilita a los usuarios predecir el contenido de cada sección. En cuanto a los encabezados, hay un par de prácticas de accesibilidad recomendadas:

  • Anidar los encabezados en su orden de clasificación: <h1> - <h6>
  • No saltar encabezados dentro de una sección
  • Usar etiquetas de encabezado reales en lugar de dar estilo al texto para dar la apariencia visual de encabezados

Leer más sobre encabezados

template
<main role="main" aria-labelledby="main-title">
  <h1 id="main-title">Main title</h1>
  <section aria-labelledby="section-title-1">
    <h2 id="section-title-1"> Section Title </h2>
    <h3>Section Subtitle</h3>
    <!-- Content -->
  </section>
  <section aria-labelledby="section-title-2">
    <h2 id="section-title-2"> Section Title </h2>
    <h3>Section Subtitle</h3>
    <!-- Content -->
    <h3>Section Subtitle</h3>
    <!-- Content -->
  </section>
</main>

Puntos de referencia

Los puntos de referencia (landmarks) proporcionan acceso programático a secciones dentro de una aplicación. Los usuarios que dependen de tecnología de asistencia pueden navegar a cada sección de la aplicación y omitir contenido. Puedes usar roles ARIA para ayudarte a lograr esto.

HTMLRol ARIAPropósito del Punto de Referencia
headerrole="banner"Encabezado principal: título de la página
navrole="navigation"Colección de enlaces adecuados para usar al navegar por el documento o documentos relacionados
mainrole="main"El contenido principal o central del documento.
footerrole="contentinfo"Información sobre el documento padre: notas al pie/derechos de autor/enlaces a la declaración de privacidad
asiderole="complementary"Soporta el contenido principal, pero está separado y tiene significado por sí mismo.
searchrole="search"Esta sección contiene la funcionalidad de búsqueda para la aplicación
formrole="form"Colección de elementos asociados a formularios
sectionrole="region"Contenido relevante al que los usuarios probablemente querrán navegar. Se debe proporcionar una etiqueta para este elemento

Leer más sobre puntos de referencia

Formularios Semánticos

Al crear un formulario, puedes usar los siguientes elementos: <form>, <label>, <input>, <textarea> y <button>.

Las etiquetas suelen colocarse encima o a la izquierda de los campos del formulario:

template
<form action="/dataCollectionLocation" method="post" autocomplete="on">
  <div v-for="item in formItems" :key="item.id" class="form-item">
    <label :for="item.id">{{ item.label }}: </label>
    <input
      :type="item.type"
      :id="item.id"
      :name="item.id"
      v-model="item.value"
    />
  </div>
  <button type="submit">Submit</button>
</form>

Observa cómo puedes incluir autocomplete='on' en el elemento form y se aplicará a todas las entradas de tu formulario. También puedes establecer diferentes valores para el atributo autocomplete para cada entrada.

Etiquetas

Proporciona etiquetas para describir el propósito de todos los controles de formulario; vinculando for con id:

template
<label for="name">Name: </label>
<input type="text" name="name" id="name" v-model="name" />

Si inspeccionas este elemento en tus Herramientas de desarrollo de Chrome (Chrome DevTools) y abres la pestaña de Accesibilidad dentro de la pestaña de Elementos, verás cómo la entrada obtiene su nombre de la etiqueta:

Herramientas de desarrollo de Chrome mostrando el nombre accesible de la entrada desde la etiqueta

Advertencia:

Aunque es posible que hayas visto etiquetas envolviendo los campos de entrada así:

template
<label>
  Name:
  <input type="text" name="name" id="name" v-model="name" />
</label>

Establecer explícitamente las etiquetas con un id coincidente está mejor soportado por la tecnología de asistencia.

aria-label

También puedes dar a la entrada un nombre accesible con aria-label.

template
<label for="name">Name: </label>
<input
  type="text"
  name="name"
  id="name"
  v-model="name"
  :aria-label="nameLabel"
/>

No dudes en inspeccionar este elemento en las Herramientas de desarrollo de Chrome para ver cómo ha cambiado el nombre accesible:

Herramientas de desarrollo de Chrome mostrando el nombre accesible de la entrada desde aria-label

aria-labelledby

Usar aria-labelledby es similar a aria-label excepto que se usa si el texto de la etiqueta es visible en pantalla. Se empareja con otros elementos por su id y puedes vincular múltiples ids:

template
<form
  class="demo"
  action="/dataCollectionLocation"
  method="post"
  autocomplete="on"
>
  <h1 id="billing">Billing</h1>
  <div class="form-item">
    <label for="name">Name: </label>
    <input
      type="text"
      name="name"
      id="name"
      v-model="name"
      aria-labelledby="billing name"
    />
  </div>
  <button type="submit">Submit</button>
</form>

Herramientas de desarrollo de Chrome mostrando el nombre accesible de la entrada desde aria-labelledby

aria-describedby

aria-describedby](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-describedby) se usa de la misma manera que aria-labelledby, excepto que proporciona una descripción con información adicional que el usuario podría necesitar. Esto se puede usar para describir los criterios de cualquier entrada:

template
<form
  class="demo"
  action="/dataCollectionLocation"
  method="post"
  autocomplete="on"
>
  <h1 id="billing">Billing</h1>
  <div class="form-item">
    <label for="name">Full Name: </label>
    <input
      type="text"
      name="name"
      id="name"
      v-model="name"
      aria-labelledby="billing name"
      aria-describedby="nameDescription"
    />
    <p id="nameDescription">Please provide first and last name.</p>
  </div>
  <button type="submit">Submit</button>
</form>

Puedes ver la descripción inspeccionando las Herramientas de desarrollo de Chrome:

Herramientas de desarrollo de Chrome mostrando el nombre accesible de la entrada desde aria-labelledby y la descripción con aria-describedby

Marcador de posición

Evita usar marcadores de posición, ya que pueden confundir a muchos usuarios.

Uno de los problemas con los marcadores de posición es que no cumplen con los criterios de contraste de color por defecto; al corregir el contraste de color, el marcador de posición parece datos precargados en los campos de entrada. Observando el siguiente ejemplo, puedes ver que el marcador de posición "Last Name" (Apellido), que cumple los criterios de contraste de color, parece datos precargados:

Marcador de posición accesible

template
<form
  class="demo"
  action="/dataCollectionLocation"
  method="post"
  autocomplete="on"
>
  <div v-for="item in formItems" :key="item.id" class="form-item">
    <label :for="item.id">{{ item.label }}: </label>
    <input
      type="text"
      :id="item.id"
      :name="item.id"
      v-model="item.value"
      :placeholder="item.placeholder"
    />
  </div>
  <button type="submit">Submit</button>
</form>
css
/* https://www.w3schools.com/howto/howto_css_placeholder.asp */

#lastName::placeholder {
  /* Chrome, Firefox, Opera, Safari 10.1+ */
  color: black;
  opacity: 1; /* Firefox */
}

#lastName:-ms-input-placeholder {
  /* Internet Explorer 10-11 */
  color: black;
}

#lastName::-ms-input-placeholder {
  /* Microsoft Edge */
  color: black;
}

Es mejor proporcionar toda la información que el usuario necesita para rellenar los formularios fuera de cualquier entrada.

Instrucciones

Al añadir instrucciones para tus campos de entrada, asegúrate de vincularlas correctamente a la entrada. Puedes proporcionar instrucciones adicionales y vincular múltiples ids dentro de un aria-labelledby. Esto permite un diseño más flexible.

template
<fieldset>
  <legend>Using aria-labelledby</legend>
  <label id="date-label" for="date">Current Date: </label>
  <input
    type="date"
    name="date"
    id="date"
    aria-labelledby="date-label date-instructions"
  />
  <p id="date-instructions">MM/DD/YYYY</p>
</fieldset>

Alternativamente, puedes adjuntar las instrucciones a la entrada con aria-describedby:

template
<fieldset>
  <legend>Using aria-describedby</legend>
  <label id="dob" for="dob">Date of Birth: </label>
  <input type="date" name="dob" id="dob" aria-describedby="dob-instructions" />
  <p id="dob-instructions">MM/DD/YYYY</p>
</fieldset>

Ocultar Contenido

Normalmente no se recomienda ocultar visualmente las etiquetas, incluso si la entrada tiene un nombre accesible. Sin embargo, si la funcionalidad de la entrada se puede entender con el contenido circundante, entonces podemos ocultar la etiqueta visual.

Veamos este campo de búsqueda:

template
<form role="search">
  <label for="search" class="hidden-visually">Search: </label>
  <input type="text" name="search" id="search" v-model="search" />
  <button type="submit">Search</button>
</form>

Podemos hacer esto porque el botón de búsqueda ayudará a los usuarios visuales a identificar el propósito del campo de entrada.

Podemos usar CSS para ocultar visualmente elementos pero mantenerlos disponibles para la tecnología de asistencia:

css
.hidden-visually {
  position: absolute;
  overflow: hidden;
  white-space: nowrap;
  margin: 0;
  padding: 0;
  height: 1px;
  width: 1px;
  clip: rect(0 0 0 0);
  clip-path: inset(100%);
}

aria-hidden="true"

Añadir aria-hidden="true" ocultará el elemento de la tecnología de asistencia pero lo dejará visualmente disponible para otros usuarios. No lo uses en elementos enfocables, solo en contenido decorativo, duplicado o fuera de pantalla.

template
<p>This is not hidden from screen readers.</p>
<p aria-hidden="true">This is hidden from screen readers.</p>

Botones

Al usar botones dentro de un formulario, debes establecer el type para evitar el envío del formulario. También puedes usar un input para crear botones:

template
<form action="/dataCollectionLocation" method="post" autocomplete="on">
  <!-- Buttons -->
  <button type="button">Cancel</button>
  <button type="submit">Submit</button>

  <!-- Input buttons -->
  <input type="button" value="Cancel" />
  <input type="submit" value="Submit" />
</form>

Imágenes Funcionales

Puedes usar esta técnica para crear imágenes funcionales.

  • Campos de entrada

    • Estas imágenes actuarán como un botón de tipo submit en formularios
    template
    <form role="search">
      <label for="search" class="hidden-visually">Search: </label>
      <input type="text" name="search" id="search" v-model="search" />
      <input
        type="image"
        class="btnImg"
        src="https://img.icons8.com/search"
        alt="Search"
      />
    </form>
  • Iconos

template
<form role="search">
  <label for="searchIcon" class="hidden-visually">Search: </label>
  <input type="text" name="searchIcon" id="searchIcon" v-model="searchIcon" />
  <button type="submit">
    <i class="fas fa-search" aria-hidden="true"></i>
    <span class="hidden-visually">Search</span>
  </button>
</form>

Estándares

La Iniciativa de Accesibilidad Web (WAI) del World Wide Web Consortium (W3C) desarrolla estándares de accesibilidad web para los diferentes componentes:

Guías de Accesibilidad al Contenido Web (WCAG)

WCAG 2.1 extiende a WCAG 2.0 y permite la implementación de nuevas tecnologías abordando los cambios en la web. El W3C anima a utilizar la versión más reciente de WCAG al desarrollar o actualizar políticas de accesibilidad web.

Cuatro Principios Rectores Principales de WCAG 2.1 (abreviado como POUR):

  • Perceptible
    • Los usuarios deben poder percibir la información que se presenta
  • Operable
    • Las formas de la interfaz, los controles y la navegación son operables
  • Comprensible
    • La información y el funcionamiento de la interfaz de usuario deben ser comprensibles para todos los usuarios
  • Robusto
    • Los usuarios deben poder acceder al contenido a medida que avanzan las tecnologías

Iniciativa de Accesibilidad Web – Aplicaciones de Internet Ricas Accesibles (WAI-ARIA)

WAI-ARIA del W3C proporciona orientación sobre cómo construir contenido dinámico y controles de interfaz de usuario avanzados.

Recursos

Documentación

Tecnologías de Asistencia

Pruebas

Usuarios

La Organización Mundial de la Salud estima que el 15% de la población mundial tiene alguna forma de discapacidad, y entre el 2% y el 4% de ellos son severas. Esto representa un estimado de 1 billón de personas en todo el mundo, lo que convierte a las personas con discapacidad en el grupo minoritario más grande del mundo.

Existe una amplia gama de discapacidades, que pueden dividirse aproximadamente en cuatro categorías:

  • Visual - Estos usuarios pueden beneficiarse del uso de lectores de pantalla, aumento de pantalla, control del contraste de la pantalla o pantallas braille.
  • Auditiva - Estos usuarios pueden beneficiarse de subtítulos, transcripciones o videos en lenguaje de señas.
  • Motora - Estos usuarios pueden beneficiarse de una gama de tecnologías de asistencia para discapacidades motoras: software de reconocimiento de voz, seguimiento ocular, acceso con un solo interruptor, puntero de cabeza, interruptor de sorber y soplar, ratón trackball de gran tamaño, teclado adaptado u otras tecnologías de asistencia.
  • Cognitiva - Estos usuarios pueden beneficiarse de medios complementarios, organización estructural del contenido, escritura clara y sencilla.

Consulta los siguientes enlaces de WebAim para comprender a los usuarios:

Accesibilidad