Packs

Un pack aporta a @surea11y/core reglas, variantes de las reglas de core, un estándar o una lista de comprobación, junto con sus perfiles y mensajes, desde un paquete independiente, sin una bifurcación ni una compilación de core. Un estándar nacional como RGAA puede ser un pack, y también la política de accesibilidad de tu organización. Un análisis recibe los packs en engineOptions.packs, y los ejecuta igual que las propias reglas y estándares de core: en Node y en un navegador, desde la CLI, las integraciones y los test matchers, con resultados, resultados agregados y entradas de informe con el mismo formato que las de core. Los packs necesitan @surea11y/core 1.11.1 o posterior.

Qué tipo de pack

TienesTipoQué contiene
La política de accesibilidad de una organización: «WCAG 2.2 AA, más estas reglas, menos esta otra, con estos elementos en nuestro informe»lista de comprobaciónPerfiles que eligen reglas de core y reglas propias, y resultados agregados como elementos de la lista de comprobación.
Un estándar con requisitos propios (un estándar nacional como RGAA, uno de un sector): requisitos numerados, cada uno comprobado por algunas reglasestándarUna tabla de requisitos, las reglas que comprueban cada requisito, un resultado por requisito y perfiles.
Unas cuantas reglas que añadir a cada análisis, sin perfilsolo reglasReglas, y variantes o sustituciones de las de core. Parte de una lista de comprobación y elimina sus perfiles y resultados agregados.

Las reglas y los perfiles de un pack pueden usar cualquiera de las reglas de core: una lista de comprobación o un estándar consiste sobre todo en reglas de core, elegidas y agrupadas, más las pocas reglas que core no tiene. Los ejemplos de esta página usan acme-policy-pack.js, el pequeño pack que cada integración incluye en su carpeta examples/: una regla propia, un perfil de lista de comprobación (WCAG 2.2 A y AA, region y la regla propia) y dos elementos de lista de comprobación. Su código fuente está más abajo.

El pack de EN 301 549

@surea11y/pack-en301549 es un pack de estándar de la familia SureA11y, listo para instalar: el capítulo 9 (Web) de EN 301 549, en V3.2.1 y V4.1.1. Tiene un perfil por versión, en301549-v4.1.1 y en301549-v3.2.1, que informa un resultado por cláusula, y mappings: ['en301549'] para nombrar las cláusulas en los resultados de cualquier análisis, también uno de WCAG. Necesita @surea11y/core 1.13.0 o posterior.

Su versión 1.1.0, hecha sobre core 1.14.0, juzga cada cláusula como core juzga el criterio de WCAG que reformula. Así que todavía no pasa ninguna cláusula: donde todas sus reglas pasan, una cláusula es cantTell, para que una persona compruebe el resto, igual que su criterio (ver Mapeo de conformidad con WCAG). El perfil en301549-v3.2.1 mantiene el 4.1.1 Parsing, así que ahí un id duplicado sigue fallando la 9.4.1.1, y la 9.1.4.5, la 9.3.3.1 y la 9.4.1.3 tienen sus primeras reglas (image-of-text, invalid-field-error-message, live-region-announcements). Con core 1.13 se comporta como sus versiones 1.0.

// npm install @surea11y/core @surea11y/pack-en301549
const en301549 = require('@surea11y/pack-en301549');
 
runDomRulesInPage(url, null, { packs: [en301549], profile: 'en301549-v4.1.1' });
runDomRulesInPage(url, null, { packs: [en301549], profile: 'wcag22-aa', mappings: ['en301549:V4.1.1'] });

Es también un ejemplo completo de un estándar propio. No tiene reglas: el capítulo 9 reformula WCAG, así que cada cláusula la comprueban las reglas de core para su criterio. Sus perfiles eligen esas reglas (tags, mappedRules: true), indican la versión de WCAG con la que se juzga cada versión del estándar (wcagVersion) y, para V3.2.1, mantienen el 4.1.1 Parsing (keepsParsing). Sus cláusulas reformulan criterios de WCAG uno a uno (applicability: 'wcag'), y su mapa de reglas se deriva del catálogo de la versión de core instalada, así que una regla que core añade queda cubierta sin cambiar el pack. Un estándar con requisitos que WCAG no tiene usa la misma forma y añade reglas propias. Su código es breve y vale la pena leerlo junto a Escribir un pack, más abajo.

Core tuvo EN 301 549 integrado hasta la 1.13.0, que lo declara obsoleto en favor de este pack (ver Mapeo de conformidad con WCAG). Como cualquier perfil, los del pack eligen qué se ejecuta: una declaración de conformidad sigue necesitando que una persona revise cada cláusula.

Usar un pack

Desde Node

const { runDomRulesInPage, getChecksCatalog } = require('@surea11y/core');
const acmePolicy = require('./acme-policy-pack');
 
const result = runDomRulesInPage(url, null, { packs: [acmePolicy], profile: 'acme-web-2026' }, null);
result.engine.packs; // ['@acme/a11y-policy@1.0.0']
getChecksCatalog({ packs: [acmePolicy] }); // core's rules and the pack's

El pack se prepara una vez y se conserva para el siguiente análisis con el mismo objeto de pack, así que un rastreo que pasa los mismos packs a cada página los prepara una sola vez. definePack congela el objeto del pack: trátalo como inmutable y reutiliza el mismo objeto entre análisis. Pasar un pack lo registra para el análisis; no selecciona sus reglas. Una regla de un pack etiquetada con el espacio de nombres del pack es opcional: sin uno de los perfiles del pack, solo se ejecuta cuando se selecciona por id o por esa etiqueta (consulta Nombres). Una regla de un pack sin la etiqueta se selecciona igual que las reglas de core, así que un análisis predeterminado con el pack la ejecuta. Añade runOnly: { bestPractices: true } junto al perfil para ejecutar también las buenas prácticas.

Con una integración de navegador

Playwright, Puppeteer, Selenium y WebdriverIO reciben el objeto de pack en .withPacks(), y lo registran en la página, y en cada frame con .frames(true), antes del análisis:

const { A11yCoreBuilder, getScanGaps } = require('@surea11y/playwright');
const acmePolicy = require('./acme-policy-pack');
 
const results = await new A11yCoreBuilder({ page })
.withPacks(acmePolicy)
.options({ profile: 'acme-web-2026' })
.analyze();
 
results.engine.packs; // ['@acme/a11y-policy@1.0.0']
 
// A checklist's items, each one result, beside core's WCAG rollups:
results.rulesResults.filter((r) => r.meta && r.meta.standard === 'Acme web policy');

Pasa un array, o llama de nuevo a .withPacks(), para analizar con varios packs. Las integraciones comparten .withPacks(), pero cada builder recibe su propio objeto de driver ({ page }, { driver } o { browser }) y necesita su propia configuración: consulta Playwright, Puppeteer, Selenium y WebdriverIO.

Con Cypress

Un pack se prepara en Node, y una spec de Cypress se ejecuta en el navegador. Por eso, en Cypress, .withPacks() recibe el nombre de módulo de cada pack (un nombre de paquete, o una ruta desde la raíz del proyecto), y el plugin de la integración prepara el pack en Node. Registra el plugin una vez:

// cypress.config.js
const { defineConfig } = require('cypress');
const surea11y = require('@surea11y/cypress/plugin');
 
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
surea11y(on, config);
}
}
});
new A11yCoreBuilder()
.withPacks('./examples/acme-policy-pack.js') // or a package name, '@acme/a11y-policy'
.options({ profile: 'acme-web-2026' })
.analyze()
.then((results) => {
expect(results.engine.packs).to.deep.equal(['@acme/a11y-policy@1.0.0']);
});

.analyze() pide al plugin, mediante cy.task, el script que registra los packs, y lo ejecuta en la página, y en cada frame del mismo origen con .frames(true). El plugin conserva un pack durante el resto de la ejecución, así que un cambio en el archivo de un pack aparece en el siguiente cypress run, o tras reiniciar cypress open. Un objeto de pack, como el que reciben las demás integraciones, lanza un error en .withPacks(): no se puede preparar en el navegador.

Con los test matchers

@surea11y/test-matchers analiza en Node, así que un pack se pasa como el propio objeto:

const acmePolicy = require('./acme-policy-pack');
const policy = { packs: [acmePolicy], profile: 'acme-web-2026' };
 
test("a page that breaks the pack's house rule fails the assertion", () => {
document.body.innerHTML = '<main><h1>Welcome</h1><video autoplay src="intro.mp4"></video></main>';
expect(document).not.toHaveNoA11yViolations(policy);
});

Mantén los objetos de pack en un solo lugar (un archivo de configuración o un módulo), para que todas las pruebas pasen los mismos. Un pack que no se ejecutó hace fallar la aserción. Consulta test-matchers.

Desde la CLI

$ surea11y scan ./dist/index.html --pack ./acme-policy-pack.js --profile acme-web-2026
$ surea11y scan ./dist/index.html --pack @acme/a11y-policy --profile acme-web-2026

--pack recibe un nombre de paquete, resuelto desde el proyecto del directorio de trabajo, o una ruta, y se repite para varios packs. El pack carga @surea11y/core/pack, que se instala junto a la CLI cuando instalas @surea11y/cli en el proyecto. Consulta CLI.

En una página propia

Para una extensión de navegador o un page.evaluate propio, inyecta packScript([acmePolicy]) de @surea11y/core/pack después del bundle de navegador de core, o distribuye un solo archivo generado con buildBrowserBundle({ packs: [acmePolicy] }), y nombra el pack como name@version. Consulta Uso independiente. Para una página que carga sus scripts como archivos, conviene el archivo único de buildBrowserBundle: el script de un pack solo se ejecuta con el core que lo escribió.

Una página con el bundle de otro core omite el pack, y skippedPacks nombra las dos versiones, así que genera de nuevo ese archivo para cada versión de core o del pack.

Qué informa un análisis

  • engine.packs nombra cada pack que se ejecutó, como name@version, y engine.profile el perfil que se aplicó.
  • Las reglas de un pack aparecen en checksResults igual que las de core, con ids que empiezan por su espacio de nombres (acme-no-autoplay). Una regla de un pack que se asocia a un criterio de WCAG cuenta para el resultado agregado de ese criterio, salvo que lleve la etiqueta opcional del espacio de nombres del pack: esas reglas comprueban lo que WCAG no exige, así que solo cuentan en los resultados agregados propios del pack. El wcagSc de esas reglas sigue nombrando los criterios de WCAG que comprueban, para los generadores de informes, y ningún otro estándar (EN 301 549, que asocia cada regla por sus criterios de WCAG) las reclama. Una sustitución cuenta en lugar de la regla de core a la que reemplaza, donde la sitúe su propio mapeo.
  • Los elementos de una lista de comprobación, o los requisitos de un estándar, son resultados agregados en rulesResults, con el nombre de la lista de comprobación o del estándar como meta.standard. El Informe HTML los muestra en una sección propia, SARIF etiqueta cada regla con los elementos a los que pertenece (acme-images), y JUnit los enumera como propiedades. El campo standards del resultado nombra la lista de comprobación, así que un resultado guardado se muestra igual dondequiera que se genere.
  • La gravedad definida por un perfil (severity) aparece en los resultados que cambia, y la gravedad propia de la regla se conserva como ruleSeverity.

Cuando un pack no se ejecuta

Un pack cuyo rango core deja fuera el core instalado, que no es válido, o que no encaja con core (un id de regla fuera de su espacio de nombres, una clave de diccionario que core ya tiene, una variante de una regla que no existe) se omite: el análisis se ejecuta sin él, avisa, y lo enumera en skippedPacks como { name, reason }. Con strictOptions lanza un error, con code: 'INVALID_ENGINE_OPTIONS', como todo error de engineOptions.packs. Dos packs que chocan entre sí (una clave de diccionario que definen ambos, dos estándares con una misma clave) lanzan un error, ya que cualquier forma de elegir entre ellos dependería del orden en que se pasaron. En una página, un pack nombrado en engineOptions.packs que ninguna llamada a packScript registró, o que se registró con otra versión de core, se enumera de la misma forma. Así, un análisis puede terminar sin las comprobaciones para las que añadiste el pack: una herramienta que depende de un pack debería comprobar skippedPacks antes de dar el análisis por completo.

Cada herramienta convierte un pack omitido en un fallo visible:

  • Las integraciones de navegador comprueban cada pack cuando llamas a .withPacks() y lanzan un error antes de analizar si core no puede ejecutar uno. Un pack que la página luego no ejecuta es una carencia 'pack-skipped' en getScanGaps(results), y .analyze() avisa de ello, así que una prueba que comprueba las carencias falla.
  • Los test matchers hacen fallar la aserción, nombrando el pack y el motivo.
  • La CLI sale con 2, sin escribir ningún informe ni línea base, para un pack que no puede encontrar o cargar, o uno que el motor omite.

Instala el pack junto a @surea11y/core y al paquete que lo ejecuta, para que los tres compartan una sola copia de core.

Escribir un pack

Partir del ejemplo

Un pack es un objeto creado con definePack de @surea11y/core/pack. Este es acme-policy-pack.js, el ejemplo que incluyen las integraciones:

'use strict';
 
// A small pack: one house rule, a checklist profile and two checklist items.
// A real pack is a package of its own; `npx surea11y-pack new <folder>` writes one.
 
const { definePack, wcagTags } = require('@surea11y/core/pack');
 
module.exports = definePack({
name: '@acme/a11y-policy',
title: 'Acme web policy',
version: '1.0.0',
namespace: 'acme',
core: '^1.11.1',
rules: [
{
id: 'acme-no-autoplay',
meta: {
title: 'Videos do not play sound on their own',
description: 'A video that starts with sound talks over a screen reader.',
tags: ['acme'],
defaultSeverity: 'serious'
},
runInPage(ctx) {
// defaultMuted reflects the muted attribute where muted isn't set from it yet (jsdom).
const loud = [...ctx.document.querySelectorAll('video[autoplay]')].filter((v) => !(v.muted || v.defaultMuted));
return loud.length
? {
outcome: 'fail',
occurrences: loud.map((v) =>
ctx.helpers.reportOccurrence(v, { summary: 'This video plays with sound as the page opens.', hint: 'Remove autoplay, or add muted.' })
)
}
: { outcome: 'pass', occurrences: [] };
}
}
],
profiles: {
'acme-web-2026': { tags: wcagTags('2.2', ['A', 'AA']), rules: ['region', 'acme-no-autoplay'] }
},
rollups: [
{ id: 'acme-images', title: 'Images have a text alternative', checksIds: ['img-alt-present'] },
{ id: 'acme-media', title: 'Media stays quiet until asked', checksIds: ['acme-no-autoplay'] }
]
});

Su perfil, acme-web-2026, ejecuta las reglas de WCAG 2.2 A y AA de core, la regla de buenas prácticas region, que ninguna etiqueta de WCAG selecciona, y la regla propia. Sus dos resultados agregados son los elementos de la lista de comprobación: cada uno agrupa reglas, de core o del pack, en un solo resultado.

Para un estándar, el pack de EN 301 549 es un ejemplo completo y publicado.

Crear uno con surea11y-pack new

Para un pack propio, parte de un paquete generado:

$ npx -p @surea11y/core surea11y-pack new acme-a11y --name @acme/a11y-pack
# or, for a standard:
$ npx -p @surea11y/core surea11y-pack new city-standard --name @city/a11y-standard --kind standard --title "City Standard"
$ cd acme-a11y
$ npm install
$ npx playwright install chromium # once, for npm run docs
$ npm test && npm run lint && npm run docs

El pack funciona tal como se genera: sus pruebas pasan, sus reglas pasan el lint sin errores, sus ejemplos concuerdan con sus reglas. surea11y-pack new comprueba el pack como lo haría un análisis antes de escribir nada, y rechaza un nombre, un espacio de nombres o un título que lo harían no válido (un título WCAG nombra su estándar como uno de los de core), explicando por qué. A partir de ahí, cámbialo paso a paso, ejecutando npm test sobre la marcha. Su README recorre cada archivo:

ArchivoQué contiene
index.jsEl pack, escrito con definePack() de @surea11y/core/pack.
rules/automatic/<ns>-link-text-specific.jsUna regla que decide, usando los helpers de core.
rules/automatic/<ns>-contrast-enhanced.jsUna variante de contrast-minimum de core a 7:1.
rules/manual/<ns>-new-window-review.jsUna regla manual que solicita revisión humana, con sus códigos de motivo y los pasos que sigue una persona para responderla (meta.reasonCodes, meta.review).
i18n/en.jsonLos mensajes.
tests/rules/*.test.js, tests/profiles.test.jsUna prueba por regla, y lo que ejecuta cada perfil.
docs/RULE_EXAMPLES.mdEjemplos de los resultados de cada regla.
eslint.config.jsLas reglas de lint de core para reglas.
requirements.js, rule-map.jsSolo para un estándar: los requisitos y las reglas que comprueban cada uno.
package.jsonprivate, con @surea11y/core como dependencia peer en la versión que lo escribió (^1.14.1), y eslint, jsdom y Playwright como dependencias de desarrollo.
README.md, .gitignoreLa guía del propio pack sobre estos archivos, y lo que git deja fuera.

Nombres

  • El nombre y la versión del paquete son un nombre de paquete npm y una versión completa (1.0.0). Un análisis los informa en engine.packs.
  • El espacio de nombres (namespace: 'acme') mantiene los ids del pack separados de los de core y de los de otros packs: todo id de regla, de resultado agregado y de perfil empieza por él y -. No puede ser un prefijo de los ids de regla de core (img, aria o link lo serían, como en img-alt-present), ni empezar por wcag, y dos packs de un mismo análisis no pueden compartirlo. También es la etiqueta que hace opcionales las reglas propias del pack: una regla etiquetada con él solo se ejecuta bajo uno de los perfiles del pack, por su id o por la etiqueta (optInRules: ['acme']). Por defecto, entonces, un análisis que apunta a WCAG no la ejecuta. Desbloquear la etiqueta con optInRules no quita el perfil ni los filtros del análisis: con un perfil de WCAG, una regla desbloqueada solo se ejecuta cuando sus etiquetas también coinciden con ese perfil. Quita la etiqueta de una regla que deba ejecutarse en todos los análisis con el pack: entonces se selecciona igual que las reglas de core, y un análisis que apunta a WCAG puede ejecutarla e informar de sus fallos cuando sus etiquetas coinciden.
  • core es el rango de versiones de core con las que funciona el pack ('^1.11.1'), interpretado como npm interpreta los rangos. Empieza los packs nuevos en 1.11.1: el paquete 1.11.0 omitía un archivo que los packs necesitan en Node. Un análisis con un core fuera de él omite el pack, con el motivo en skippedPacks. surea11y-pack new escribe la versión que generó el pack (^1.14.1); mantén un rango que empiece en 1.14 si el pack usa lo que añadió la 1.14: meta.review, filas con reasonCodes y findings, meta.contributions, applicability, partial o keepsParsing.
  • title es como los resultados y los informes llaman a una lista de comprobación, y description una frase sobre lo que comprueba el pack, para herramientas que permiten a sus usuarios elegir packs (describePacks la devuelve).

Perfiles

Un perfil es una selección con nombre, que se ejecuta con engineOptions.profile. Una regla se ejecuta bajo él cuando tiene una de las etiquetas definidas en tags o su id está en rules, y no está en exclude; las reglas del pack etiquetadas con su espacio de nombres se ejecutan bajo todos los perfiles del pack.

profiles: {
'acme-policy': {
tags: wcagTags('2.2', ['A', 'AA']), // core's WCAG 2.2 A and AA rules
rules: ['region', 'heading-order'], // and these, by id
exclude: { rules: ['contrast-minimum'] }, // but not this one
severity: { 'img-alt-present': 'critical' } // and this one is critical here
},
'acme-quick': {
tags: [],
rules: ['img-alt-present', 'link-name-present', 'page-title-present']
}
}
CampoQué hace
tagsLas reglas que llevan cualquiera de estas etiquetas. wcagTags(version, levels) de @surea11y/core/pack da las etiquetas de WCAG de core (wcagTags('2.1') es 2.0 y 2.1, A y AA).
rulesReglas por id, de core o del pack: una regla de buenas prácticas que ninguna etiqueta de WCAG selecciona o, con tags: [], las reglas de core que el perfil ejecuta además de las propias del pack.
exclude{ rules: [...] } deja reglas fuera del análisis; { criteria: ['2.5.8'] } exime de esos criterios: sus resultados agregados desaparecen, y también cada regla de la que se eximen todos los criterios; una regla que también comprueba un criterio que el perfil conserva se sigue ejecutando. Esto reduce lo que comprueba el análisis: los requisitos de WCAG siguen siendo los mismos.
severity{ ruleId: level }: otra gravedad bajo el perfil. Los resultados la muestran y conservan la propia de la regla como ruleSeverity; las líneas base y los ids de los hallazgos no cambian.
wcagVersion'2.0', '2.1' o '2.2': la versión de WCAG con la que se juzgan los análisis del perfil (engine.wcagVersion). Sin ella, las etiquetas de WCAG de tags implican la versión, y un perfil sin ninguna se juzga con 2.2. El engineOptions.wcagVersion propio de un análisis sigue ganando. Core 1.12.0 y posteriores.
keepsParsingtrue cuando el estándar exige el 4.1.1 Parsing tal como está escrito, como hace EN 301 549 V3.2.1: un fallo en él sigue siendo un fallo con un objetivo 2.0 o 2.1. Con un perfil sin este campo, las erratas de 2023 a WCAG 2.0 y 2.1 dan el 4.1.1 por cumplido para HTML y XML, y duplicate-id pregunta en su lugar (con 2.2, que eliminó el 4.1.1, pregunta diga lo que diga el perfil). Solo cuenta el campo del propio perfil: el perfil de un pack que ocupa el lugar de uno de core no lo hereda. engineOptions.mappings nunca cambia un resultado, así que un análisis juzgado por un estándar que mantiene el 4.1.1 ejecuta su perfil. Core 1.14.0 y posteriores.
version, mappedRulesSolo para el perfil de un estándar: la versión del estándar a la que apunta, y mappedRules: true para ejecutar todas las reglas que el estándar asocia para esa versión.

Un perfil que ejecuta solo lo que pide su estándar. El perfil de un estándar con tags: [<ns>] y mappedRules: true ejecuta las reglas propias del pack y las reglas de core que su mapa vincula a un requisito, y nada más: ninguna regla comprueba lo que el estándar no pide, y los resultados no contienen criterios de WCAG. Sin etiqueta de WCAG, sin embargo, nada dice en qué versión de WCAG se basa el estándar, y la ejecución se juzga con 2.2. Para un estándar basado en 2.1 eso es un error: la ejecución informa criterios de 2.2, como el 2.5.8, que el estándar no tiene. Indica la versión, y keepsParsing: true si el estándar sigue exigiendo el 4.1.1 Parsing. El index.js generado la lee de requirements.js, donde cada versión del estándar nombra la versión de WCAG en la que se basa ({ version: '1.0', wcagVersion: '2.2' }):

'city-1.0': {
version: '1.0',
tags: ['city'], // the pack's own rules
mappedRules: true, // and the core rules its map links to a requirement
wcagVersion: wcagVersionOf('1.0') // judged by the WCAG version requirements.js gives for 1.0
}

Las reglas de core, con sus ids y etiquetas, están en Reglas.

Elementos de lista de comprobación y requisitos

Los elementos de una lista de comprobación son sus rollups: { id, title, checksIds }, cada uno de los cuales agrupa reglas en un solo resultado. Un elemento falla cuando falla una de sus reglas, y pasa cuando pasan. Cuando ninguna falla pero una devuelve cantTell, el elemento también es cantTell: necesita revisión humana. Un pack indica o bien profiles y rollups, una lista de comprobación, o bien un standard, no ambos.

Los requisitos de un estándar vienen de dos tablas que escribe el generador: requirements.js (por versión, el número, la redacción y los criterios de WCAG de cada requisito) y rule-map.js (qué requisitos comprueba cada regla, y por qué). ruleMappedStandard() de @surea11y/core/pack los convierte en el estándar: un resultado agregado por requisito, los requisitos que nombra el resultado de cada regla, y comprobaciones de ambas tablas cuando se prepara el pack.

Enviar hallazgos a requisitos

La fila de una regla en rule-map.js puede enviar a otro sitio sus hallazgos de un código de motivo, con reasonCodes: { [code]: { requirements, outcome } }: requirements (por defecto, los de la fila) son aquellos a los que cuentan esos hallazgos, [] para ninguno, y outcome: 'cantTell' convierte uno que falla en una pregunta ahí. El resultado agregado de un requisito juzga entonces la regla por los hallazgos que cuentan para él, así que un estándar puede preguntar por una causa donde WCAG la falla, o archivar dos causas en dos pruebas:

'list-children-valid': {
requirements: ['9.3.1'],
reasonCodes: { LIST_ITEM_ROLE_OVERRIDDEN: { requirements: ['9.3.1', '9.3.2'], outcome: 'cantTell' } }
}

Una fila también puede enviar hallazgos según lo que registran, con findings: [{ when, requirements, outcome }]: when da valores que deben tener campos del data.details de un hallazgo, nombrados por su ruta con puntos. Un hallazgo va según la primera entrada con la que coincide; si no, según su código de motivo; si no, según la fila. Así un estándar puede archivar los fallos de una regla en la prueba de su tamaño de texto, aunque compartan código de motivo:

'contrast-minimum': {
requirements: ['3.1', '3.2'],
findings: [
{ when: { 'typography.isLargeText': false }, requirements: ['3.1'] },
{ when: { 'typography.isLargeText': true }, requirements: ['3.2'] }
]
}

Compara solo campos que una regla mantiene estables: reasonCode en todas las reglas, y los pocos más que nombra la política de estabilidad de la API de core en GitHub (entre ellos, la ratio, el umbral y la tipografía de contrast-minimum). Un campo que cambia o desaparece no coincide con nada, y el hallazgo va según su código o la fila. Los códigos de motivo de cada regla figuran en «What a finding reports» en el catálogo de reglas de core en GitHub.

Dos campos más cambian cómo se juzga un requisito. Un estándar cuyos requisitos reformulan criterios de WCAG uno a uno, como el capítulo 9 de EN 301 549, pasa applicability: 'wcag' a ruleMappedStandard: el resultado agregado de un requisito es entonces notApplicable o pass solo donde lo sería el resultado agregado de WCAG de sus criterios, así que los dos coinciden en un mismo informe. Un requisito que las reglas de core no comprueban por completo, aunque no reformule un criterio, lleva partial: true en la tabla de requisitos: su resultado agregado es entonces cantTell donde todas sus reglas pasan o no encuentran nada, ya que solo comprobaron una parte. Un estándar que escribe sus propios resultados agregados indica ambas cosas en el meta de cada uno, y envía los hallazgos en meta.contributions; StandardRollup en pack.d.ts da la forma, y la guía de packs de core en GitHub lo explica paso a paso. Todo esto necesita core 1.14.0.

Reglas

Una regla tiene la forma de los módulos de regla de core: { id, meta, runInPage }, y applicability cuando solo se aplica a veces, con el mismo ctx. runInPage(ctx) se ejecuta en la página, así que solo usa ctx, nunca una variable o un módulo de fuera, y solo lee: las reglas de un pack se ejecutan después de las de core, y los helpers y las opciones que reciben son de solo lectura. Solo lectura es una norma, no un entorno aislado: nada impide que el código de una regla haga más, así que carga solo packs de confianza (consulta la política de seguridad de core en GitHub). Tres atajos necesitan poco o ningún código: una variante ejecuta una regla de core con otros ajustes ({ id, from: 'contrast-minimum', config: { normalTextRatio: 7 }, meta }; un umbral admite un número finito mayor que 0, una opción uno de sus valores listados, y definePack rechaza cualquier otra cosa), una sustitución reemplaza una regla de core (enumerada en overrides, y en el overriddenBuiltinIds de los resultados), y una regla de core tal cual solo necesita su id en un perfil o en un resultado agregado. Cómo escribir una regla está en la guía de autoría de reglas de core en GitHub.

La lista de ajustes de reglas de core en GitHub da cada ajuste que las reglas de core ofrecen a una variante, con su valor predeterminado, los valores que admite y lo que cambia; getChecksCatalog() da lo mismo como variantSettings de cada regla. Así es como la versión de un requisito de otro estándar, o una decisión que WCAG deja a quien evalúa, se ejecuta con el código de core. Desde core 1.14, contrast-minimum ofrece opciones para el texto que parece un logotipo (logoText), una página con un control para cambiar a más contraste (contrastSwitch) y el texto seminegrita (semiBold), y valid-lang ofrece codes: 'iso639' para un estándar, como RGAA, que acepta cualquier código de idioma ISO 639.

Cómo comprueba una persona un cantTell

Una regla que puede dejar un hallazgo sin decidir puede indicar, por código de motivo, cómo lo responde una persona, como hacen las reglas de core (ver «Cómo comprobarlo» en el esquema de salida): unos pocos pasos que parten del elemento que nombra el hallazgo, y luego lo que se ve si pasa y si falla.

meta.review = {
GENERIC_LINK_NAME: {
steps: ['Find the link the report names.', 'Read the text around it.'],
passWhen: 'The text around the link says where it goes.',
failWhen: 'Nothing near the link says where it goes, which fails WCAG 2.4.4.'
}
};

definePack comprueba la forma: de 1 a 6 pasos y un passWhen y un failWhen no vacíos por código de motivo. Una regla que declara sus códigos de motivo en meta.reasonCodes (['GENERIC_LINK_NAME']) da pasos solo para un código que declara ahí o que nombra en su runInPage, así que se rechaza un código mal escrito: declararlos vale la pena por esa comprobación. Los pasos están en inglés, sea cual sea la configuración regional del análisis, y no entran en la página: runDomRulesInPage con engineOptions.packs los añade al resultado, y para un resultado obtenido en una página lo hace addHowToCheck(result, { packs }). Una variante tiene los pasos de su regla de core, traducidos, así que no necesita ninguno; una sustitución, como una regla personalizada, no tiene ninguno. Core 1.14.0 y posteriores.

Mensajes e idiomas

Cada texto que muestra una regla tiene una clave, guardada por idioma en i18n/<locale>.json y enumerada en los dictionaries del pack. Un análisis con engineOptions.locale: 'fr' muestra los textos en francés; si el pack no incluye un diccionario para el idioma solicitado, se muestran en inglés. Las claves de un pack son propias y empiezan por su espacio de nombres: un pack no puede cambiar un mensaje de core en un idioma que core incluye.

Probarlo, pasarle el lint y documentarlo

  • Las pruebas usan @surea11y/core/testing, lo mismo que usan las pruebas de reglas del propio core: runa11yCoreOnHtml(html, { engineOptions }) analiza HTML en jsdom, y assertRule(result, ruleId, outcome) comprueba una regla. Con packs, el análisis los ejecuta tanto desde los objetos de pack como tal como los tiene una página, así que una regla que lee una variable de fuera de su función falla su prueba. Necesita jsdom como dependencia de desarrollo.
  • Las comprobaciones: definePack lanza un error que nombra cada problema, así que fallan las pruebas del propio pack; checkPack(pack) devuelve la misma lista sin lanzar nada, y comprueba también la versión de core instalada. Ambos ejecutan las comprobaciones propias de un estándar sobre sus tablas (validate) contra las reglas de core y las del pack, como hace un análisis, así que una versión sin su wcagVersion, o un hallazgo enviado a un requisito que la tabla no tiene, se rechaza ahí en lugar de hacer que todos los análisis omitan el pack. Core 1.14.0 y posteriores.
  • El lint (npm run lint, @surea11y/core/eslint-plugin) somete las reglas del pack a las mismas reglas de lint que las de core.
  • La documentación (npm run docs, surea11y-pack docs) escribe el docs/RULE_CATALOG.md del pack y ejecuta en Chromium todos los ejemplos de su docs/RULE_EXAMPLES.md (CHROMIUM_EXECUTABLE_PATH nombra otra compilación de Chromium); --check falla cuando alguno de los dos está desactualizado, para la CI del pack.

Publicarlo

El paquete generado es private. Elige su licencia, define license y quita private en package.json, y ejecuta npm publish. Mantén @surea11y/core como dependencia peer, con el rango en core. Semver cubre aquello sobre lo que se construye un pack: un pack escrito para core 1.11 sigue funcionando con versiones 1.x posteriores. Para tus propios usuarios, el id de una regla y sus códigos de motivo son lo que guardan sus líneas base: cambia el nombre de uno o elimínalo solo en una versión mayor del pack.

La guía completa, desde una carpeta vacía hasta un pack publicado, es PACKS.md de core en GitHub, y cada campo está en la referencia de packs de ENGINE_OPTIONS.md.