Mapeo de conformidad con WCAG

Cómo se relacionan los resultados de reglas individuales con un Criterio de Éxito (SC) de WCAG, y qué puede y no puede decirte surea11y sobre la conformidad general.

Las tres capas

  1. Reglas atómicas (checksResults[]): una decisión normativa cada una, por ejemplo "¿tiene este <img> un atributo alt?". Ver el catálogo de reglas para todas.
  2. Facetas: un SC de WCAG suele ser más grande de lo que una sola regla puede decidir de forma determinista. Internamente, cada SC se divide en "facetas" nombradas (por ejemplo, 1.1.1 Non-text Content tiene facetas como img-alt-attr-present, text-alternative-quality, decorative-null) registradas en src/coverage/wcag-facets.js, cada una marcada como full (una regla la decide con alta confianza), partial (una regla decide parte de ella; ver las notas de alcance de cada regla) o manual (no existe ninguna heurística automatizada segura). Ejecuta npm run coverage para regenerar coverage/coverage-report.md, el desglose de facetas por SC. Desde core 1.15.0 cada faceta de un criterio que evalúa una regla tiene además un procedimiento, cómo la evalúa una persona a mano (ver Procedimientos de prueba).
  3. Reglas compuestas (resultado agregado por SC de WCAG) (rulesResults[]): un agregado generado de cada regla atómica asociada a un SC, que da un único veredicto pass/fail/cantTell/notApplicable por SC en lugar de tener que combinar tú mismo docenas de resultados atómicos. Ver la lista completa (por ejemplo, wcag-1.1.1-non-text-content agrupa 21 reglas atómicas).

Cómo se calcula el resultado de un compuesto

Precedencia determinista, evaluada sobre los contribuyentes atómicos de ese compuesto:

Precedencia del resultado compuesto
CondiciónResultado compuesto
Cualquier contribuyente en failfail
Ningún fail, pero algún contribuyente en cantTell o un contribuyente listado que no se ejecutó en absolutocantTell
Todos los contribuyentes en notApplicable, para un criterio cuyas reglas encuentran todos los casos a los que se aplicanotApplicable
Todos los contribuyentes en notApplicable, para cualquier otro criteriocantTell (composite.rollup.cantTell.partialCoverage)
En cualquier otro caso (todos se ejecutaron, ninguno falló ni quedó en cantTell, no todos son N/A), para un criterio cuyas reglas comprueban todo lo que pidepass
En cualquier otro caso, para cualquier otro criteriocantTell (composite.rollup.cantTell.partialCheck)

Cuándo un criterio se cumple y cuándo no es aplicable

Desde la 1.14, un criterio se cumple solo cuando sus reglas comprueban todo lo que pide, igual que WCAG lo da por satisfecho solo cuando lo está entero. Que todas las reglas se cumplan solo dice que se cumplen las partes que comprueban: html-lang-attr-present comprueba que <html lang="fr"> nombra un idioma, no que la página esté en francés (3.1.1), y page-title-present, que la página tiene título, no que el título la describa (2.4.2). Un criterio así queda en cantTell, con el código de motivo composite.rollup.cantTell.partialCheck, y el informe HTML dice que se comprobó en parte («Comprobado en parte»): el resto lo comprueba una persona. Los criterios cuyas reglas los comprueban enteros se enumeran en src/coverage/complete-applicability.js (COMPLETE_CHECK), cada uno con su motivo, y todavía no hay ninguno: cada uno pide algo que las reglas no ven, ya sea si un texto alternativo, una etiqueta, un encabezado o el texto de un enlace dicen lo correcto, o cómo lo cumple el contenido que un script dibuja o cambia. Así que en la 1.14 no se cumple ningún compuesto de WCAG.

Del mismo modo, un criterio es notApplicable solo cuando sus reglas encuentran todos los casos a los que se aplica: los enlaces para 2.4.4 y 2.4.9, los elementos enfocables para 2.4.3 y 2.4.7, los componentes con etiqueta para 2.5.3, el marcado para 4.1.1. Los campos de formulario (1.3.5, 3.3.2) y los objetivos de puntero (2.5.8) no están en esa lista: un campo hecho con un rol ARIA, un campo sin autocomplete o un elemento que solo es un objetivo mediante un script pueden estar en una página donde esas reglas no encontraron nada. Para cualquier otro criterio, que ninguna regla encuentre nada solo dice que faltan los casos que buscan: un script que redirige tras un retardo es un límite de tiempo (2.2.1) sin <meta http-equiv="refresh">, y un pasaje en otro idioma (3.1.2) no tiene por qué llevar lang. En términos de WCAG-EM, «no presente» exige haber mirado todos los lugares donde podría estar el contenido, así que un criterio así queda en cantTell, con el código de motivo composite.rollup.cantTell.partialCoverage.

Esto significa: un compuesto en fail es una violación real y determinista, y un compuesto en cantTell cuyos contribuyentes se cumplieron todos (partialCheck) dice que todas las comprobaciones automatizadas de ese criterio salieron limpias y que lo que queda lo comprueba una persona. No es, por sí solo, una declaración de conformidad. La tabla de facetas (coverage-report.md) muestra qué partes de cada criterio tienen cobertura automatizada y cuáles comprueba una persona. Muéstralos como «comprobaciones superadas, requiere revisión» y «nada que comprobar encontrado», nunca como cumplidos.

El array data.details.contributors de un compuesto (ver el esquema de salida) enumera cada regla atómica y su resultado individual: úsalo para ver exactamente qué faceta o facetas provocaron un fail/cantTell, en lugar de tratar el compuesto como una caja negra.

Una regla personalizada (engineOptions.customRules) o una regla de un pack que se ejecutó cuenta para el compuesto de cada criterio al que se asocia, mediante meta.wcagSc o una entrada de WCAG en meta.normativeMappings, igual que una regla incorporada: si falla, el compuesto falla. El compuesto marca una regla personalizada con custom: true entre sus contribuyentes y la enumera en data.details.customChecksIds, ya que las reglas personalizadas no se comprueban contra las especificaciones como las incorporadas. Una sustitución de una regla incorporada cuenta donde dice su propia asociación: en el lugar de la incorporada cuando se asocia a ese criterio, y en ninguna parte cuando no se asocia a ninguno (con un aviso).

Un análisis que nombra reglas en lugar de criterios (por ID, como includeRuleIds: ['link-name-present'], por una etiqueta que no es de WCAG, por optInRules o como las buenas prácticas) también obtiene los compuestos de los criterios que esas reglas comprueban; una etiqueta de criterio como wcag244 informa solo de ese criterio (consulta Opciones del motor). Antes de 1.12, un análisis así no informaba de ninguno. La tabla anterior los decide: una regla que falla hace fallar el criterio, y las reglas que el análisis no seleccionó son contribuyentes que no se ejecutaron, así que el criterio es como mucho cantTell, comprobado en parte (reasonCode composite.rollup.cantTell.notSelectedChild, con cada uno de esos contribuyentes marcado notSelected: true; «Comprobado en parte» en el informe HTML).

Apuntar a un nivel de conformidad (A / AA / AAA)

Nombra la versión y el nivel de WCAG en runOnly.wcag:

// WCAG 2.2 Level A and AA:
runDomRulesInPage(url, null, {}, { wcag: { version: '2.2', level: 'AA' } });
 
// The same, and the best-practice rules, which test no WCAG criterion:
runDomRulesInPage(url, null, {}, { wcag: { version: '2.2', level: 'AA' }, bestPractices: true });

Un objetivo ejecuta las reglas de cada criterio vigente en esa versión en ese nivel y en los inferiores, según el nivel que tiene cada criterio en esa versión (WCAG_CRITERIA en @surea11y/core/wcag), así que el 4.1.1 Parsing forma parte de los objetivos 2.0 y 2.1 y no de los 2.2. También establece la versión de WCAG de la ejecución y el nivel de los resultados agregados que se informan. Ver Seleccionar por objetivo de WCAG.

Cada entrada de WCAG_CRITERIA tiene también el id que el W3C da al criterio en WCAG 2.2 (non-text-content). Cada asociación de WCAG que lleva una regla enlaza la sección de su criterio en la Recomendación de esa versión (url) y su documento Understanding (understandingUrl), para 2.1 y 2.2: los generadores de informes usan el documento Understanding como enlace de ayuda de una regla cuando la regla no tiene uno propio.

Con etiquetas

Los mismos objetivos se pueden expresar con etiquetas de nivel, en runOnly.tags (o engineOptions.tags.include):

// WCAG 2.0 A and AA. Both tags are required: they are not cumulative.
runDomRulesInPage(url, null, {}, { tags: ['wcag2a', 'wcag2aa'] });

Las etiquetas de nivel no se anidan, y esto es lo más fácil de entender mal aquí. Una regla lleva una etiqueta de nivel por cada Criterio de Éxito al que se asocia, y nada más: una regla asociada solo a un criterio AA lleva la etiqueta wcag2aa y no wcag2a. Pedir { tags: ['wcag2aa'] } por sí solo, por tanto, ejecuta las 11 reglas asociadas a un criterio AA de la versión 2.0, no las 105 que forman un objetivo A + AA de la 2.0. Enumera cada nivel que quieras, o usa runOnly.wcag, que no tiene ese problema. engineOptions.profile (wcag20-aa, wcag21-aa, wcag22-aa y las demás versiones y niveles de WCAG) también lo hace por ti; ver Perfiles de conformidad.

Lo mismo aplica entre versiones de WCAG (un criterio introducido en 2.1 o 2.2 lleva solo su propia etiqueta de origen), así que un objetivo de conformidad completo es una unión de conjuntos de etiquetas. Ver la guía de opciones del motor para los conjuntos ya preparados por versión, incluido el único criterio que WCAG 2.2 eliminó en lugar de añadir.

El criterio eliminado se maneja automáticamente. Cada ejecución resuelve una versión de WCAG objetivo (engineOptions.wcagVersion, si no, el wcagVersion del perfil del pack que aplica la ejecución, si no, la versión de runOnly.wcag, si no, lo que impliquen tus etiquetas de versión, si no, 2.2) y la devuelve como engine.wcagVersion. Bajo un objetivo 2.2, una regla asociada solo al SC 4.1.1 Parsing no puede devolver fail: se ejecuta, informa de sus ocurrencias, y vuelve como cantTell con un campo wcagVersionScope que explica la coerción. Desde la 1.14, bajo un objetivo 2.0 o 2.1 hace lo mismo, con wcagVersionScope.by: "errata": las erratas de 2023 del W3C a esas versiones dan el 4.1.1 por satisfecho siempre para contenido en HTML o XML. Así, ningún análisis de WCAG bloquea por el 4.1.1; solo un perfil cuyo estándar conserva su texto, uno que declara keepsParsing: true como hace en301549-v3.2.1, sigue haciéndolo fallar.

Los compuestos, a diferencia de las reglas atómicas, sí se filtran de forma acumulativa. El ejecutor lee engineOptions.wcagLevel, si no, el nivel de runOnly.wcag, o el nivel más alto nombrado en tags, y descarta todo compuesto por encima de él, así que pedir ['wcag2a', 'wcag2aa'] no devuelve ninguna entrada en rulesResults para un SC exclusivo de AAA. wcagLevel ('A', 'AA' o 'AAA') es la opción para un análisis que nombra comprobaciones en lugar de un objetivo: todas las reglas seleccionadas se siguen ejecutando, y solo los criterios que informa se limitan a ese nivel, así que un análisis de contrast-computable con 'AA' informa del 1.4.3 y no del 1.4.6 de nivel AAA. La ejecución lo registra como engine.wcagLevel. Eso es targetWcagLevelOf/isAllowedByTargetLevel en src/core/dom-runner.js si necesitas la precedencia exacta.

Omite tags por completo (el valor por defecto) y se ejecuta cada regla en cada nivel, sin ninguna supresión de compuestos.

EN 301 549

Obsoleto

EN 301 549 pasa a su propio pack, @surea11y/pack-en301549, y sale de core en la 2.0.0, con sus perfiles, su clave de mappings y @surea11y/core/en301549. Todo funciona como antes, con un console.warn por proceso. Para cambiar, se pasa el pack en engineOptions.packs: el análisis toma del pack los mismos nombres, y @surea11y/pack-en301549/public tiene las exportaciones de la tabla. Ver cómo core declara obsoleto un estándar.

El capítulo 9 de EN 301 549 reformula los criterios de éxito de nivel A y AA de WCAG como cláusulas numeradas 9. más el número del propio criterio: WCAG 1.4.3 es la cláusula 9.1.4.3. Cuando un análisis las pide, cada resultado atómico y compuesto lleva, tras sus entradas de WCAG en meta.normativeMappings, la cláusula de cada uno de sus criterios, una vez por cada versión del estándar que incluye ese criterio. Un análisis las pide con engineOptions.mappings: ['en301549'] (ambas versiones) o ['en301549:V3.2.1'] (una), o apuntando a un perfil de EN 301 549, que añade la versión que nombra; por defecto los resultados solo nombran WCAG (ver Otros estándares).

VersiónBasada enSe diferencia de la otra en
V3.2.1 (2021-03)WCAG 2.1 A y AA, 50 criteriosIncluye 9.4.1.1 Procesamiento
V4.1.1 (2026-09)WCAG 2.2 A y AA, 55 criteriosAñade 2.4.11, 2.5.7, 2.5.8, 3.2.6, 3.3.7, 3.3.8; 9.4.1.1 queda sin efecto

Cada entrada de cláusula nombra también el criterio que reformula, como wcagSc: ["1.4.3"], para que una vista agrupada por criterio (las suites de JUnit, el resultado agregado del informe HTML) la sitúe bajo el correcto incluso en una regla asociada a varios. Los criterios AAA no tienen cláusula en ninguna versión. Los catálogos nombran las cláusulas con las mismas opciones: getChecksCatalog({ mappings: ['en301549'] }) en el normativeMappings de cada entrada, y las entradas compuestas que devuelve getRulesCatalog({ mappings: ['en301549'] }) como meta.standardMappings, así que las cláusulas de un criterio se pueden leer sin ejecutar un análisis.

La tabla es pública como @surea11y/core/en301549, para herramientas que necesitan la vista inversa, como qué criterios exige una versión que un análisis no cubrió:

const { EN301549_CLAUSES, en301549ClausesForSc } = require('@surea11y/core/en301549');
 
Object.keys(EN301549_CLAUSES['V3.2.1']); // the 50 WCAG 2.1 A and AA criteria V3.2.1 restates
en301549ClausesForSc('2.5.8'); // [{ version: 'V4.1.1', clause: '9.2.5.8', title: 'Target size (minimum)' }]

Es una correspondencia entre dos documentos publicados, no una declaración de conformidad: una cláusula en un resultado dice qué requisito de EN 301 549 es ese criterio de WCAG, nada más. Qué versión exige una ley concreta queda fuera del motor.

EN 301 549 es una entrada de un registro de estándares que leen el ejecutor, el catálogo de reglas y los generadores de informes. Un estándar con veredictos propios (requisitos que comprueban sus propias reglas, un estándar nacional como RGAA, por ejemplo), o la lista de comprobación de una organización, llega como un pack: un paquete independiente fuera de core, que un análisis recibe en engineOptions.packs y cuya entrada se une al registro para ese análisis, con sus propios perfiles, reglas y resultados agregados. Una regla personalizada puede nombrar cualquier estándar en su propio normativeMappings, pero solo los estándares registrados (los de core y los de los packs del análisis) reciben un perfil, un interruptor de mappings, reglas opcionales, resultados agregados o un lugar en SARIF, JUnit y el informe HTML. Cómo se integra un estándar en el propio core está en WCAG_CONFORMANCE.md, la guía para contribuidores en GitHub.

Lo que este motor no puede decirte

Ninguna herramienta automatizada, esta incluida, puede certificar la conformidad WCAG completa. Esa no es una limitación específica de surea11y; es inherente a WCAG en sí; una fracción importante de los Criterios de Éxito requiere criterio humano (¿es este texto alternativo exacto, y no solo presente?; ¿es este mensaje de error comprensible?) o pruebas dinámicas que la arquitectura de análisis estático del DOM de este motor no puede realizar en absoluto (detección de trampas de teclado, disposición y reflujo real con zoom). Ver las limitaciones conocidas para la lista completa y explícita de qué queda fuera de alcance y por qué.

Lo que surea11y sí puede darte, con honestidad:

  • Las reglas integradas de core están escritas para devolver fail solo ante una violación normativa y determinista del estándar y la versión que elegiste como objetivo, y para informar cantTell cuando no pueden estar seguras. Es un objetivo de diseño, no una garantía: las limitaciones conocidas enumeran casos límite en los que una regla aún puede equivocarse, las ocurrencias de cada hallazgo muestran sobre qué se decidió, y el catálogo de reglas indica la confianza de cada regla. La precisión de una regla personalizada, una regla de un pack o una sustitución depende de cómo se haya escrito. Por defecto ese estándar es WCAG. Una regla para un requisito que solo impone otro estándar es opcional: por defecto solo se ejecuta cuando apuntas a ese estándar (ver Reglas opcionales). engineOptions.optInRules desbloquea las reglas opcionales sin quitar el perfil ni los filtros del análisis. Con optInRules: 'all' y sin perfil ni filtro, la ejecución abarca todas las reglas y no apunta a ningún estándar concreto; una lista de etiquetas desbloquea solo esos grupos de reglas opcionales. engine.optInRules enumera las etiquetas cuyas reglas se ejecutaron. Con un perfil de WCAG, una regla desbloqueada solo se ejecuta cuando sus etiquetas coinciden con ese perfil, y la ejecución sigue apuntando a WCAG. Así, entre las reglas de core, un análisis con un objetivo de WCAG solo ejecuta las de los criterios del objetivo, más las reglas de buenas prácticas si las añades. Una regla de buenas prácticas no nombra ningún criterio, y aun así puede informar fail cuando lo que encuentra es en sí una barrera, como un encabezado vacío (empty-heading); ningún compuesto de WCAG lo cuenta. Una regla personalizada, una sustitución o una regla de un pack sin etiqueta opcional se ejecuta cuando la selección la incluye, y entonces puede informar un fail por una comprobación propia que WCAG no exige.
  • Desde core 1.15.0, una regla hace fallar un criterio de WCAG solo donde WCAG falla. Donde solo se incumple WAI-ARIA o el modelo de contenido de HTML y el nombre, el rol y el valor de la página sobreviven, las reglas de estructura ARIA y HTML preguntan en su lugar (cantTell, incertidumbre spec-only), de modo que un fallo de WCAG nunca representa una infracción de la especificación que WCAG no cuenta. Para juzgar esos hallazgos según el propio WAI-ARIA, ejecuta el perfil del pack de WAI-ARIA junto al de WCAG.
  • Cada cantTell es una señal explícita para revisión humana, no una incertidumbre silenciada.
  • Un compuesto nunca da por cumplido un criterio que sus reglas solo comprueban en parte: cuando todas se cumplen, queda en cantTell (composite.rollup.cantTell.partialCheck), así que un pass nunca guarda silencio sobre una parte que ninguna regla comprueba.
  • La tabla de cobertura de facetas te dice exactamente qué partes de qué SC no tienen ninguna cobertura automatizada, y por tanto qué queda para una persona, y los procedimientos de prueba de core dicen cómo comprueba cada una una persona, para cada criterio que evalúa una regla.

Que ningún compuesto esté en fail en cada SC de tu nivel objetivo significa: cada comprobación automatizable para ese nivel salió limpia. Es el subconjunto automatizable de la conformidad, declarado con precisión, no un sustituto de la revisión manual que la propia WCAG exige.