Opciones del motor
Todos los ejecutores (runDomRulesInPage, runa11yCoreInPage) reciben los mismos cuatro argumentos: (pageUrl, contextSelector, engineOptions, runOnly). Esta página documenta engineOptions y runOnly en su totalidad.
Seleccionar qué reglas se ejecutan
Hay dos formas independientes de seleccionar reglas: el cuarto argumento (runOnly), o bien engineOptions.rules/.tags/.tests/.includeMode. Si runOnly contiene algún filtro, tiene prioridad; de lo contrario, el motor recurre a engineOptions. No combines ambos mecanismos: no se aplican simultáneamente. Elige uno.
Mediante runOnly (4.º argumento)
runDomRulesInPage(url, null, {}, {includeRuleIds: ['img-alt-present', 'button-name-present'],excludeRuleIds: ['region'],tags: ['wcag412'],excludeTags: ['best-practice'],includeMode: 'and' // 'and' (default) | 'or' — see below});
runOnly debe tener esta forma de objeto, no ser un array simple. runOnly: ['img-alt-present'] (un array simple) se ignora silenciosamente; el motor ejecuta todas las reglas en su lugar. Este es el error de integración más común con diferencia. Consulta las preguntas frecuentes.
| Campo | Tipo | Significado |
|---|---|---|
Cada uno de estos acepta un array o una cadena separada por comas, igual que la forma de engineOptions de más abajo: includeRuleIds: 'img-alt-present, button-name-present' y includeRuleIds: ['img-alt-present', 'button-name-present'] son equivalentes.
Los IDs de regla van sin prefijo (sin prefijo del motor), por ejemplo 'img-alt-present'. Por compatibilidad con versiones anteriores, la coincidencia también acepta una forma heredada con el prefijo a11ycore- del mismo id ('a11ycore-img-alt-present').
También se acepta una forma heredada de filtro por etiqueta como valor completo de runOnly: { type: 'tag', values: ['wcag2a', 'wcag2aa'] }, equivalente a { tags: ['wcag2a', 'wcag2aa'] }.
Filtrado por versión de WCAG (2.0, 2.1, 2.2)
Cada regla y compuesto lleva exactamente una etiqueta de nivel de versión de origen de WCAG: wcag2a/wcag2aa/wcag2aaa para un Criterio de Éxito (SC) que sea línea base de WCAG 2.0, wcag21a/wcag21aa/wcag21aaa para uno introducido en WCAG 2.1 (por ejemplo, 1.3.5 Identify Input Purpose), wcag22a/wcag22aa/wcag22aaa para uno introducido en WCAG 2.2 (por ejemplo, 2.5.8 Target Size Minimum). Una regla obtiene solo la etiqueta de la versión de origen real de su criterio de éxito: un criterio de éxito introducido en 2.1 nunca lleva también la etiqueta wcag2aa, ya que no existe bajo un objetivo de conformidad de WCAG 2.0.
Dado que las versiones son acumulativas (2.1 = 2.0 + nuevo; 2.2 = 2.0 + 2.1 + nuevo), selecciona un objetivo de conformidad de versión de WCAG combinando conjuntos de etiquetas: la coincidencia OR del motor sobre tags (cualquier coincidencia incluye la regla) hace el resto:
// WCAG 2.0 AA only (excludes every 2.1/2.2-introduced SC, even at level AA):{ tags: ['wcag2a', 'wcag2aa'] }// WCAG 2.1 AA conformance (2.0 baseline + everything 2.1 added, both at A and AA):{ tags: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'] }// WCAG 2.2 AA conformance (2.0 baseline + 2.1 additions + 2.2 additions):{ tags: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22a', 'wcag22aa'] }// Just the SCs 2.2 introduced, nothing else:{ tags: ['wcag22a', 'wcag22aa', 'wcag22aaa'] }
Un criterio de éxito va en sentido contrario. WCAG 2.2 eliminó el criterio de éxito 4.1.1 Parsing: el único criterio que se ha eliminado alguna vez en lugar de añadirse. Una regla mapeada a él lleva su etiqueta de origen 2.0 (wcag2a) como cualquier otra regla de línea base, más wcag22-removed, y por eso los conjuntos de etiquetas de versión de arriba la incluyen bajo un objetivo 2.2, donde no pertenece.
No tienes que hacer nada al respecto. El motor resuelve una versión objetivo de WCAG para cada ejecución y, cuando ese objetivo es 2.2, una regla wcag22-removed no puede informar fail: sigue ejecutándose, sigue informando cada ocurrencia que encontró, pero su resultado se fuerza a cantTell y el resultado lleva un campo wcagVersionScope que explica por qué (consulta el esquema de salida). Nada se descarta silenciosamente, y una ejecución 2.2 no queda bloqueada por un criterio que 2.2 no contiene.
La versión objetivo se resuelve en este orden:
engineOptions.wcagVersion:'2.0','2.1'o'2.2', si lo configuras.- Las etiquetas de origen de versión en tu propio filtro: un conjunto que llega como máximo hasta
wcag21a/wcag21aase interpreta como objetivo 2.1, uno que contiene cualquierwcag22*etiqueta como 2.2, uno con solo etiquetaswcag2*como 2.0. Solo cuentan esas nueve etiquetas: una etiqueta de criterio de éxito (wcag411) obest-practiceno dice nada sobre una versión. - En caso contrario
'2.2', el objetivo predeterminado de este motor.
// Nothing to declare: a plain run already targets 2.2, so a duplicate id// comes back cantTell rather than fail.runDomRulesInPage(url, null, {}, null);// Conformance-testing against 2.1, where SC 4.1.1 still exists:runDomRulesInPage(url, null, { wcagVersion: '2.1' }, null);// Same thing, implied by the tag set — no extra option needed:runDomRulesInPage(url, null, {}, { tags: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'] });
La versión de destino resuelta se incluye en cada resultado como engine.wcagVersion, así puedes confirmar cuál usó realmente una ejecución.
Si prefieres no ver la regla en absoluto bajo 2.2, exclúyela directamente: la etiqueta está ahí precisamente para eso:
// WCAG 2.2 AA conformance, with the removed criterion left out entirely:{tags: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22a', 'wcag22aa'],excludeTags: ['wcag22-removed']}
duplicate-id es la única regla que lleva esa etiqueta hoy en día. Si se deja, sigue informando algo real (un id duplicado rompe <label for>, los enlaces de fragmento y getElementById diga lo que diga el estándar), simplemente no es un fallo de conformidad 2.2.
Mediante engineOptions (sin runOnly)
El mismo filtrado, expresado como cadenas separadas por comas (o arrays) anidadas en engineOptions:
runDomRulesInPage(url, null, {rules: { include: 'img-alt-present, button-name-present', exclude: 'region' },tags: { include: 'wcag412', exclude: 'best-practice' },includeMode: 'and'}, null);
rules.include/.exclude, tags.include/.exclude, tests.include/.exclude (alias de rules), y el includeMode de nivel superior reflejan exactamente los campos de runOnly de arriba. Las cadenas separadas por comas se recortan, se eliminan duplicados y los tokens vacíos se descartan automáticamente.
engineOptions: el resto
const engineOptions = {locale: 'en', // default 'en'; de-DE falls back to de, then to en per stringwcagVersion: '2.2', // default '2.2' — the conformance targetmessages: { de: { /* key: text */ } }, // optional caller-supplied dictionaries; win over built-in onesincludeHiddenElements: false, // default false — set true to evaluate hidden/collapsed subtrees tooincludeShadowDom: true, // default true — opt OUT with false to skip open shadow rootsfragment: false, // default false — set true when the scan target isn't a real pageexcludeSelectors: ['#cookie-banner', '.third-party-widget'], // array or comma-separated stringtimestamp: '2026-07-20T12:00:00Z', // optional — engine has no built-in clockperfStats: false, // default false — internal timing counters, debug-only shapeprofileRules: false, // default false — per-rule timings; needs perfStatscontrast: {mode: 'strictConformance', // 'strictConformance' (default) | 'auditorAssist'rootCanvasFallback: '#ffffff' // background assumed when the true root background isn't computable},visibilityMode: 'styleOnly', // 'styleOnly' (default) | 'styleAndGeometry' — scoped to the contrast rules onlypolicyContract: 'a11y', // 'a11y' (default) | 'generic' | inline contract objectpolicy: { // optional overrides on top of policyContractcoerceManualFailToCantTell: true},output: {includeSelector: true, // set false to suppress auto-filled selectorsincludeHtml: true},rules: {'some-rule-id': {excludeSelectors: ['.some-noisy-widget'] // narrows candidates for THIS rule only}},probes: { /* optional host-supplied evidence */ },customRules: [ /* runtime-registered rules, see below */ ],// Only read by runa11yCoreAcrossFrames. Ignored by runDomRulesInPage/runa11yCoreInPage.pingWaitTime: 500, // ms to wait for a child frame to answer a pingframeWaitTime: 60000 // ms to wait for a child frame's full scan result};
| Opción | Significado |
|---|---|
excludeSelectors por regla
El excludeSelectors de nivel superior se aplica a todas las reglas: no hay forma de excluir un elemento de una sola regla mientras se siguen ejecutando todas las demás reglas sobre él. rules[ruleId].excludeSelectors llena ese vacío: reduce los candidatos para solo esa regla, además de (nunca en lugar de) la lista global.
const engineOptions = {excludeSelectors: ['#cookie-banner'], // applies to every rule, as alwaysrules: {'aria-required-children': {excludeSelectors: ['mat-select', 'mat-stepper', 'mat-horizontal-stepper', 'mat-vertical-stepper']},'aria-allowed-attr': {excludeSelectors: ['mat-progress-spinner']}}};
Por qué querrías esto: el <mat-select> de Angular Material construye su estructura ARIA interna de una manera que dispara un falso positivo específicamente en aria-required-children, aunque el componente por lo demás esté bien. Con solo el excludeSelectors global, la única forma de silenciar ese falso positivo es excludeSelectors: ['mat-select']: lo cual también oculta mat-select a todas las demás reglas, incluidas contrast-minimum y aria-allowed-attr, eliminando silenciosamente cobertura real con la que esas comprobaciones nunca tuvieron problema. El ejemplo de arriba mantiene mat-select completamente visible para todas las reglas excepto la que falla incorrectamente con él.
Las exclusiones efectivas para una regla dada son la unión de la lista global y la lista propia de esa regla: un elemento que coincide con cualquiera de las dos se elimina de los candidatos de esa regla. Una regla cuyos únicos elementos que fallarían quedan todos excluidos de esta manera informa outcome: 'pass' o 'notApplicable' (siguiendo la convención propia de esa regla para el caso sin candidatos), con occurrences: []; nunca outcome: 'fail' con un array occurrences vacío, ya que esa forma exacta está reservada en otra parte del esquema para significar "esta regla lanzó un error" (consulta el esquema de salida).
Acepta las mismas formas que la opción global: un array (['mat-select', 'mat-stepper']) o una cadena separada por comas ('mat-select, mat-stepper').
Si estás usando un paquete de integración (binding) (@surea11y/binding-base y sus wrappers de Playwright/Puppeteer), consulta el propio README de esa integración para ver si su método constructor .exclude() ya tiene una forma por regla: esta es una forma de engineOptions documentada aquí a nivel del motor; no todas las integraciones la han incorporado todavía.
Recetas: combinar opciones para escenarios reales
Puerta de CI: solo WCAG 2.2 AA, ignorando un widget de terceros que no controlas
runDomRulesInPage(url, null, {excludeSelectors: ['#cookie-banner', '.intercom-launcher'],tags: { include: 'wcag2a,wcag2aa,wcag21a,wcag21aa,wcag22a,wcag22aa' }}, null);
Auditor humano haciendo una pasada de contraste en profundidad en un navegador real: sacrifica algo de protección contra falsos positivos a cambio de más hallazgos, y comprueba el diseño real (no solo el estilo calculado) ya que se está controlando una página real. Se muestra con el page.evaluate de Puppeteer (acepta múltiples argumentos); si estás en Playwright, primero envuelve los cuatro argumentos posicionales en un solo objeto; consulta INTEGRATION.md:
const result = await page.evaluate(runa11yCoreInPage, url, null, {contrast: { mode: 'auditorAssist' },visibilityMode: 'styleAndGeometry'}, null);
Reanálisis delimitado de una región tras un cambio de interfaz, omitiendo el shadow DOM: útil en una prueba a nivel de componente donde solo te importa el widget que acabas de cambiar:
runDomRulesInPage(url, '#checkout-form', {includeShadowDom: false}, { includeRuleIds: ['form-control-programmatic-label-present', 'button-name-present'] });
Salida reproducible para pruebas de snapshot: fija un timestamp para que dos ejecuciones del mismo HTML produzcan un JSON idéntico byte a byte, y solicita el desglose de tiempos de depuración:
runDomRulesInPage(url, null, {timestamp: '2026-01-01T00:00:00Z',perfStats: true,profileRules: true}, null);
Una regla personalizada, específica de la organización, junto a las integradas, solo para esta llamada:
runDomRulesInPage(url, null, {customRules: [{id: 'org-no-inline-onclick',meta: { title: 'No inline onclick handlers', defaultSeverity: 'moderate' },runInPage(ctx) {const els = ctx.helpers.queryAll('[onclick]');const occurrences = els.map((el) => ({selector: ctx.helpers.buildSelector(el),html: el.outerHTML,summary: 'Inline onclick handler found.',hint: 'Move event handling into an external script.'}));return { ruleId: ctx.rule.ruleId, outcome: occurrences.length ? 'fail' : 'pass', severity: 'moderate', occurrences };}}]}, null);
customRules: reglas registradas en tiempo de ejecución
Todas las reglas distribuidas están incorporadas en src/core.js en tiempo de compilación. engineOptions.customRules es la vía de escape en tiempo de ejecución: un array de descriptores de regla registrados solo para esa llamada; no se añade nada al catálogo estático, y nada persiste entre llamadas. Esto es deliberado, no una limitación que sortear: surea11y ya toma engineOptions nuevas en cada llamada sin configuración global mutable (a diferencia de otros motores, que necesitan un paso de configure()/reset() sobre un runtime compartido), y las reglas personalizadas siguen ese mismo modelo por llamada.
Llamar a la biblioteca directamente es una forma de acceder; la CLI también expone esto vía --custom-rules <path> (un archivo local, cargado una vez por análisis). Consulta la documentación de la CLI.
Un descriptor tiene la misma forma que la propia exportación de un módulo de regla interno: si ya sabes cómo escribir un archivo de regla para este motor, ya conoces esta API:
{id: 'my-org-custom-rule', // requiredmeta: { title, description, tags, defaultSeverity, defaultConfidence, /* same fields as a rule module's meta */ },runInPage(ctx) { /* same ctx shape and same return contract as any built-in rule */ },applicability(ctx) { return true; }, // optional, same contract as a built-in rule's applicabilitydata: { /* optional, JSON-serializable */ }}
runInPage/applicabilitypuede ser una función real o una cadena con el código fuente de la función (es decir,fn.toString()). Pasa una función real cuandoengineOptionsnunca sale del ámbito de JS actual (uso normal en Node/jsdom). Pasa una cadena cuando sí lo hace: por ejemplo, una llamada de Playwrightpage.evaluate(runa11yCoreInPage, { engineOptions }), dondeengineOptionscruza un límite de JSON/clonación estructurada que no puede llevar una referenciaFunctionviva pero sí puede llevar una cadena. El motor reconstruye una cadena víanew Function, el mismo mecanismo que usa la compilación para incrustar el código fuente de cada regla integrada en el ejecutor dentro de la página.metarecibe los mismos valores predeterminados/validación que una regla en tiempo de compilación: omite lo que no necesites;severitytiene como predeterminadomoderate,confidencetiene como predeterminadomedium,typetiene como predeterminadoautomatic, etc.- Una regla personalizada cuyo
idcoincide con el de una regla integrada la sobrescribe para ese análisis, en lugar de ejecutar ambas. Dado que una regla personalizada con el mismo nombre es tan probable que sea una colisión accidental como una sobrescritura deliberada, cada colisión se muestra de dos formas: unconsole.warnque nombra el/los id(s), y un arrayoverriddenBuiltinIdsde nivel superior en el resultado (vacío cuando no hay colisión). Consulta el esquema de salida. - Un descriptor inválido (
idausente/no-string, o bien unrunInPageque no es una función ni una cadena de código fuente reconstruible) se omite silenciosamente: el resto del análisis, incluidas todas las reglas integradas, sigue ejecutándose con normalidad. Esto no es un vacío de validación por corregir: una regla personalizada es código arbitrario proporcionado por quien la llama, así que "fallar cerrado en esta sola entrada, sin abortar el análisis" es el comportamiento predeterminado más seguro, reflejando cómo una regla integrada que lanza un error queda contenida en uncantTellpara esa regla en lugar de hacer fallar toda la ejecución. - Los resultados aparecen en
checksResultsexactamente igual que los de cualquier otra regla, incluido el autocompletado automático deselector/html/structuralPathpara ocurrenciasfail/cantTellque solo adjuntan{ __node }(consulta el esquema de salida).
contextSelector (2.º argumento del ejecutor, no un campo de engineOptions)
Un selector CSS (o array de selectores) que delimita el análisis a uno o más subárboles, resuelto vía document.querySelectorAll (todas las coincidencias, no solo la primera), recurriendo a document.documentElement/document.body si nada coincide. Pasa null para analizar todo el documento.
- Una sola cadena puede ser en sí misma una lista de selectores separados por comas (semántica de unión CSS habitual):
'#a, #b'analiza tanto#acomo#b. - Un array de cadenas analiza la unión de las coincidencias de cada selector:
['#a', '.card']se comporta igual que'#a, .card'; la forma de array existe para quienes construyen la lista de forma programática. - Las regiones superpuestas/anidadas se deduplican automáticamente: un elemento alcanzable desde más de una raíz coincidente solo se informa una vez, no una vez por región.