Apuntes DAM
Volver al inicio

Página de un evento accesible: HTML semántico y ARIA

Ejercicio de HTML y CSSMedioUnos 60 minutos

Corrige las barreras de accesibilidad de la página de unas jornadas: header, nav y main, enlace para saltar al contenido, encabezados, textos alternativos (y alt vacío para lo decorativo), una tabla bien marcada, enlaces que dicen adónde llevan y un menú desplegable con aria-expanded.

  • HTML semántico (landmarks)
  • Jerarquía de encabezados
  • Texto alternativo
  • Tablas de datos: caption y th
  • Enlaces descriptivos
  • aria-expanded y teclado

Enunciado

El instituto organiza unas jornadas de software libre y alguien hizo la página a toda prisa con div y span. Se ve bien, pero una persona que navega con teclado o con un lector de pantalla no puede usarla: el menú no se puede enfocar, no hay encabezados para saltar de sección en sección, las imágenes no tienen texto alternativo y la tabla del programa es una cuadrícula sin sentido.

Las pautas WCAG 2.2 (nivel AA, el que exige la ley a las webs públicas) piden que todo funcione con teclado, que la estructura se pueda leer sin verla y que los controles anuncien su nombre y su estado. Casi todo se consigue con el elemento HTML adecuado; ARIA solo hace falta para lo que el HTML no tiene, como el estado abierto o cerrado de un menú.

Qué hay que hacer

  1. Cada apartado se corrige por separado con pruebas que se ejecutan dentro de la página (algunas a varios anchos de pantalla). Debajo del editor tienes el resultado en directo.
  2. Conserva las clases y los textos visibles de la página: las pruebas buscan los elementos por ellos.

Guía paso a paso

Intenta resolverlo por tu cuenta y abre un paso solo cuando te atasques: cada uno te acerca a la solución sin dártela entera.

1. El elemento correcto antes que ARIA

Un button ya se enfoca, se activa con Intro y Espacio y se anuncia como botón; un div con role="button" necesita todo eso a mano. La primera regla de ARIA es no usarlo si existe un elemento HTML que hace lo mismo.

2. Estructura que se puede recorrer

Los lectores de pantalla permiten saltar de región en región (header, nav, main) y de encabezado en encabezado. Por eso el h1 es único y los niveles no se saltan, y por eso el enlace «Saltar al contenido» lleva directo al main.

3. Alternativas de verdad

El alt es lo que se lee en lugar de la imagen: tiene que contener la información que da (las fechas del cartel), no describir el dibujo. Si la imagen no aporta nada, alt="".

4. El estado de un control

Un menú desplegable tiene un estado (abierto o cerrado) que no se ve si no se mira. aria-expanded lo anuncia: el JavaScript tiene que mantenerlo al día cada vez que cambia.

css
boton.setAttribute("aria-expanded", String(abierto));
menu.hidden = !abierto;
5. Prueba con el teclado

Recorre la página solo con Tab: ¿se ve dónde está el foco?, ¿llegas al menú?, ¿Escape cierra lo que se abre? Si con el teclado funciona, con un lector de pantalla casi seguro también.

Resuélvelo aquí

Cada apartado edita un fichero de la página (el HTML, el CSS o el JavaScript) y se corrige con pruebas que se ejecutan en la propia página, también a distintos anchos de pantalla. Debajo del editor ves el resultado en directo.

HTMLApartado 1 · HTMLMedio

Rehaz el HTML de la página: un enlace «Saltar al contenido» (clase saltar) como primer elemento, que lleve al main (con id principal); la cabecera en un header, el menú en un nav con aria-label="Principal" y sus opciones como enlaces (#programa y #entradas); el contenido en main; el título como h1 y «Programa» como h2 con id programa; el logo con el alt «Jornadas de Software Libre», el cartel con un alt que describa su información (fechas y lugar) y el separador, que es decorativo, con alt=""; la tabla con un caption y su primera fila con th scope="col"; y el enlace al PDF con un texto que diga qué es y que es un PDF (sin «aquí»).

index.html
Vista previa
JS · DOMApartado 2 · JavaScriptMedio

En el móvil, el menú se despliega con un botón. El HTML ya es correcto (un button con aria-expanded y aria-controls, y el nav con hidden); escribe el JavaScript: al pulsar el botón, el menú se muestra u oculta (atributo hidden) y aria-expanded pasa a "true" o "false"; con el menú abierto, la tecla Escape lo cierra y devuelve el foco al botón.

index.htmlsolo lectura
script.js
Vista previa

Solución explicada

Ver las soluciones de todos los apartados

Apartado 1 · HTML

markup
<a class="saltar" href="#principal">Saltar al contenido</a>
<header class="cabecera">
  <img src="logo.svg" class="logo" alt="Jornadas de Software Libre">
  <nav class="menu" aria-label="Principal">
    <a href="#programa">Programa</a>
    <a href="#entradas">Entradas</a>
  </nav>
</header>
<main id="principal" class="contenido">
  <h1 class="titulo">Jornadas de Software Libre 2026</h1>
  <img src="cartel.jpg" alt="Cartel: 14 y 15 de noviembre en el salón de actos del instituto">
  <img src="separador.png" alt="">
  <h2 class="subtitulo" id="programa">Programa</h2>
  <table>
    <caption>Charlas del sábado</caption>
    <tr><th scope="col">Hora</th><th scope="col">Charla</th><th scope="col">Sala</th></tr>
    <tr><td>10:00</td><td>Linux en el aula</td><td>A</td></tr>
    <tr><td>11:30</td><td>Accesibilidad web</td><td>B</td></tr>
  </table>
  <a href="programa.pdf">Descarga el programa completo (PDF, 120 KB)</a>
</main>

Apartado 2 · JavaScript

javascript
const boton = document.querySelector(".abrir-menu");
const menu = document.getElementById(boton.getAttribute("aria-controls"));

function abrir(abierto) {
  boton.setAttribute("aria-expanded", String(abierto));
  menu.hidden = !abierto;
}

boton.addEventListener("click", () => abrir(boton.getAttribute("aria-expanded") !== "true"));

// Escape cierra el menú y devuelve el foco al botón
document.addEventListener("keydown", (e) => {
  if (e.key === "Escape" && boton.getAttribute("aria-expanded") === "true") {
    abrir(false);
    boton.focus();
  }
});

Casi todas las barreras se arreglan cambiando div y span por el elemento que describe su función: header, nav, main, h1, a, button, th. Los navegadores y los lectores de pantalla ya saben qué hacer con ellos.

Los textos alternativos y los textos de los enlaces son contenido: deben tener sentido fuera de contexto, porque los lectores de pantalla permiten listar todos los enlaces de una página. «Pincha aquí» repetido diez veces no dice nada.

El menú desplegable usa ARIA solo para el estado (aria-expanded) y la relación (aria-controls); el resto lo da el button. Cerrar con Escape y devolver el foco al botón evita que el usuario de teclado se quede perdido.

Las pruebas de este ejercicio comprueban lo mismo que herramientas automáticas como axe o Lighthouse (estructura, alternativas, nombres de controles), pero ninguna herramienta sustituye a probar la página con teclado y con un lector de pantalla.

Para ir más allá

  • Haz que el enlace «Saltar al contenido» solo se vea cuando recibe el foco (sin ocultarlo con display: none).
  • Haz que el menú también se cierre al hacer clic fuera de él.
  • Pasa la página por axe DevTools y por el lector de pantalla NVDA o VoiceOver y anota qué se lee.

Dónde se explica