Esquema de salida

Esta es la estructura exacta del objeto que devuelven runDomRulesInPage(...) / runa11yCoreInPage(...) (consulta INTEGRATION.md para saber cuál llamar). Todos los ejemplos de esta página son salida real del motor actual (schemaVersion: "1.0.0"), no están escritos a mano. runa11yCoreAcrossFrames devuelve una estructura distinta y recursiva que envuelve esta. Ver Resultado entre frames más abajo.

Resultado de nivel superior

{
engine: {
tag: string,
version: string, // the @surea11y/core release that produced the result
schemaVersion: string,
locale: { requested: string, resolved: string, reason: string },
wcagVersion: "2.0" | "2.1" | "2.2",
wcagLevel?: "A" | "AA" | "AAA",
profile?: string, // "wcag20-a" … "wcag22-aaa" (wcag<version>-<level>), the deprecated "en301549-v4.1.1", "en301549-v3.2.1" and "section508", or a pack's own
profiles?: string[], // a scan of several profiles only: the profiles that ran
profilesNotRun?: { profile: string, reason: string }[], // a scan of several profiles only
mappings?: string[], // e.g. ["en301549"] or ["en301549:V3.2.1"]
profileExcludes?: { rules: string[], criteria: string[] },
law?: { id: string, name: string, jurisdiction: string, lang: string }, // experimental
optInRules?: string[],
outputDetail?: "findings",
packs?: string[], // name@version
environment: {
layout: boolean,
viewport?: { width: number, height: number }, // CSS px
devicePixelRatio?: number,
colorScheme?: "light" | "dark",
forcedColors?: "active" | "none",
forcedColorsCss?: true, // under forced colors, when the page's own CSS reacts to them
fonts?: "loaded" | "loading",
images?: "loaded" | "loading",
animationsSettled?: number,
contentVisibilitySkipped?: number, // elements whose content-visibility: auto content was skipped
internalsHook?: "early" | "late", // only where the page runs the internals hook
listenerHook?: "early" | "late", // only where the page runs the listener hook
liveRegionHook?: "early" | "late" // only where the page runs the live-region hook
}
},
url: string | null,
title: string | null,
timestamp: string | null,
perfStats: object | null,
contextSelector: string | string[] | null,
contextMatch: { elementCount: number, unmatchedSelectors: string[] } | null,
checksResults: CheckResult[],
rulesResults: CompositeResult[],
standards?: Array<{ key: string, standard: string, titleLang?: string, note?: string }>,
overriddenBuiltinIds: string[],
skippedCustomRules: Array<{ id: string | null, reason: string, excluded?: true }>,
skippedPacks?: Array<{ name: string | null, reason: string }>,
profiles?: { [profile: string]: { wcagVersion, wcagLevel?, standards, rulesResults } } // a scan of several profiles only
}
CampoSignificado
engine.tagLa etiqueta de identidad propia del motor, actualmente "a11ycore". Cada regla (integrada o personalizada) la lleva en meta.tags: los ruleId de las reglas en sí son simples (sin prefijo).
engine.versionLa versión de @surea11y/core que produjo el resultado, como "1.11.1": lo que hay que citar en un informe de error, y cómo distinguir resultados de dos versiones. Un resultado puede cambiar entre versiones sin que cambie su estructura, así que esto no es schemaVersion. Los generadores de SARIF y EARL lo usan como versión de la herramienta cuando no indicas ninguna.
engine.schemaVersionLa versión del esquema de resultados ("1.0.0"). Sujeta a incrementarse si la estructura de este documento cambia alguna vez de manera incompatible: ánclate a ella si analizas la salida de forma programática. Consulta API_STABILITY.md para ver la lista completa de campos estables/inestables y la política de incremento de versión.
engine.localeQué diccionario usó realmente la ejecución. requested es tu engineOptions.locale después de recortar espacios ("en" si no pasaste nada o pasaste algo que no es una cadena); resolved es el idioma cuyo diccionario se usó; reason explica el porqué de ese emparejamiento. Como el mecanismo de reserva de idioma es gradual y se aplica cadena por cadena, pedir un idioma que la compilación no incluye produce texto en inglés en lugar de un error: este campo es cómo te enteras de eso sin leer las cadenas. Se informa una vez por resultado: una ejecución usa un solo diccionario en todo momento.
engine.locale.reason"ok": obtuviste el diccionario que pediste, y contiene todas las claves. "primary-subtag": tu código tenía un subtag sin diccionario propio, así que se usó su idioma base: "de-DE" se resuelve a "de". "dictionary-not-loaded": el proyecto incluye ese idioma, pero esta compilación no lo lleva y no se suministró ninguno (el paquete independiente para navegador, sin su archivo lateral de idioma). "unknown-locale": el proyecto no tiene ninguna traducción de ese tipo. "partial-dictionary": se usó el diccionario, pero le faltan claves que el inglés sí tiene, así que esas cadenas recurrieron al inglés. Trata este conjunto como abierto; versiones posteriores pueden añadir más.
engine.wcagVersionContra qué versión de WCAG comprobó esta ejecución: tu engineOptions.wcagVersion, o el wcagVersion del perfil del pack que aplicó la ejecución, o lo que implicaban tus etiquetas de origen de versión, o el valor predeterminado "2.2". Para el SC 4.1.1 Parsing indica por qué una regla mapeada únicamente a él no puede dar fail: 2.2 lo eliminó, y las erratas de 2023 de 2.0 y 2.1 lo consideran siempre satisfecho (ver checksResults[i].wcagVersionScope más abajo). Se informa una vez por resultado: una ejecución tiene un solo objetivo en todo momento.
engine.wcagLevelEl nivel de WCAG que filtró los criterios informados, "A", "AA" o "AAA": engineOptions.wcagLevel cuando se indicó y es válido, o si no el nivel que da la selección (runOnly.wcag.level, o el nivel más alto que nombran sus etiquetas de versión/nivel, incluidas las de un perfil). Los resultados agregados de WCAG por encima de él no están en rulesResults, aunque sus reglas se ejecutaron y están en checksResults; un resultado agregado que la selección nombró por id se informa diga lo que diga engineOptions.wcagLevel. Ausente cuando el análisis no tenía nivel, como un análisis completo o uno que solo nombra reglas, que informa todos los niveles. Los generadores de informes filtran por él los criterios que nombran. Ver Indicar el nivel de un análisis que nombra comprobaciones.
engine.profilePresente solo cuando un engineOptions.profile seleccionó las reglas de esta ejecución, y nombra el perfil en minúsculas (en un análisis de varios perfiles, el primero que se ejecutó). Ausente cuando no se pidió ninguno, y también cuando se pidió uno pero no se aplicó (nombre desconocido, o un include en runOnly o engineOptions eligió las reglas en su lugar), así que su presencia es cómo confirmas que una ejecución apuntó de verdad a ese perfil. Ver Perfiles de conformidad.
engine.profilesSolo en un análisis de varios perfiles (engineOptions.profile como lista): los perfiles que se ejecutaron, en el orden pedido. engine.profile es entonces el primero de ellos. Ver Varios perfiles.
engine.profilesNotRunSolo en un análisis de varios perfiles, y solo cuando uno de ellos no se ejecutó: [{ profile, reason }], con reason unknown-profile (no existe ese perfil), pack-skipped (su pack se omitió, ver skippedPacks) u overridden-by-selection (un include en runOnly, o ids o etiquetas de reglas en engineOptions, eligen las reglas en su lugar, como lo harían para un perfil; lo hacen para el análisis de cada perfil). El conjunto de motivos está abierto.
engine.profileExcludesPresente solo cuando el perfil aplicado deja reglas fuera: { rules, criteria }, las reglas que nombra y los criterios de WCAG que deja fuera. Las reglas que nombra, las reglas cuyos criterios deja fuera todos, y los resultados agregados de WCAG de esos criterios no se ejecutaron; una regla que también comprueba un criterio que el perfil conserva sí se ejecutó, y cuenta para ese criterio. Esto reduce el análisis sin cambiar lo que exige WCAG. Ningún perfil incluido deja nada fuera.
engine.lawExperimental (core 1.16.0, hasta la 1.18.0). Presente solo cuando el perfil aplicado nombra una ley, aquella bajo la que se evalúa el estándar de un pack (ver Packs): { id, name, jurisdiction, lang }, siendo lang el idioma en que está name. Core indica la ley; nunca decide a quién se aplica.
engine.optInRulesPresente solo cuando engineOptions.optInRules ejecutó al menos una regla opcional que el resto de la selección no habría ejecutado. Enumera sus etiquetas. Su presencia significa que el resultado incluye reglas de requisitos más allá del estándar objetivo, así que un fallo puede no ser un fallo de WCAG. Ver Ejecutar todas las reglas.
engine.mappingsPresente solo cuando la ejecución nombra un estándar además de WCAG en meta.normativeMappings, mediante engineOptions.mappings o el perfil de un estándar. Los enumera de forma canónica: "en301549" para todas las versiones, "en301549:V3.2.1" para una. Su ausencia significa que todos los resultados nombran solo WCAG. Ver Otros estándares.
engine.packsPresente solo cuando el análisis se ejecutó con engineOptions.packs: cada pack que se ejecutó, como name@version, ordenados por nombre. Ver Packs.
engine.outputDetailPresente solo cuando engineOptions.output.detail es 'findings', como "findings": cada resultado de comprobación pass o notApplicable que no enumera ocurrencias es compacto, { ruleId, outcome, type } y su margin o su error cuando lo tiene, sin el título, la descripción ni meta, que son los del catálogo e iguales en todas las páginas. Los resultados fail y cantTell, los resultados agregados y engine están completos, así que ningún veredicto cambia. Los generadores de informes (SARIF, JUnit, el informe HTML, EARL, las funciones de baseline) leen un resultado compacto como el completo, rellenándolo a partir del catálogo de core. Un resultado que ese catálogo no podría rellenar se queda completo: todos los resultados de comprobación de un análisis con packs, el de una regla personalizada, y uno cuyo título o descripción cambiaron los messages del análisis. El resultado de una página vacía pasa de unos 263 KB a 90 KB.
engine.environmentLas condiciones en que se renderizó la página, leídas de la página al empezar el análisis, antes de que se ejecute ninguna regla. Las reglas que miden el diseño, como text-spacing-content-loss, pueden dar otro resultado para el mismo marcado con otro ancho de viewport o mientras las fuentes web aún se están cargando, así que esto es lo que necesitas para reproducir una ejecución. layout indica si la página tenía diseño: false en jsdom y otros emuladores de DOM, y entonces el viewport y los demás campos de renderizado se omiten, ya que no describen nada que se haya medido; solo internalsHook, listenerHook y liveRegionHook pueden acompañarlo, donde la página los ejecuta. En un navegador, viewport es innerWidth/innerHeight en píxeles CSS, devicePixelRatio el de la página (el zoom del navegador también se refleja aquí), colorScheme lo que coincide con prefers-color-scheme, forcedColors si coincide forced-colors: active ("active" con el alto contraste de Windows o los colores forzados emulados de Chromium, "none" en caso contrario; las reglas miden los colores propios de la página en ambos casos, ver Forced colors), forcedColorsCss es true con colores forzados cuando el propio CSS de la página reacciona a ellos de una forma que el análisis no puede evitar (una media query forced-colors, que sigue coincidiendo, o un color del sistema como GrayText, que sigue tomando el de la paleta forzada), así que los resultados de contraste pueden diferir de los de un análisis sin colores forzados, y fonts es "loading" mientras alguna de las fuentes de la página aún se está cargando, "loaded" en caso contrario. Un campo que la página no puede informar se omite. Leerlos no añade no determinismo: igual que el DOM, son entrada. Para obtener resultados que puedas comparar entre ejecuciones, espera a document.fonts.ready antes de analizar. images es "loading" mientras alguna imagen aún se está cargando, "loaded" en caso contrario: una imagen que llega más tarde puede mover lo que midieron las reglas de diseño, así que, para obtener resultados comparables, analiza después del evento load de la página. Una imagen con loading="lazy" se omite, ya que espera a que el lector desplace la página. animationsSettled es cuántas animaciones y transiciones en curso detuvo el análisis: durante el análisis, una finita (un fundido de entrada) se lee en su final y una que se repite indefinidamente (una marquesina, un indicador de carga) en su inicio, y cada una se devuelve exactamente a donde estaba después, así que una página analizada durante un fundido de entrada obtiene el mismo resultado que una analizada después. El navegador nunca pinta el estado detenido, ya que el análisis es una única tarea síncrona. contentVisibilitySkipped es cuántos elementos con content-visibility: auto tenían su contenido omitido por el navegador, como hace fuera de la pantalla: el análisis dispone ese contenido tal como queda una vez cerca del viewport, y deja que el navegador vuelva a omitirlo después (ver LAYOUT_MEASUREMENT.md en GitHub); no está presente si no hubo ninguno. internalsHook está presente solo donde la página ejecuta el hook opcional de internals (internalsHookScript(), ver INTEGRATION.md) en el documento analizado, con o sin diseño: "early" cuando se instaló antes de los scripts de la página y sigue siendo el attachInternals de la página, así que el análisis leyó los ElementInternals de cada elemento personalizado y un elemento sin ellos no plantea ninguna duda; "late" cuando se instaló después de ellos, o la página reemplazó attachInternals después, así que solo se leyeron los internals adjuntados después. Sin hook el campo no aparece y el resultado es como antes. listenerHook es lo mismo para el hook opcional de listeners (listenerHookScript(), ver INTEGRATION.md): "early" cuando se instaló antes de los scripts de la página y sigue siendo el addEventListener de la página, así que las reglas de teclado vieron todos los listeners que la página añadió desde script y que falte uno demuestra que no lo hay; "late" cuando se instaló después de ellos, o la página reemplazó addEventListener después. liveRegionHook está presente solo donde la página ejecuta el hook opcional de regiones vivas (liveRegionHookScript(), ver INTEGRATION.md): "early" cuando se instaló antes de que el documento terminara de cargarse, así que vio todos los cambios en las regiones vivas de la página desde entonces; "late" en caso contrario. Cada frame de un resultado entre frames lleva el suyo.
urlEl argumento pageUrl que pasaste, o document.location.href si pasaste null/lo omitiste (o pasaste algo que no es una cadena, lo que además registra un console.warn), o null si ninguno está disponible.
titledocument.title en el momento del análisis o null.
timestampNo se genera automáticamente. Solo se establece si pasas engineOptions.timestamp como una cadena no vacía: el motor no tiene reloj integrado (es determinista por diseño). Si quieres una marca de tiempo del análisis en el resultado, súplela tú mismo.
perfStatsnull a menos que engineOptions.perfStats: true. Temporizaciones/contadores internos: su estructura no está cubierta por este documento, trátalo como solo para depuración.
contextSelectorEl argumento (recortado) contextSelector que pasaste: una cadena, un array de cadenas (análisis multi-región, ver Opciones del motor), o null si no hay ninguno o está vacío.
contextMatchCómo se resolvió el contextSelector, o null si no se indicó ninguno. elementCount es cuántos elementos distintos coincidieron, las raíces del análisis. unmatchedSelectors enumera cada selector, tal como lo pasaste, que no coincidió con ningún elemento; una cadena separada por comas es aquí un solo selector, igual que es una sola llamada a querySelectorAll. Un elementCount de 0 significa que no se analizó nada: la página no tiene ese ámbito, todas las entradas de checksResults y rulesResults son notApplicable, y el resultado no dice nada sobre la página. Infórmalo como un ámbito que no se encontró, no como un análisis sin hallazgos. Un selector que el navegador no puede interpretar nunca llega a un resultado: el análisis lanza una excepción con code: 'INVALID_CONTEXT_SELECTOR' (ver Opciones del motor).
checksResultsUna entrada por cada regla atómica que se ejecutó (toda regla no filtrada por runOnly, ver Opciones del motor). Cada regla cargada produce una entrada, incluso las que dan como outcome notApplicable: esto no es una lista de «solo violaciones».
rulesResultsUna entrada por cada regla compuesta (resultado agregado) que se ejecutó. Ver Un resultado compuesto y Conformidad. Normalmente una por criterio de éxito de WCAG; una ejecución con el perfil de un estándar que tiene resultados agregados propios también los recibe (con meta.standard). Contiene los resultados agregados que selecciona la selección, y los de los criterios que comprueba una regla que la selección nombró por sí sola (por id, por ejemplo), que pueden estar comprobados en parte (ver Qué criterios informa un análisis). Array vacío cuando ninguna de las dos da alguno.
standardsPresente solo cuando el resultado nombra un estándar además de WCAG en una entrada de meta.normativeMappings o en el meta.standard de un resultado agregado: un { key, standard, titleLang?, note? } por cada estándar registrado que nombra, incluidos los de un pack, en el orden del registro. key es la clave del estándar ("en301549"), standard el nombre que llevan sus entradas ("EN 301 549"), titleLang el idioma en que están escritos los títulos de sus propios resultados agregados cuando no es el del análisis, y note el texto que aparece encima de sus propios resultados agregados en el informe HTML, en el idioma del análisis. Los generadores de informes lo leen en lugar del registro, así que un resultado guardado se muestra igual dondequiera que se genere, conozca o no esa instalación el estándar. Un estándar que solo nombra una regla personalizada no se incluye.
skippedPacksPresente solo cuando un pack de engineOptions.packs no se ejecutó: cada uno { name, reason }, con name igual a null cuando el pack no tenía. En una página (runa11yCoreInPage), name es el name@version indicado, y el motivo dice por qué la página no lo ejecutó: ninguna llamada a packScript registró exactamente el conjunto nombrado, o el script se preparó con otra versión de core. Cada uno se registra también con console.warn.
overriddenBuiltinIdsIds de regla en los que una entrada de engineOptions.customRules compartió su id con una regla integrada, de modo que la implementación personalizada reemplazó a la integrada para este análisis (ver Opciones del motor). Siempre es un array; vacío cuando no ocurrió ninguna colisión. También se registra vía console.warn en el momento del análisis, ya que una regla personalizada con el mismo nombre es tan probable que sea una colisión accidental como una sustitución deliberada.
skippedCustomRulesLas entradas de engineOptions.customRules que no se ejecutaron, cada una { id, reason } (id es null cuando la entrada no tenía): sin id, un runInPage que no es una función o no puede reconstruirse a partir de su código fuente, un meta que no supera la validación, un id que ya tiene otra regla personalizada, o el id de una regla compuesta; o una regla válida que la selección de la ejecución no selecciona (bajo un perfil o un objetivo de WCAG, una regla etiquetada como best-practice o sin etiqueta de WCAG), cuyo reason dice cómo seleccionarla; o una regla que dejan fuera las exclusiones de la selección (excludeRuleIds, excludeTestIds, excludeTags, rules.exclude…), cuyo reason es excluded by the selection. y que además tiene excluded: true, para que una herramienta distinga la elección de quien llama de un hueco sin leer el motivo. Siempre es un array; vacío cuando se ejecutaron todas las reglas personalizadas. Cada una se registra también con console.warn, salvo una regla excluida, que quien llama pidió dejar fuera. Una regla listada aquí no aparece en ningún resultado, así que un análisis que la dejó fuera se distingue de uno en el que pasó.

Varios perfiles

Con engineOptions.profile como una lista de dos o más (['wcag22-aa', 'wai-aria-1.2'], ver Opciones del motor), cada perfil juzga la página como lo haría un análisis solo de él, y el resultado se lee como un análisis de un solo perfil con las reglas de todos. Un único nombre, o una lista de uno, da exactamente el resultado de siempre, sin ninguno de los campos siguientes. Core 1.15.0 y posteriores.

  • checksResults: cada regla una vez. verdicts es el resultado de cada perfil para ella ({ 'wcag22-aa': 'cantTell', 'wai-aria-1.2': 'fail' }), por perfil, para los perfiles que la ejecutaron; outcome es el peor de ellos: fail, luego cantTell, luego pass, luego notApplicable. Un generador de informes que lee outcome cuenta el análisis como ese único perfil. checkOutcome es el resultado propio de la regla, antes de que ningún estándar juzgue sus hallazgos por código de motivo: lo que indican sus criterios de WCAG, ya que el fallo de un estándar en un hallazgo por el que WCAG solo pregunta no es un fallo de WCAG.
  • Cada ocurrencia figura una vez, con verdicts: lo que hace de ella cada perfil. Es su nivel, salvo que los resultados agregados de un estándar juzguen la regla por los códigos de motivo de sus hallazgos: entonces es lo que estos hacen de ella (un cantTell de WCAG del que un estándar está seguro es fail allí), o notApplicable donde el estándar no lo cuenta. Un hallazgo que falla en un perfil y es una pregunta para otro dice ambas cosas. occurrenceOutcome es el peor de ellos cuando difiere del de la comprobación.
  • rulesResults: los resultados agregados de todos los perfiles, cada uno una vez, con profiles, los perfiles que lo dan. Uno que dos perfiles dan con resultados distintos (dos versiones de WCAG, por ejemplo) es el peor de ellos.
  • profiles: por cada perfil que se ejecutó, { wcagVersion, wcagLevel, standards, rulesResults } (wcagLevel solo para un perfil que apunta a un nivel de WCAG), siendo rulesResults el resultado de cada uno de sus resultados agregados por id: lo que habría informado un análisis solo de ese perfil. Un perfil que nombra una ley también tiene law, como engine.law (experimental).
  • severity: la más alta que le da a la regla cualquier perfil que la hace fallar o pregunta por ella, sea cual sea el orden en que se enumeran los perfiles.
LectorQué hace con varios perfiles
SARIF, JUnitCondicionan según outcome: el fallo de cualquier perfil hace fallar la compilación. Un perfil pedido y no ejecutado es un error salvo con allowProfilesNotRun (SARIF, JUnit).
EARLAfirma cada regla frente a sus criterios de WCAG según checkOutcome, así que el fallo de un estándar nunca se afirma como uno de WCAG (Informe EARL).
El informe HTMLNombra cada perfil en su cabecera, y muestra el veredicto de cada perfil sobre un hallazgo en su tarjeta (wai-aria-1.2: Fail) junto a sus criterios de WCAG (Informe HTML).
BaselinesComparan los hallazgos por regla, código de motivo y marcado, como en cualquier resultado: un hallazgo es una entrada, sean cuales sean sus veredictos (Baseline).
BindingsPasan engineOptions tal cual; con frames, el resultado de cada frame es uno fusionado. Un perfil no ejecutado es un hueco de análisis profile-not-run.

Resultado entre frames (runa11yCoreAcrossFrames)

runa11yCoreAcrossFrames (consulta INTEGRATION.md) devuelve una estructura distinta y recursiva en lugar de un resultado de nivel superior simple:

{
topFrame: <the normal top-level result shape above>,
frames: Array<
| { url: string | null, selector: string | null, title: string | null,
topFrame: <top-level result>, frames: [...same shape, recursively] }
| { url: string | null, selector: string | null, title: string | null, error: string }
>
}
  • topFrame es exactamente la estructura del resultado de nivel superior, para el frame en el que se llamó a la función.
  • frames tiene una entrada por cada <iframe>/<frame> hijo directo dentro del ámbito analizado (incluidos los shadow roots abiertos, y el propio ámbito cuando es un frame), salvo un frame que la página no muestra y un frame que excludeSelectors excluye, que quedan fuera con todo su documento. Cada entrada nombra su elemento frame: selector es un selector CSS del <iframe>/<frame> en el documento del padre, y title es el atributo title del elemento, o null, para que se puedan distinguir frames que cargan el mismo documento. url es el documento que muestra el frame o, para un frame que aún no ha navegado, su src. Un hijo alcanzable (uno que llamó a a11yCoreEnableFrameResponder()) aporta su propio { url, topFrame, frames } completo: incluyendo sus propios frames anidados, de forma recursiva, ya que un nieto anidado más profundo solo es alcanzable a través de su padre inmediato. Un hijo inalcanzable (el caso común para la mayoría de los embebidos de terceros, sin responder cooperante o que agotó el tiempo de espera) aporta { url, error } en su lugar, y no aborta el resto del análisis.
  • Esto es un árbol, no una lista plana: una diferencia deliberada respecto a @surea11y/playwright's .frames(true), que sí puede aplanar porque page.frames() de Playwright ya entrega todos los frames sin importar la profundidad de anidamiento; un relé por postMessage no tiene esa visión global, así que el anidamiento se expresa de forma estructural en su lugar.
  • Los generadores de informes aceptan el árbol tal cual. renderHtmlReport, renderSarifReport, renderJunitReport, buildBaselineEntries, matchBaseline y renderEarlReport informan de todos sus frames: cada frame que respondió, con sus propios hallazgos, ubicados en su propio documento, y cada frame que no respondió, como no analizado, para que un hueco en la cobertura no se lea como un aprobado. Un frame se nombra por su ruta, los selector de los elementos frame que llevan hasta él desde la página, del más externo al más interno (#checkout → iframe:nth-of-type(2)). Lanzan un TypeError ante cualquier cosa que no sea ni un resultado de análisis ni un resultado entre frames (un array, null o undefined de un análisis que falló), en lugar de presentarlo como un análisis que no encontró nada.
  • También aceptan el análisis de frames de una integración. El .frames(true) de las integraciones de navegador devuelve { topFrame, frames } con cada frame como un resultado simple (o { url, error }) en lugar de una entrada anidada; los generadores de informes, getMargins y flattenCrossFrameResult leen cada uno de esos frames como analizado, nombrado por la ruta ['iframe'], ya que no lleva selector.
  • flattenCrossFrameResult(result), de @surea11y/core, enumera los frames tal como los leen los generadores de informes: [{ frame: { path, title, url }, result }], o error en lugar de result para un frame que no respondió, primero la página y cada frame antes de los frames que contiene. El path de la página es []. Un resultado de análisis simple es una sola entrada.

Un resultado de comprobación (checksResults[i])

{
ruleId: string,
outcome: "pass" | "fail" | "cantTell" | "notApplicable",
outcomeNormalized: "pass" | "fail" | "cantTell" | "inapplicable",
severity: "minor" | "moderate" | "serious" | "critical",
ruleSeverity?: "minor" | "moderate" | "serious" | "critical", // the rule's own, when a profile gave it another
verdicts?: { [profile: string]: string }, // a scan of several profiles only: each profile's outcome
checkOutcome?: string, // a scan of several profiles only: the rule's own outcome, which its WCAG criteria read
policyOutcome?: "pass" | "fail" | "cantTell" | "notApplicable", // what the rule found, when the policy made it cantTell
confidence: "high" | "medium" | "low",
type: "automatic" | "manual",
occurrences: Occurrence[],
title: string,
description: string,
i18n: { titleKey: string, descriptionKey: string, variantOfTitleKey?: string } | null,
meta: {
ruleId: string,
ruleInterfaceVersion: string,
ruleVersion: string,
normative: boolean,
atomic: boolean,
deprecated: boolean,
deprecation: object | null,
category: "perceivable" | "operable" | "understandable" | "robust" | null,
helpUrl: string, // where to read how to fix it; "" when there is none
tags: string[], // the rule's tags, its own and the engine's
normativeMappings: Array<{ standard: string, version: string, requirement: string, title: string,
conformanceLevel?: string, wcagSc?: string[], url?: string, understandingUrl?: string }>,
standard: string | null,
applicability: string,
expectation: string,
references: string[],
requirements: object | null,
mappings: object | null
},
engineOptions: object, // the resolved engineOptions this rule actually ran under
schemaVersion: string,
rollupIds: string[], // the rulesResults entries that group this rule in this run; [] if none
variantOf?: string, // a pack's variant only: the id of the rule it runs at other settings; i18n.variantOfTitleKey is that rule's titleKey
data?: object, // page-level data a rule reports whatever its outcome; see below
margin?: { // the closest a measurement came to its threshold while meeting it; see below
measure: string, // "contrast-ratio" | "overflow-px" | "target-size-px" | ... (open set)
unit: "px" | "ratio", // open set
limit: "min" | "max",
threshold: number,
value: number,
headroom: number, // >= 0
measuredCount: number,
selector?: string,
shadowHostSelectors?: string[], // as on an occurrence
structuralPath?: number[],
context?: object // rule-specific; not a stable contract
},
wcagVersionScope?: { // present only when the target WCAG version changed this outcome
target: "2.0" | "2.1" | "2.2",
removedSc: string[],
coercedFrom: "fail",
by?: "errata" // 2.0 or 2.1: their errata count the criterion as always satisfied
},
ancestorWalkLimit?: { // present only when a failed element is nested deeper than the engine walks up from it
limit: number, // 10000: the ancestors read, the element included
occurrences: number, // the fail-tier occurrences made cantTell-tier
coercedFrom?: "fail" // no fail-tier occurrence was left, so the outcome went from fail to cantTell
},
error?: string // present when the rule did not complete, or the engine noted a change — see below
}

Notas:

  • outcome frente a outcomeNormalized: idénticos, salvo que notApplicable se convierte en "inapplicable" en outcomeNormalized. Se ofrecen ambos para que puedas ajustarte a tu propio vocabulario o al vocabulario interno del motor.
  • Con la política predeterminada a11y, las reglas type: "manual" devuelven cantTell en lugar de fail. Si la lógica propia de una regla manual hubiera dicho fail, el motor la fuerza a cantTell y añade una nota explicativa a error: esto se aplica de forma centralizada (policy.coerceManualFailToCantTell, activado por defecto bajo el contrato de política a11y; consulta POLICY.md), no es algo que cada regla tenga que recordar por su cuenta. Con esta política, fail está reservado únicamente para hallazgos deterministas de type: "automatic".
  • meta.normativeMappings es cómo un resultado de comprobación se vincula de vuelta a un criterio de éxito de WCAG. Una lista vacía significa que la regla no tiene un mapeo formal en este resultado. Eso no indica si la regla es automática o manual: para eso, lee type (aria-allowed-role, por ejemplo, es automática y no tiene mapeo a WCAG). Consulta Conformidad para ver cómo se agregan. La lista no es solo de WCAG. Cuando el análisis pide otro estándar (engineOptions.mappings, o el perfil de un estándar), cada criterio de WCAG va seguido del requisito correspondiente de ese estándar, como la cláusula de EN 301 549 que lo reformula ({ standard: "EN 301 549", version: "V3.2.1" | "V4.1.1", requirement: "9.1.1.1", title, wcagSc: ["1.1.1"] }, una entrada por cada versión que incluye el criterio); y una regla también puede citar los documentos Understanding de WCAG (type: "Understanding"). Filtra por standard (y por la ausencia de type) antes de leer requirement como un criterio de éxito. La entrada de WCAG de un compuesto va siempre primero. Ver EN 301 549.
  • rollupIds enumera los compuestos de rulesResults que agrupan esta regla en esta ejecución, incluidos los que se informan porque la selección nombró la regla por sí sola. Una lista vacía significa que los hallazgos de la regla no aparecen en ningún resultado agregado, así que quien lea solo rulesResults nunca los ve; heading-order, por ejemplo, no pertenece a ningún resultado agregado de WCAG.
  • wcagVersionScope: presente solo cuando la versión de WCAG objetivo de la ejecución convirtió el fail de esta regla en un cantTell: hoy eso significa una regla mapeada al SC 4.1.1 Parsing (duplicate-id): bajo 2.2, que eliminó ese criterio, y bajo 2.0 y 2.1, cuyas erratas de 2023 lo consideran siempre satisfecho para HTML y XML (by: "errata"). Un perfil cuyo estándar conserva el texto de 4.1.1 (en301549-v3.2.1, o el perfil de un pack con keepsParsing) no se convierte. removedSc enumera los criterios afectados, target es la versión de la ejecución, y coercedFrom es el outcome que la propia regla devolvió. Las occurrences son las propias de la regla, sin cambios: no se descartó nada, solo cambió el veredicto de conformidad. Ausente en cualquier otro resultado, y nunca se indica a través de error: no salió nada mal. Consulta Filtrado por versión de WCAG.
  • ancestorWalkLimit (core 1.16.0 y posteriores): el motor lee todos los ancestros de un elemento, a través de shadow roots y slots, para saber si algo a su alrededor lo oculta, hasta 10.000 de ellos: mucho más hondo de lo que renderiza ningún navegador. Un fallo en un elemento anidado a más profundidad no es seguro, ya que un elemento que el motor no leyó puede ocultarlo, así que su ocurrencia pasa al nivel cantTell, conservando su código de motivo, con uncertainty.code: "not-computable" y pasos propios del motor que preguntan si el elemento está expuesto. El veredicto de la regla sobre cada uno de los demás elementos se mantiene, así que el resultado sigue siendo fail mientras quede una ocurrencia de nivel fail, y pasa a cantTell en caso contrario (coercedFrom: "fail"). Como wcagVersionScope, no se indica a través de error: la regla terminó.
  • data: presente solo en una regla que informa algo sobre la página entera, sea cual sea su resultado. Ninguna regla de core lo hace hoy. Como data.details en una ocurrencia, no es un contrato estable.
  • margin: presente solo en una regla que declara uno en el catálogo (getChecksCatalog()[i].margin, { measure, unit, limit, band? }), cuando al menos un elemento alcanzó el umbral de la regla y, si la declaración tiene un band, quedó dentro de él. band.relative es una fracción del umbral: contrast-minimum, contrast-enhanced y link-in-text-block, con 0.1, solo indican un margen para una razón a menos del 10 % de la que necesitaba (de 4,5 a 4,95 frente a 4,5:1, de 3 a 3,3 frente a 3:1). band.absolute es una cantidad en unit: target-size-minimum, con 6, solo indica un objetivo de hasta 30 px, y text-spacing-content-loss, con 8, una línea solo desde 6 px dentro del borde de su caja. Un texto a 21:1 o un botón de 48 px no está cerca de nada, y ningún margen lo dice. Indica el elemento que más se acercó al umbral cumpliéndolo, de modo que un pass a 4,52:1 frente a 4,5:1, o a 0,4 px de cortar el texto, se distingue de uno holgado. Va junto al resultado y no es un hallazgo: nunca añade, quita ni cambia una ocurrencia, y las líneas base, SARIF, JUnit y EARL lo ignoran. También se indica en resultados fail y cantTell cuando algunos elementos pasaron. limit es min cuando el valor debe alcanzar el umbral (contraste, tamaño) y max cuando debe quedar por debajo (desbordamiento); headroom es cuánto margen queda, en unit, es decir, value - threshold para min y threshold - value para max, nunca negativo. threshold es el umbral con el que se juzgó ese elemento, que puede variar entre elementos (3:1 para texto grande). Los píxeles se dan con un decimal: los píxeles CSS y umbrales como la mitad de una fuente de 15px son fraccionarios, y una interfaz los redondea para las personas. Una razón no se redondea, ya que WCAG no redondea el contraste. En caso de empate gana el primer elemento en el orden del documento, así que una página da siempre el mismo margen. measure y unit son conjuntos abiertos que pueden crecer en una versión menor; context es específico de cada regla y, como data.details, no es un contrato estable. getMargins(result) devuelve todos los márgenes de un resultado como [{ ruleId, ...margin }], ordenados por ruleId; de un resultado entre frames, los de todos los frames, los de un frame hijo con frame, la ruta hasta él.
  • error: presente cuando la regla no terminó, o cuando el motor cambió o ignoró parte de lo que devolvió la regla. Una regla que lanzó una excepción (o devolvió algo que no es un resultado) siempre aparece como outcome: "cantTell" con occurrences: [] y error establecido al mensaje de la excepción, así que cantTell sin ocurrencias y con un error es la forma de reconocer una regla que no terminó; el motor nunca deja que una regla rota haga fallar todo el análisis. En los demás casos, error contiene las notas del motor, unidas con | después de cualquier error que la propia regla devolvió: la coerción de fail manual descrita arriba, un outcome o un severity fuera de su conjunto, un type distinto del de su meta, ocurrencias que no son objetos (se dejan fuera), un policyOutcome o un ruleSeverity que devolvió la regla (no se toman: el motor establece ambos), y un uncertainty.code fuera del conjunto cerrado. Esos resultados conservan sus ocurrencias: la regla terminó. (Antes de la 1.16.0, también se anotaba aquí un fail antes de cuyos elementos se detenía el recorrido de ancestros de 200 pasos; consulta ancestorWalkLimit más arriba.)
  • policyOutcome: presente cuando el allowedOutcomes de la política no permite el resultado que encontró la regla. El resultado es entonces cantTell, con el resultado propio de la regla aquí y sin error, para que los generadores de informes no lo lean como una regla que no terminó.
  • engineOptions en cada resultado es el objeto de opciones resuelto (después de aplicar los valores predeterminados de locale/contrast), no literalmente lo que pasaste: útil para confirmar qué vio realmente una regla dada, especialmente el locale resuelto y contrast.mode/contrast.rootCanvasFallback. contrast.rootCanvasFallback es blanco cuando quien llama no dio ninguno, y contrast.rootCanvasFallbackSet indica cuál: true para un color que dio quien llama, false para el predeterminado. Omite messages, los diccionarios con los que tradujo la ejecución (un archivo de locale aparte en el paquete para navegador, o tus propias cadenas): son datos, no un ajuste, y una copia en cada resultado hacía que un análisis ocupara decenas de megabytes. engine.locale indica qué diccionario se usó. Otras tres opciones se reflejan tal como las vieron las reglas, para que el resultado siga siendo datos simples que JSON.stringify y structuredClone (postMessage) puedan transportar: probes como la copia limitada que leen las reglas, customRules como [{ id }], una entrada por cada regla personalizada con id, sin sus funciones, y styleSheetTexts como [{ href }], una entrada por cada hoja dada, sin su texto, ya que el CSS de una página repetido en cada resultado se multiplicaba por el número de reglas.

Una ocurrencia (occurrences[i])

Un fail siempre tiene al menos una: un fail de una regla personalizada que no nombra ningún elemento recibe una en el elemento del documento (selector: "html", código de motivo FAIL_WITHOUT_OCCURRENCE). Normalmente presente solo cuando outcome es fail o cantTell: un resultado pass tiene occurrences: [], ya que este motor no enumera los elementos que pasó, solo los que señaló. Las reglas de contraste son la excepción: su pass lleva una ocurrencia que describe el análisis (cuánto texto era elegible y calculable), con un selector vacío, para que un pass sin texto se distinga de uno sobre una página llena de texto.

Un notApplicable de core no nombra ninguna occurrence. Cuando las reglas de contraste son notApplicable porque ningún texto tenía un fondo calculable, dicen cuánto texto examinaron en el propio data.details del resultado (eligibleTextCount, computableTextCount), y contrast-computable informa como cantTell de cada texto que no pudo medir: la diferencia entre «se comprobó, nada que señalar» y «no se pudo comprobar» es algo que este motor indica en lugar de ocultar. Una regla de un pack o personalizada que no tuvo nada que juzgar todavía puede adjuntar una única occurrence explicando por qué; una occurrence así describe el análisis, no un elemento, así que su selector está vacío. No interpretes occurrences.length como un recuento de violaciones sin comprobar antes outcome.

{
selector: string,
html: string,
structuralPath: number[] | null,
shadowHostSelectors?: string[],
summary: string,
hint: string,
i18n: { summaryKey: string, hintKey: string, params: object } | null,
occurrenceOutcome?: "fail" | "cantTell", // present when the rule graded its findings into tiers
verdicts?: { [profile: string]: string }, // a scan of several profiles only
uncertainty?: { // present only on a cantTell-tier occurrence
code: "not-computable" | "runtime-dependent" | "spec-only"
| "equivalence-unknown" | "judgement-required" | "out-of-scope",
needed?: string, // what would settle the question
evidence?: object // what the rule did establish, rule-specific
},
lean?: { // present only on a cantTell-tier occurrence, and only with evidence
toward: "pass" | "fail",
why: string, // localized: what in the page points that way
signals: { signal: string, value: string | number | boolean | null }[],
caution?: string, // localized: what could make it wrong
measured?: { agreed: number, cases: number, corpus?: string, coreVersion?: string },
i18n?: { whyKey?: string, cautionKey?: string, params?: object }
},
howToCheck?: { // present only on a cantTell-tier occurrence, when the rule says
steps: string[], // localized: what a person does, in order
passWhen: string, // localized: what they see if it passes
failWhen: string // localized: what they see if it fails
},
data?: { // absent on manual-review's page-level finding
visibilityFilter?: { eligible?: boolean, targetSet: string, accEligible: boolean | null, reasons: string[] },
details?: object // rule-specific, non-normative — see below
}
}
CampoSignificado
selectorUn selector CSS construido con el mejor esfuerzo posible para resolver de vuelta al elemento señalado (ver helpers.buildSelector en RULE_AUTHORING.md). Un id, un test id, un name o un aria-label se usa como ancla solo cuando ningún otro elemento del documento lo tiene, incluidos los elementos ocultos, excluidos y fuera del ámbito, ya que querySelector lo resuelve frente a todo el documento; si no, el selector es una ruta. No se garantiza que sea único en todas las estructuras de DOM: un elemento dentro de un shadow root se nombra dentro de su propio árbol (structuralPath lo sitúa), y la ruta de un elemento muy profundo se acorta desde arriba. En 255 páginas de prueba y reales, 15 de 39.857 selectores no nombraron exactamente su elemento, todos de esos tipos. Un valor de más de 256 caracteres tampoco sirve de ancla, así que un selector sigue siendo corto ponga lo que ponga la página en un atributo, y un carácter de control en un valor se escapa como lo lee CSS (\a para un salto de línea). La excepción es una regla cuyo hallazgo es un elemento ausente: page-title-present informa de un <title> ausente con el selector de <head>, donde le corresponde estar (<html> sin <head>), y un html de <title>(missing)</title>, que no está en la página. Ese html es una constante, así que la huella que alimenta se mantiene estable, pero no lo trates como marcado real sin comprobar antes que la regla devolvió algo que existe. Para un elemento dentro de un árbol shadow, selector se resuelve dentro de su shadow root, y shadowHostSelectors indica cómo llegar hasta él.
shadowHostSelectorsPresente solo para un elemento dentro de un árbol shadow: los selectores de los shadow hosts que llevan hasta él, del más externo al más interno, cada uno resuelto en el árbol que lo contiene, de modo que document.querySelector(s[0]).shadowRoot.querySelector(s[1])…shadowRoot.querySelector(selector) encuentra el elemento. Dos componentes con el mismo contenido dan el mismo selector y se distinguen aquí. Como selector, no forma parte de la identidad de un hallazgo. El informe HTML, SARIF (logicalLocations), JUnit y las entradas de baseline muestran ambos como una sola ubicación, los hosts y luego el selector unidos con >>> (#card >>> img).
htmlUn fragmento de outer-HTML del elemento señalado, cortado a 2.000 caracteres: útil para mostrar el elemento y, para un elemento dentro de un árbol shadow, para leerlo junto a shadowHostSelectors. Para <html>, <head> y <body>, que contienen la página entera, es solo la etiqueta de apertura (<html class="x">), así que un hallazgo a nivel de página conserva su identidad cuando cambia el resto del contenido.
structuralPathLa ruta de índices de hermanos del elemento señalado, desde documentElement hasta él (por ejemplo, [1, 0, 2]): [] si el elemento es documentElement, null si no pudo determinarse, lo que incluye todo elemento dentro de un árbol shadow (usa ahí shadowHostSelectors y selector). Un mecanismo de identidad de elemento más robusto que selector por sí solo: sobrevive a cambios en el DOM que una cadena de selector no soportaría (un cambio de id/clase, por ejemplo), a costa de no poder usarse como un selector CSS real.
summaryLegible para humanos, ya localizado ("This button has no accessible name.").
hintGuía de corrección legible para humanos, ya localizada.
i18nLas claves de traducción originales detrás de summary/hint, por si quieres volver a renderizarlas tú mismo en otro idioma sin volver a ejecutar el análisis. null si la ocurrencia no usó i18n basado en claves.
data.visibilityFilterPresente en la mayoría de las ocurrencias: por qué el motor consideró este elemento elegible (o no) según el modelo de elegibilidad que usó la regla. eligible es ese resultado (ausente en los hallazgos de nivel de página de bypass-blocks-present, html-lang-attr-present y page-title-present); targetSet indica qué modelo lo produjo ('dom': visibilidad de DOM/CSS en bruto, la mayoría de las reglas; 'acc': elegibilidad del árbol de accesibilidad). accEligible refleja eligible solo cuando targetSet es 'acc'; en caso contrario es null. reasons es una lista de códigos de exclusión legibles por máquina cuando eligible: false.
data.detailsDatos estructurados específicos de la regla (métricas calculadas, referencias resueltas), no normativos: útiles para construir una interfaz más rica o para depurar, pero nunca cambian lo que significan outcome/severity. La estructura varía según la regla; trátalo como contexto adicional de mejor esfuerzo, no como un contrato estable. La única excepción es data.details.reasonCode, que sí es estable: identifica cuál de los hallazgos de una regla es este, y junto con ruleId y html forma la huella sobre la que se indexan los baselines y SARIF. Una regla puede ganar un código de motivo nuevo en una versión menor, o dividir uno según su causa; consulta API_STABILITY.md. Los campos de tamaño y razón de un fallo de contraste (metrics.ratio, metrics.threshold, y fontSizePx, fontSizePt, fontWeight, isBold e isLargeText de typography) también son estables, para que un informe o un pack pueda saber qué tipo de texto falló, y también lo son element en los hallazgos de list-children-valid, parentElement en los de listitem-parent-valid y role en los de aria-role-name-present, que dicen qué tipo de lista o de widget falló. Desde core 1.16.0 también lo son los mismos campos de typography en un hallazgo de contrast-computable (el texto cuyo contraste no se pudo calcular; isLargeText según la definición de core, 24px, o 14pt y negrita), linkKind en los hallazgos de link-name-quality e identical-links-same-purpose (de qué está hecho el enlace: text, image, composite o svg), y messageKind en los hallazgos de live-region-announcements (el tipo de mensaje que la región declara llevar, leído de su rol y sus atributos de región viva, nunca de su texto: status, alert, log, progress o unknown). linkKind y messageKind pueden ganar valores en una versión menor. También lo es data.details.evidenceState (core 1.15.0 y posteriores): en un hallazgo que solo se ve en un estado en que la regla puso la página y del que la sacó antes de que el navegador pintara, nombra ese estado (text-spacing, en los hallazgos de text-spacing-content-loss sobre texto cortado, superpuesto o con el desplazamiento bloqueado), para que una herramienta pueda volver a poner la página en él con evidenceState() y mostrar el hallazgo en imagen, como hacen los .screenshots() de los bindings. La página de cada regla enumera los campos que llevan sus hallazgos, en “Qué informa un hallazgo”.
occurrenceOutcomeA qué nivel pertenece esta ocurrencia, en una regla que clasificó sus hallazgos en un nivel fail (con confianza) y un nivel cantTell (que necesita revisión). Una regla que solo informa un nivel lo omite, en cuyo caso el propio outcome del resultado es el nivel de la ocurrencia. occurrenceOutcome(check, occurrence), de @surea11y/core, da el nivel tal como lo lee cada generador de informes, en ambos casos; desde core 1.16.0 da 'pass' para una ocurrencia de una comprobación que se cumple, donde daba null. El resultado siempre concuerda con los niveles: cualquier ocurrencia de nivel fail lo hace fail, y un fail cuyas ocurrencias son todas de nivel cantTell es cantTell, así que SARIF, JUnit, EARL y las líneas base dan una sola respuesta. El motor resuelve una regla personalizada o de un pack que los devuelve en desacuerdo; con la política predeterminada, una regla manual nunca es fail. Los generadores de informes leen un resultado guardado antes de que el motor hiciera esto con la misma regla, bajo la política que registra su engineOptions, y vuelven a calcular sus resultados agregados de WCAG a partir de lo que leen.
uncertaintyPor qué no se pudo decidir este hallazgo (ver Códigos de incertidumbre más abajo).
leanHacia dónde apunta este hallazgo sin decidir, cuando la página da indicios de ello (ver Tendencias más abajo).
howToCheckCómo responde una persona a este hallazgo sin decidir (ver Cómo comprobarlo más abajo).

Códigos de incertidumbre

Un cantTell dice que el motor no decidió. uncertainty dice por qué, a partir de un vocabulario cerrado, para que un consumidor pueda ramificar según el motivo en lugar de analizar una cadena de resumen. Está presente solo en una occurrence de nivel cantTell: una de nivel fail estaría afirmando que la regla decidió y no decidió a la vez, así que el motor la omite. Una incertidumbre cuyo code no es uno de los códigos de abajo también se deja fuera, y el error de la regla nombra el código.

codeSignificadoForma típica
not-computableLa evidencia que la regla necesitaba no se pudo leer en este entorno.Una hoja de estilos de origen cruzado, un color de fondo que no resuelve a ningún valor, un src que no se resuelve.
runtime-dependentEl marcado no puede resolverlo porque un script lo decide en tiempo de ejecución.Un aria-controls que nombra un elemento que el widget crea al abrirse.
spec-onlyUna violación real de la especificación, pero el nombre, rol y valor expuestos sobreviven a ella, así que no se establece que ningún criterio de éxito haya fallado.Un atributo ARIA cuya ausencia la especificación cubre con un valor predeterminado.
equivalence-unknownDos cosas pueden o no cumplir el mismo propósito, y ni el marcado ni el contenido lo resuelven.Dos frames que comparten un nombre accesible pero incrustan recursos distintos.
judgement-requiredLa pregunta es, por naturaleza, una decisión humana.Si un objetivo de tamaño insuficiente es esencial; toda regla type: "manual".
out-of-scopeEl hallazgo es real, pero queda fuera del estándar al que apunta esta ejecución.Una regla mapeada únicamente a un criterio que la versión de WCAG objetivo eliminó.

needed indica, en una frase, qué resolvería la cuestión: lo que un revisor tiene que ir a comprobar. evidence lleva lo que la regla sí llegó a establecer, para que el revisor parta del trabajo del motor en lugar de repetirlo; su estructura es específica de cada regla y, como data.details, no es un contrato estable. En cuanto a code: pueden añadirse códigos nuevos en una versión menor, pero uno existente no cambia de significado, así que ramifica según los códigos que conoces y trata uno no reconocido como «necesita revisión» en lugar de como un error.

Toda regla automática que puede devolver cantTell lleva esto, y una prueba mantiene esa línea para que no pueda llegar una nueva sin ello. El código out-of-scope lo adjunta el motor, no una regla, en las mismas occurrences que producen un wcagVersionScope a nivel de resultado. Las reglas manuales no lo llevan: judgement-required ya es lo que significa type: "manual", así que repetirlo por occurrence no diría nada que el resultado no diga ya.

Tendencias

Un cantTell puede apuntar igualmente hacia un lado. lean indica hacia cuál, cuando la página da indicios de ello: toward es la respuesta que esos indicios hacen probable, why dice cuáles son, y signals enumera los hechos que leyó la regla, cada uno con un nombre en kebab-case y un valor simple, para que una herramienta pueda mostrarlos o ponderarlos. caution dice qué podría hacer que la tendencia se equivoque, cuando no queda claro por uncertainty.needed. El resultado sigue siendo cantTell: una tendencia es un consejo, no un veredicto, y nada de lo que lee resultados o niveles cambia por ella. LEANS.md es la guía para quienes leen informes y construyen herramientas: qué hallazgos tienen tendencia y por qué, cómo se mide una tendencia y hasta qué punto fiarse de ella.

lean solo está presente donde la regla tiene indicios. Su ausencia también es información: solo una persona puede juzgar este caso. Solo está presente en una ocurrencia de nivel cantTell, igual que uncertainty; el motor la descarta en una ocurrencia decidida, y descarta una mal formada (un toward distinto de pass o fail, o sin why) sin tocar el hallazgo. measured aparece cuando una tendencia se ha contado frente a al menos cinco casos de respuesta conocida: cuántas veces acertó (agreed) de cuántas (cases), de qué corpus salieron (corpus, con la fecha en que se leyó) y qué versión de core los contó (coreVersion). Los recuentos son por regla, código de motivo y dirección, a partir de los casos de prueba de las W3C ACT Rules, y el motor los lleva consigo, así que un resultado de cualquier punto de entrada los tiene. Una tendencia nunca contada, o contada en menos casos, no lo tiene; no hay ningún número de confianza. Ninguna lo tiene todavía: el primer recuento, en jsdom, vio 9 tendencias, ninguna en cinco casos.

why y caution están en el idioma del análisis; i18n guarda sus claves y parámetros, como hace el i18n de una ocurrencia con summary y hint. El propio motor da tendencia a un hallazgo: una regla mapeada únicamente a un criterio que la versión de WCAG objetivo eliminó, o que las erratas de 2023 consideran satisfecho, tiende a pass junto a su incertidumbre out-of-scope.

reviewGroupOf(check, occurrence), de @surea11y/core, indica a qué grupo de una revisión va una ocurrencia: 'fail', 'leanFail', 'leanPass' u 'open' (solo una persona puede juzgarla), los grupos del informe HTML. leaningFailures(result, { wcagOnly }) enumera los hallazgos que tienden a fallo, los que cuenta una puerta con leanFailAs (JUnit, SARIF).

Llegan tendencias nuevas en versiones menores, regla a regla, y una tendencia puede retirarse si la medición muestra que se equivoca demasiado a menudo: trata una tendencia como un consejo que puede estar o no, nunca como parte de la identidad de un hallazgo. No entra en la huella.

El mapeo de un estándar dirige los hallazgos por su data.details (ver Packs), y una tendencia queda fuera de él, así que skip-link copia ahí también la dirección de su tendencia (core 1.16.0, experimental hasta la 1.18.0): data.details.lean es fail o pass, lo mismo que lean.toward, y no aparece cuando el hallazgo no tiene tendencia.

Cómo comprobarlo

Un cantTell le pasa una pregunta a una persona. howToCheck le dice cómo responderla: steps, unas pocas acciones en orden, empezando por el elemento que nombra el hallazgo (“Ve con Tab hasta el elemento.”, “Observa si el indicador de foco se queda en él.”), y luego passWhen y failWhen, lo que ve si cumple y si falla, nombrando el criterio que incumple. Core los tiene para cada código de motivo de cada regla de core que puede devolver cantTell, guardados con los procedimientos para evaluar a mano cada criterio (publicados como los diccionarios de revisión, @surea11y/core/review/<locale>), y cada ocurrencia de nivel cantTell recibe la entrada de su código, en el idioma del análisis. El hallazgo de una variante (variantOf) recibe los de su regla base cuando no tiene propios. Una regla de un pack también puede escribirlos, en inglés, y los diccionarios de su pack pueden traducirlos (ver Packs); sus hallazgos los reciben cuando se conocen los packs, como los conocen runDomRulesInPage con engineOptions.packs y addHowToCheck(result, { packs }). No aparece en una ocurrencia decidida, en el hallazgo de una regla personalizada, en el de una que reemplaza a otra (una regla personalizada o de un pack en overriddenBuiltinIds), ni en el de una regla de un pack que no escribe ninguno.

Los pasos se añaden después del análisis, en Node, no los añade el motor en la página: el motor se inyecta en cada página y frame que abre un análisis, y no le sirven de nada mil frases que ninguna regla lee. runDomRulesInPage los añade a su resultado. Un resultado de runa11yCoreInPage ejecutado en una página (el de un binding, el de una extensión) vuelve sin ellos; addHowToCheck(result) devuelve una copia con ellos, y howToCheckFor(ruleId, reasonCode, locale) da los de un hallazgo. Ambos leen del disco los diccionarios de revisión de core, así que necesitan Node; un binding cuyo código se ejecuta en un navegador (Cypress) llama a applyHowToCheck(result, { en, fr }) de @surea11y/core/how-to-check con los diccionarios que importa de @surea11y/core/review/<locale>, y obtiene los mismos pasos (BINDING_AUTHORS_GUIDE.md muestra cómo lo hace un binding). El informe HTML (renderHtmlReport) los añade en Node a un resultado que llega sin ellos; en el navegador (surea11y.report.js) muestra los que lleva el resultado. Una herramienta que muestra un resultado en otro idioma que el del análisis pasa { locale } a addHowToCheck o applyHowToCheck (addHowToCheck(result, { locale: 'de' }), o una lista de locales, la preferida primero; renderHtmlReport no lo admite): los pasos se dan en ese idioma, incluidos los que ya estaban, con inglés para cualquier paso que le falte al locale.

Los pasos dan por hecho un navegador, sus herramientas de desarrollo (el árbol de accesibilidad), un teclado y, en algunos casos, un lector de pantalla. A diferencia de uncertainty.needed, que dice en una frase qué resolvería la cuestión, ellos dicen cómo resolverla. Como una tendencia, son una guía, no parte de la identidad de un hallazgo, y su redacción puede mejorar en cualquier versión.

Pasos que nombran el caso. Desde core 1.15.0 un paso, passWhen o failWhen puede nombrar lo que encontró su hallazgo: {{name|text}} se lee como el valor del hallazgo para name, de sus i18n.params o de sus data.details, y como text cuando no tiene ninguno. Así, un paso “Open the popup, which aria-haspopup says is a {{expectedRole|menu}}” se lee “…is a listbox” en un hallazgo cuyos detalles dan expectedRole: 'listbox', y “…is a menu” en howToCheckFor, que no tiene hallazgo, y en una página de procedimientos. Un valor es una línea de 120 caracteres como máximo, y viene de la página: muéstralo como texto, como hace el informe HTML. howToCheckFor(ruleId, code, locale, { values }) los rellena con los valores dados.

Las palabras propias de un estándar. El estándar de un pack puede dar sus propios pasos para un código de una regla de core (standard.review): un hallazgo por el que solo preguntan los perfiles del estándar los lee en lugar de los de core, en inglés, así que la pregunta de un análisis de WAI-ARIA nombra WAI-ARIA, no WCAG 4.1.2. Cuando también pregunta un perfil de WCAG, en un análisis de varios, el hallazgo lee los de core.

Un resultado compuesto (rulesResults[i])

Los compuestos agregan varias reglas atómicas en un solo criterio de éxito de WCAG (por ejemplo, wcag-1.1.1-non-text-content agrega 21 reglas atómicas). Con el perfil de un estándar que tiene resultados agregados propios, también están esos, con la redacción del estándar como title y su nombre como meta.standard. La estructura es la misma que la de un resultado de comprobación, con un data.details específico de compuestos:

{
ruleId: string, // e.g. "wcag-1.1.1-non-text-content"
outcome: "pass" | "fail" | "cantTell" | "notApplicable",
severity, confidence, type, title, description, meta, engineOptions, schemaVersion, // as on a check result, except that
// meta.normativeMappings names the criterion in a shorter form:
// { standard, requirement, level }
occurrences: [], // always empty — composites are rollups, not element-level findings
data: {
details: {
reasonCode: string, // e.g. "composite.rollup.fail.anyFail"
standard?: string, // a standard's own rollup only, with version and criterion
version?: string,
criterion?: string,
checksIds: string[], // every atomic ruleId it rolls up, custom rules mapped to its criteria included
customChecksIds?: string[], // the custom rules among them; absent when none
restatesWcag?: string[], // a standard's rollup that restates WCAG criteria (applicability: 'wcag'): those criteria
partial?: true, // a standard's requirement its rules don't check in full (partial: true)
contributors: Array<{ testId: string, outcome: string, severity?: string | null, custom?: true, notSelected?: true,
byReasonCode?: true, checkOutcome?: string, requirements?: string[] }>,
metrics: { failCount, cantTellCount, notApplicableCount, passCount, missingCount }
}
}
}

Precedencia de la agregación (determinista, en este orden): cualquier contribuyente fail → compuesto fail; si no, cualquier cantTell (o una regla contribuyente que no se ejecutó en absoluto, missingCount > 0) → compuesto cantTell; si no, todos los contribuyentes notApplicable → compuesto notApplicable, donde las reglas encuentran todos los casos a los que se aplica el criterio, y cantTell (partialCoverage) en caso contrario; si no, pass, donde las reglas comprueban todo lo que pide el criterio, y cantTell (partialCheck) en caso contrario. Todavía no hay ningún criterio de WCAG catalogado como comprobado por completo, así que hoy ningún resultado agregado de WCAG da pass. Consulta Conformidad para ver qué implica esto en una afirmación de conformidad global.

El resultado agregado propio de un estándar puede juzgar una regla por sus hallazgos en lugar de entera (ver Packs): su contribuyente indica entonces byReasonCode: true, outcome es el resultado de la regla en este resultado agregado (un fail cuyos hallazgos pertenecen todos a otro sitio es aquí un pass) y checkOutcome el propio de la regla. Cuando los hallazgos que cuentan para el resultado agregado indican a cuál de sus requisitos pertenecen, requirements los enumera, y el meta.normativeMappings del resultado agregado nombra esos para esa regla, no todos los requisitos que comprueba la regla.

Las reglas de un resultado agregado de WCAG son su propia lista y cada regla de engineOptions.customRules que se ejecutó y se asocia a uno de sus criterios (mediante meta.wcagSc o una entrada de WCAG en meta.normativeMappings), marcada con custom: true entre los contribuyentes y enumerada en customChecksIds, para que un lector vea cuándo una regla personalizada decidió el resultado. Los resultados agregados propios de un estándar conservan sus listas.

Un criterio comprobado en parte. Un análisis que nombra reglas en lugar de criterios (includeRuleIds: ['link-name-present']) también recibe los resultados agregados de los criterios que comprueban esas reglas, cuando la selección no los selecciona por sí misma (ver Qué criterios informa un análisis). La precedencia anterior los decide como a cualquier otro, así que una regla que falla hace fallar el criterio, y las reglas que el análisis no seleccionó impiden que pase. Cada una de ellas es un contribuyente con outcome: "missing" y notSelected: true, contado en missingCount como antes. Cuando son lo que hace cantTell al resultado agregado, su reasonCode es composite.rollup.cantTell.notSelectedChild, y el informe HTML dice «Comprobado en parte: 1 de 2 comprobaciones no se seleccionó para este análisis». composite.rollup.cantTell.missingChild sigue significando una regla que no se ejecutó por otro motivo, como una que excluye el perfil (engine.profileExcludes), que nunca se marca como notSelected.

reasonCodeResultadoCuándo
composite.rollup.fail.anyFailfailUn contribuyente falló.
composite.rollup.cantTell.anyCantTellcantTellNinguno falló, y uno es cantTell.
composite.rollup.cantTell.notSelectedChildcantTellNinguno falló ni es cantTell, y algunos no se ejecutaron porque el análisis no los seleccionó: comprobado en parte.
composite.rollup.cantTell.missingChildcantTellNinguno falló ni es cantTell, y algunos no se ejecutaron por otro motivo.
composite.rollup.notApplicable.allInapplicablenotApplicableTodos los contribuyentes son notApplicable, y encuentran todos los casos a los que se aplica el criterio.
composite.rollup.cantTell.partialCoveragecantTellTodos los contribuyentes son notApplicable, pero el criterio (o un requisito de un estándar marcado como partial) puede aplicarse a contenido que no buscan, así que no se ha demostrado que la página no tenga ninguno.
composite.rollup.cantTell.emptyCompositecantTellEl resultado agregado no tiene reglas.
composite.rollup.cantTell.partialCheckcantTellNinguno falló ni es cantTell, y no todos son notApplicable, pero las reglas solo comprueban parte de lo que pide el criterio (o un requisito de un estándar marcado como partial): el resto lo comprueba una persona.
composite.rollup.pass.otherwisepassEn cualquier otro caso.

Valores de outcome

ResultadoSignificado¿Puede aparecer en type: "manual"?
failLa regla informa de que no se cumple el requisito que comprueba. Consulta su mapeo, confianza y ocurrencias para interpretar el hallazgo.No, con la política predeterminada (se fuerza a cantTell)
passEl/los objetivo(s) aplicable(s) de la regla existen y ninguno fue señalado.Sí
cantTellRequiere juicio humano: ya sea genuinamente ambiguo o el hallazgo de carácter consultivo de una regla manual.Sí
notApplicableLa regla no encontró elementos a los que aplicarse en esta página/ámbito.Sí

En las reglas integradas de core, fail es, de forma intencional, el outcome más estrecho y de mayor exigencia: reservado para violaciones deterministas y normativas; la persecución de cobertura de reglas nunca debe diluir esto.

Valores de severity y confidence

  • severity: minor < moderate < serious < critical: la evaluación del autor de la regla sobre el impacto en el usuario, independiente de confidence. Un perfil puede dar a una regla otra gravedad (el severity de un perfil en un estándar o en la lista de comprobación de un pack): bajo ese perfil, el resultado muestra la del perfil, conserva la propia de la regla como ruleSeverity (presente solo entonces), y los resultados agregados toman la del perfil. En un análisis de varios perfiles, es la gravedad más alta que le da a la regla cualquier perfil que la hace fallar o pregunta por ella (la más alta de todas cuando ninguno lo hace), sea cual sea el orden en que se enumeran los perfiles.
  • confidence: low < medium < high: cuán seguro está el motor de que un veredicto fail/cantTell es correcto. Ambos son metadatos informativos para priorizar; ninguno cambia el significado de outcome.

Un fail no siempre tiene confidence: "high", y eso no es una contradicción. El outcome describe el procedimiento de decisión (resolvió la cuestión sin adivinar), mientras que confidence describe el modelo contra el que se tomó esa decisión. Un puñado de reglas automáticas deciden de forma determinista contra algo que en sí mismo es una aproximación (las tablas curadas de roles WAI-ARIA, los mapeos de roles nativos, un árbol de accesibilidad inferido a partir de marcado estático) y devuelven medium: aria-required-children, aria-prohibited-children, aria-required-parent, aria-allowed-attr, form-control-programmatic-label-present, identical-iframes-same-purpose, svg-image-text-alternative-present, video-poster-text-alternative-present, target-size-minimum y text-spacing-content-loss. confidence está presente en todo resultado, así que un consumidor que solo quiera los fallos más seguros puede filtrar directamente por él; policy.allowedConfidence no hará eso por ti, ya que un valor no permitido se sustituye por el defaultConfidence propio de la regla en lugar de cambiar el outcome (consulta POLICY.md).

Ejemplo resuelto

Analizando <img src="logo.png"> (sin alt) y <button></button> (sin nombre accesible), limitado a esas dos reglas mediante runOnly: { includeRuleIds: [...] } (ver Opciones del motor; desde la 1.10.0, un array simple de ids de regla hace lo mismo):

const result = runDomRulesInPage(
'https://example.test/',
null,
{},
{ includeRuleIds: ['img-alt-present', 'button-name-present'] }
);
{
"engine": {
"tag": "a11ycore",
"version": "1.14.0",
"schemaVersion": "1.0.0",
"locale": { "requested": "en", "resolved": "en", "reason": "ok" },
"wcagVersion": "2.2",
"environment": { "layout": false }
},
"url": "https://example.test/",
"title": "Example",
"timestamp": null,
"perfStats": null,
"contextSelector": null,
"contextMatch": null,
"checksResults": [
{
"ruleId": "button-name-present",
"outcome": "fail",
"severity": "serious",
"confidence": "high",
"type": "automatic",
"occurrences": [
{
"selector": "html > body > button",
"html": "<button></button>",
"structuralPath": [1, 1],
"summary": "This button has no accessible name.",
"hint": "Give the button visible text, or name it with aria-label, so screen reader users know what it does.",
"data": {
"visibilityFilter": { "eligible": true, "reasons": [], "targetSet": "acc", "accEligible": true },
"details": { "reasonCode": "name_missing" }
}
}
]
},
{
"ruleId": "img-alt-present",
"outcome": "fail",
"severity": "serious",
"confidence": "high",
"type": "automatic",
"occurrences": [
{
"selector": "html > body > img",
"html": "<img src=\"logo.png\">",
"structuralPath": [1, 0],
"summary": "This <img> has no alt attribute.",
"hint": "Add an alt attribute that says what the image shows, or alt=\"\" if the image is purely decorative.",
"data": {
"details": { "reasonCode": "TEXT_ALTERNATIVE_MISSING" },
"visibilityFilter": { "eligible": true, "reasons": [], "targetSet": "acc", "accEligible": true }
}
}
]
}
],
"rulesResults": [
{
"ruleId": "wcag-1.1.1-non-text-content",
"outcome": "fail",
"severity": "serious",
"title": "Non-text content: text alternatives",
"occurrences": [],
"data": {
"details": {
"reasonCode": "composite.rollup.fail.anyFail",
"checksIds": ["area-alt-present", "img-alt-decorative", "img-alt-quality", "..."],
"contributors": [
{ "testId": "area-alt-present", "outcome": "missing", "notSelected": true },
"...",
{ "testId": "img-alt-present", "outcome": "fail", "severity": "serious" },
"..."
],
"metrics": { "failCount": 1, "cantTellCount": 0, "notApplicableCount": 0, "passCount": 0, "missingCount": 20 }
}
}
},
{
"ruleId": "wcag-4.1.2-name",
"outcome": "fail",
"severity": "serious",
"title": "Name, role, value: accessible name",
"occurrences": [],
"data": {
"details": {
"reasonCode": "composite.rollup.fail.anyFail",
"metrics": { "failCount": 1, "cantTellCount": 0, "notApplicableCount": 0, "passCount": 0, "missingCount": 23 }
}
}
}
],
"overriddenBuiltinIds": [],
"skippedCustomRules": []
}

(Recortado por legibilidad: el resultado real también incluye title/description/i18n/meta/engineOptions/schemaVersion en cada entrada, según la estructura completa mostrada arriba, y cada contribuyente de cada resultado agregado. rulesResults contiene los criterios que comprueban las dos reglas nombradas: 1.1.1 para img-alt-present y 4.1.2 para button-name-present, cada uno fail porque su regla falló. Las demás reglas de esos criterios no se seleccionaron, así que son missing con notSelected: true; sin una regla que falle, los criterios serían cantTell (notSelectedChild), comprobados en parte. El rollupIds de cada resultado de comprobación nombra su resultado agregado.)