Criterios de éxito de las WCAG · Level A

WCAG 4.1.1: Análisis sintáctico (Obsoleto en WCAG 2.2)

WCAG 4.1.1 Análisis sintáctico requiere que el contenido web esté libre de errores importantes de HTML/XML, como IDs duplicados, que podrían hacer que las tecnologías de asistencia interpreten mal la página o no puedan procesarla. Aunque está obsoleta en WCAG 2.2, las reglas subyacentes de axe-core siguen activas y las infracciones siguen indicando un riesgo real de accesibilidad.

Qué Significa Esta Regla

WCAG 4.1.1 Parsing se diseñó originalmente para garantizar que los agentes de usuario, incluidos los navegadores y las tecnologías de asistencia, pudieran analizar e interpretar con precisión el contenido web. El criterio exigía que las páginas creadas en lenguajes de marcado como HTML o XML cumplieran cuatro condiciones estructurales: los elementos deben tener etiquetas de inicio y fin completas; los elementos deben estar anidados de acuerdo con sus especificaciones; los elementos no deben contener atributos duplicados; y cualquier ID utilizado en el contenido debe ser único.

En WCAG 2.2, el W3C declaró formalmente obsoleto este criterio. La razón fue que los navegadores modernos se han vuelto muy resistentes al HTML mal formado, corrigiendo automáticamente la mayoría de los errores estructurales antes de que lleguen al árbol de accesibilidad. Como resultado, muchas de las preocupaciones originales —como etiquetas sin cerrar o elementos mal anidados— ya no causan daños prácticos a las tecnologías de asistencia en entornos contemporáneos.

Sin embargo, que se haya declarado obsoleto no significa que las preocupaciones del criterio hayan desaparecido por completo. El W3C señala explícitamente que los atributos ID duplicados siguen siendo un problema de accesibilidad significativo. Cuando dos o más elementos comparten el mismo valor de id, los navegadores deben tomar una decisión arbitraria sobre qué elemento asociar con referencias ARIA, asociaciones de etiquetas o enlaces de fragmentos. Esta ambigüedad puede hacer que los lectores de pantalla anuncien contenido incorrecto, omitan controles interactivos o no expongan las etiquetas de los formularios en absoluto. La condición de aprobación del criterio se entiende hoy mejor como: no aparecen valores de ID duplicados en el DOM. Una página no cumple este criterio cuando los ID se duplican de formas que rompen las asociaciones programáticas de las que dependen las tecnologías de asistencia.

Las excepciones oficiales en la especificación WCAG son mínimas. El criterio se aplica al contenido creado en lenguajes de marcado; no se aplica al contenido generado por entornos de scripting donde la persona autora no tiene control directo sobre el formato de salida. En la práctica, sin embargo, las personas desarrolladoras son responsables del DOM final renderizado independientemente del stack tecnológico utilizado para producirlo.

Por Qué Importa

Los ID duplicados pueden parecer un problema menor de orden interno, pero sus consecuencias para las personas usuarias de tecnología de asistencia pueden ser graves. Lectores de pantalla como JAWS, NVDA y VoiceOver dependen del árbol de accesibilidad del navegador, que a su vez depende de referencias de ID correctamente resueltas para construir las relaciones entre los elementos de la interfaz. Cuando un ID está duplicado, el navegador normalmente resuelve las referencias solo al primer elemento coincidente en el orden del documento, ignorando silenciosamente los elementos posteriores con el mismo ID.

Para personas ciegas y con baja visión, esto puede significar que un campo de formulario se anuncie sin su etiqueta, o que un mensaje de error asociado a una entrada mediante aria-describedby nunca se lea en voz alta. Considere un formulario de pago en un sitio de comercio electrónico donde los campos de dirección de envío y los campos de dirección de facturación usan IDs como city, zip y state. Una persona usuaria de lector de pantalla que rellena la sección de facturación puede escuchar la etiqueta de la sección de envío en su lugar, lo que genera confusión, errores y un posible abandono de la transacción.

Para personas con discapacidades cognitivas, las asociaciones de etiquetas rotas significan que el texto visible que leen en pantalla no coincide con lo que anuncia su lector de pantalla o software de control por voz, creando una desconexión desorientadora que aumenta la carga cognitiva.

Para personas con discapacidad motriz que dependen de software de entrada por voz como Dragon NaturallySpeaking, los ID duplicados pueden hacer que los comandos de voz dirigidos a un control específico activen el elemento equivocado, porque el software puede depender internamente de la selección basada en ID.

Más allá del impacto en la discapacidad, los ID duplicados también afectan al SEO: los rastreadores de motores de búsqueda que dependen de identificadores de fragmentos para indexar secciones específicas de la página pueden indexar contenido incorrecto cuando los IDs no son únicos. La usabilidad también se degrada para todas las personas usuarias cuando los enlaces de anclaje dentro de la página navegan a la ubicación equivocada en la página.

Aproximadamente 2.2 billion people worldwide tienen algún tipo de discapacidad visual según la Organización Mundial de la Salud. Una proporción significativa de esas personas usuarias depende de lectores de pantalla que se ven directamente afectados por asociaciones de ID rotas. Garantizar IDs únicos es una de las correcciones de menor esfuerzo y mayor impacto que un equipo de desarrollo puede implementar.

Reglas Relacionadas de Axe-core

Tres reglas de axe-core se corresponden directamente con las preocupaciones planteadas por WCAG 4.1.1. Cada una se dirige a una manifestación específica del problema de ID duplicados:

  • duplicate-id: Esta regla comprueba todo el DOM en busca de cualquier valor de atributo id que aparezca en más de un elemento. Señala todos los elementos posteriores al primero que comparten un ID, independientemente de si esos elementos son interactivos o están referenciados por ARIA. Esta es la más amplia de las tres reglas y detecta infracciones estructurales incluso cuando no hay ninguna relación ARIA explícita involucrada. Un desencadenante común son los frameworks basados en componentes que renderizan el mismo componente reutilizable varias veces en una página sin generar IDs únicos para cada instancia.
  • duplicate-id-active: Esta regla restringe su enfoque a IDs duplicados en elementos que son interactivos o enfocables: botones, enlaces, entradas y cualquier elemento con un tabindex no negativo. El impacto en la accesibilidad es mayor aquí porque tanto las tecnologías de asistencia como la navegación por teclado dependen de la capacidad de identificar de forma inequívoca un control activo. Cuando un botón de envío y un icono no relacionado comparten el mismo ID, los anuncios del orden de tabulación y la gestión programática del foco pueden fallar.
  • duplicate-id-aria: Esta es la más crítica de las tres reglas. Señala IDs duplicados específicamente cuando esos IDs son referenciados por atributos ARIA —aria-labelledby, aria-describedby, aria-controls, aria-owns y atributos de relación similares—. Dado que estos atributos son el mecanismo principal mediante el cual las tecnologías de asistencia comprenden las relaciones entre elementos, los duplicados aquí rompen directamente el cálculo del nombre accesible y las relaciones de rol. Un ejemplo de fallo sería dos elementos <div> con id='dialog-title' cuando un modal usa aria-labelledby='dialog-title': el lector de pantalla anunciará el elemento que aparezca primero en el DOM, que puede no ser el encabezado de diálogo previsto.

Las herramientas automatizadas son muy adecuadas para detectar IDs duplicados porque la comprobación es puramente sintáctica: la herramienta lee el DOM y compara los valores de ID. No se requiere estrictamente ninguna prueba manual para este criterio. Sin embargo, si los IDs se generan dinámicamente después de la interacción de la persona usuaria —por ejemplo, un scroll infinito que inyecta nuevo contenido con IDs repetidos—, los análisis automatizados ejecutados al cargar la página pueden pasar por alto infracciones que solo aparecen más tarde. En tales casos, las personas evaluadoras deben activar el comportamiento dinámico antes de ejecutar los análisis, o supervisar el DOM utilizando las herramientas de desarrollo del navegador después de las interacciones.

Cómo Probar

  1. Análisis automatizado con axe DevTools: Abra la página en Chrome o Firefox. Abra DevTools (F12), vaya al panel de axe DevTools (o instale la extensión del navegador) y ejecute un análisis de página completa. Filtre los resultados por las reglas duplicate-id, duplicate-id-active y duplicate-id-aria. Cada infracción enumerará los elementos afectados y sus valores de ID duplicados. Exporte el informe si es necesario para la documentación de auditoría. Para Lighthouse, ejecute una auditoría de accesibilidad de Lighthouse desde la pestaña Lighthouse de DevTools y busque la auditoría "Document has multiple elements with the same id".
  2. Comprobación en la consola de las DevTools del navegador: Abra la consola del navegador y ejecute el siguiente fragmento de JavaScript para encontrar todos los IDs duplicados en la página actual: const ids = [...document.querySelectorAll('[id]')].map(el => el.id); const dupes = ids.filter((id, i) => ids.indexOf(id) !== i); console.log([...new Set(dupes)]); Esto imprimirá un array de todos los valores de ID que aparecen más de una vez. Un array vacío significa que no hay duplicados.
  3. Pruebas con lector de pantalla usando NVDA y Firefox: Cargue la página con NVDA en ejecución. Navegue hasta un formulario que contenga campos con etiquetas asociadas mediante for/id o aria-labelledby. Recorra cada campo con la tecla Tab y escuche con atención si NVDA anuncia la etiqueta correcta. Si un campo se anuncia sin etiqueta o con la etiqueta incorrecta de una sección diferente, la causa puede ser un ID duplicado. Repita este proceso para cualquier región de referencia ARIA, diálogos modales o widgets que usen aria-controls o aria-describedby.
  4. VoiceOver y Safari en macOS: Active VoiceOver (Command+F5). Use el rotor de VoiceOver (Control+Option+U) para abrir la lista de Controles de formulario o Enlaces y verifique que cada control tenga una etiqueta única y correctamente anunciada. Navegue dentro de cualquier diálogo modal y confirme que el título del diálogo se anuncia correctamente cuando el diálogo se abre.
  5. JAWS y Chrome: Con JAWS en ejecución, abra la página y use la lista de campos de formulario de JAWS (Insert+F5) para revisar todos los elementos de formulario y sus etiquetas asociadas. Verifique que no haya dos campos que compartan el mismo texto de etiqueta cuando deberían ser distintos.
  6. Pruebas de contenido dinámico: Si la página usa scroll infinito, navegación de una sola página o diálogos modales inyectados mediante JavaScript, interactúe con estas funciones para cargar nuevo contenido en el DOM y luego vuelva a ejecutar el análisis automatizado o el fragmento de consola para comprobar si hay duplicados introducidos por el contenido dinámico.

Cómo Corregir

IDs de campos de formulario duplicados en secciones repetidas — Incorrecto

<!-- Shipping Address -->
<label for='city'>City</label>
<input type='text' id='city' name='shipping-city'>

<!-- Billing Address -->
<label for='city'>City</label>
<input type='text' id='city' name='billing-city'>
<!-- FAIL: Both inputs share id='city'. The second label's 'for' attribute
     resolves to the first input, so screen readers announce the wrong field. -->

IDs de campos de formulario duplicados en secciones repetidas — Correcto

<!-- Shipping Address -->
<label for='shipping-city'>City</label>
<input type='text' id='shipping-city' name='shipping-city'>

<!-- Billing Address -->
<label for='billing-city'>City</label>
<input type='text' id='billing-city' name='billing-city'>
<!-- PASS: Each input has a unique ID scoped to its section.
     Screen readers correctly announce each field's label. -->

Componente reutilizable renderizado varias veces — Incorrecto

<!-- Product Card 1 -->
<div class='product-card'>
  <img id='product-img' src='shoe.jpg' alt='Running Shoe'>
  <button id='add-to-cart' aria-describedby='product-desc'>Add to Cart</button>
  <p id='product-desc'>Free shipping on orders over 500 TL.</p>
</div>

<!-- Product Card 2 (same template, duplicate IDs) -->
<div class='product-card'>
  <img id='product-img' src='boot.jpg' alt='Hiking Boot'>
  <button id='add-to-cart' aria-describedby='product-desc'>Add to Cart</button>
  <p id='product-desc'>Free shipping on orders over 500 TL.</p>
</div>
<!-- FAIL: IDs duplicated across cards. aria-describedby on the second button
     resolves to the <p> in the first card, not the second. -->

Componente reutilizable renderizado varias veces — Correcto

<!-- Product Card 1 -->
<div class='product-card'>
  <img id='product-img-1' src='shoe.jpg' alt='Running Shoe'>
  <button id='add-to-cart-1' aria-describedby='product-desc-1'>Add to Cart</button>
  <p id='product-desc-1'>Free shipping on orders over 500 TL.</p>
</div>

<!-- Product Card 2 -->
<div class='product-card'>
  <img id='product-img-2' src='boot.jpg' alt='Hiking Boot'>
  <button id='add-to-cart-2' aria-describedby='product-desc-2'>Add to Cart</button>
  <p id='product-desc-2'>Free shipping on orders over 500 TL.</p>
</div>
<!-- PASS: Each card's IDs are unique. ARIA references resolve correctly
     within their own card. Use a counter, UUID, or slug-based strategy
     to generate IDs in your component framework. -->

Diálogo modal con referencia de etiqueta ARIA duplicada — Incorrecto

<!-- A generic heading used as a reusable ID -->
<h1 id='dialog-title'>Welcome</h1>

<div role='dialog' aria-modal='true' aria-labelledby='dialog-title'>
  <h2 id='dialog-title'>Confirm Your Order</h2>
  <p>Are you sure you want to place this order?</p>
  <button>Confirm</button>
  <button>Cancel</button>
</div>
<!-- FAIL: Two elements share id='dialog-title'. The dialog's
     aria-labelledby resolves to the page <h1>, not the dialog heading.
     Screen readers will announce 'Welcome' as the dialog name. -->

Diálogo modal con referencia de etiqueta ARIA duplicada — Correcto

<h1>Welcome</h1>

<div role='dialog' aria-modal='true' aria-labelledby='confirm-dialog-title'>
  <h2 id='confirm-dialog-title'>Confirm Your Order</h2>
  <p>Are you sure you want to place this order?</p>
  <button>Confirm</button>
  <button>Cancel</button>
</div>
<!-- PASS: The dialog heading has a unique, descriptive ID.
     aria-labelledby correctly identifies the dialog to screen readers
     as 'Confirm Your Order'. -->

Errores Comunes

  • Copiar y pegar el marcado de un componente sin actualizar los IDs: Las personas desarrolladoras a menudo duplican una sección de HTML que funciona para una segunda instancia (un segundo bloque de dirección, un segundo panel de pestañas, un segundo elemento de acordeón) y se olvidan de actualizar todos los valores de ID para que sean únicos. Establezca una convención de nombres como component-name-index (por ejemplo, accordion-panel-1, accordion-panel-2) y aplíquela en la revisión de código.
  • Usar IDs estáticos en componentes de framework sin una estrategia de clave única: React, Vue, Angular y frameworks similares pueden renderizar el mismo componente docenas de veces en una página. Usar un id='search-input' codificado de forma rígida dentro de un componente reutilizable creará tantos duplicados como instancias haya. Derive siempre los IDs de props, de un contador o de una utilidad como useId() en React 18+.
  • Confiar en la selección por clase CSS en lugar de corregir el HTML: Algunas personas desarrolladoras sortean los problemas de IDs duplicados cambiando los selectores de JavaScript de getElementById a querySelector con una clase, mientras dejan los IDs duplicados en su lugar. Esto puede corregir el comportamiento visual pero no hace nada para resolver las asociaciones rotas en el árbol de accesibilidad.
  • Bucles de plantillas del lado del servidor que generan el mismo ID en cada iteración: Una plantilla de Jinja2, Blade o Twig que renderiza id='item-title' dentro de un bucle {% for item in items %} producirá un duplicado por cada elemento de la lista. Añada siempre el índice del bucle o el identificador del elemento al ID: id='item-title-{{ loop.index }}'.
  • Ignorar IDs en elementos ocultos o fuera de pantalla: Los elementos con display: none o visibility: hidden siguen presentes en el DOM y sus IDs siguen registrados. Una plantilla de modal oculta que comparte un ID con un elemento visible causará los mismos fallos de análisis. Use el atributo hidden o asegúrese de que las plantillas ocultas usen IDs únicos.
  • Suponer que el ámbito de un Shadow DOM resuelve el problema: Los IDs dentro de un Shadow DOM nativo están acotados y no entran en conflicto con los IDs del light DOM u otras raíces shadow. Sin embargo, muchas bibliotecas de componentes usan un polyfill o un enfoque no estándar que no proporciona un verdadero aislamiento. Verifique la salida real del DOM en lugar de asumir el comportamiento del framework.
  • Generar IDs basados en contenido proporcionado por la persona usuaria sin saneamiento ni desduplicación: Crear IDs a partir de nombres de productos, títulos de artículos u otro texto dinámico puede producir colisiones cuando dos elementos comparten el mismo nombre (por ejemplo, dos productos llamados "Classic" que generan ambos id='classic'). Añada siempre una clave de base de datos única o un índice a los IDs derivados del contenido.
  • No hacer pruebas después de la navegación del lado del cliente en aplicaciones de una sola página: Las SPAs que inyectan nuevo contenido de ruta en el DOM sin recargar completamente la página pueden acumular IDs de rutas visitadas previamente si el contenido antiguo no se desmonta correctamente. Ejecute análisis de axe después de navegar entre rutas, no solo en la carga inicial.
  • Olvidar los IDs usados en elementos SVG <defs> y <use>: Los patrones de sprites SVG que definen símbolos con IDs dentro de <defs> y luego los referencian con <use href='#icon-arrow'> pueden crear IDs duplicados si la misma definición de símbolo se incluye varias veces en una página. Centralice las definiciones de sprites SVG e inclúyalas solo una vez.
  • Pasar por alto IDs generados por widgets de terceros, plugins de chat o scripts de analítica: Los scripts de terceros a veces inyectan elementos con IDs codificados de forma rígida. Si su propio código usa el mismo ID, surge un conflicto que puede no notar durante el desarrollo. Audite el DOM completo renderizado, incluido el contenido de terceros, y comunique los conflictos a los proveedores o aplique un espacio de nombres a sus propios IDs para evitar colisiones.

Relación con las Regulaciones de Accesibilidad de Turquía

La Circular Presidencial 2025/10 de Turquía, publicada en el Boletín Oficial número 32933 el 21 de junio de 2025, establece requisitos obligatorios de accesibilidad web para un amplio conjunto de entidades públicas y privadas que operan en Turquía. La circular adopta WCAG 2.2 como su estándar técnico de referencia, haciendo de la conformidad de Nivel A el mínimo legal para todas las entidades cubiertas.

WCAG 4.1.1 Parsing es un criterio de Nivel A. Aunque el W3C lo declaró obsoleto en WCAG 2.2, las reglas de axe-core que aplican su preocupación principal —IDs únicos— siguen activas y continúan siendo señaladas en auditorías de accesibilidad realizadas con base en WCAG 2.2. Las auditorías regulatorias turcas y las revisiones de conformidad que usan herramientas de análisis automatizado señalarán por tanto las infracciones de duplicate-id como posibles fallos de Nivel A, independientemente del estado de obsolescencia del criterio en la especificación. Las organizaciones que busquen demostrar conformidad deben tratar las infracciones de IDs duplicados como problemas bloqueantes.

Las entidades cubiertas por la Circular Presidencial 2025/10 incluyen una amplia gama de instituciones públicas y organizaciones del sector privado: todos los organismos de gobierno central y local y sus agencias afiliadas; bancos e instituciones financieras reguladas por la legislación bancaria turca; hospitales y proveedores de atención sanitaria privados; operadores de telecomunicaciones que atienden a 200,000 o más suscriptores; plataformas de comercio electrónico y mercados en línea; agencias de viajes y operadores turísticos; empresas de transporte privado que operan bajo concesiones públicas; y escuelas e instituciones educativas privadas autorizadas por el Ministerio de Educación Nacional (MoNE).

La circular establece un calendario de cumplimiento por fases. Las instituciones públicas deben lograr la conformidad completa de Nivel A en el plazo de un año a partir de la fecha de publicación de la circular. Las entidades del sector privado en las categorías cubiertas tienen dos años para alcanzar el mismo estándar. El incumplimiento expone a las entidades cubiertas a escrutinio regulatorio, posibles sanciones administrativas y riesgo reputacional en un mercado cada vez más consciente de la accesibilidad.

Para las organizaciones turcas, abordar las infracciones de IDs duplicados es particularmente relevante en contextos donde intervienen formularios digitales, flujos de pago en línea, portales de servicios gubernamentales y sistemas de reserva de atención sanitaria. Estos son precisamente los tipos de interfaz que tienen más probabilidades de usar secciones de formulario repetidas, componentes reutilizables e integraciones de widgets de terceros que introducen IDs duplicados. Establecer pruebas de accesibilidad automatizadas —como integrar axe-core en los pipelines de CI/CD— como parte del proceso de desarrollo es tanto una buena práctica técnica como una estrategia pragmática para mantener el cumplimiento regulatorio continuo bajo los requisitos de la circular.