Options du moteur

Tous les runners (runDomRulesInPage, runa11yCoreInPage) prennent les mêmes quatre arguments : (pageUrl, contextSelector, engineOptions, runOnly). Cette page documente engineOptions et runOnly dans leur intégralité. Pour vérifier une page au regard d’une version et d’un niveau WCAG, commencez par runOnly.wcag.

Choisir les règles à exécuter

Nommez ce au regard de quoi vous testez, et le moteur choisit les règles. Sans sélection, ce sont les règles par défaut qui s’exécutent : toutes les règles sauf les règles optionnelles d’une norme ou d’un pack, qui ne s’exécutent qu’une fois débloquées (voir Règles optionnelles).

Pour exécuterPassez
Les règles par défaut : toutes les règles sauf les règles optionnelles non débloquéesrien : runOnly null
Une cible de conformité WCAG : les règles des critères d’une version à un niveau et en dessousrunOnly: { wcag: { version: '2.2', level: 'AA' } }
La même chose, plus les règles de bonnes pratiques, qui ne testent aucun critère WCAGrunOnly: { wcag: { version: '2.2', level: 'AA' }, bestPractices: true }
Les règles de bonnes pratiques seulementrunOnly: { bestPractices: true }
Une norme fondée sur WCAG (EN 301 549, Section 508)engineOptions: { profile: 'en301549-v4.1.1' }
La même chose, plus les règles de bonnes pratiquesengineOptions: { profile: 'en301549-v4.1.1' }, runOnly: { bestPractices: true }
// WCAG 2.2 AA, with the best practices:
runDomRulesInPage(url, null, {}, {
wcag: { version: '2.2', level: 'AA' },
bestPractices: true
});

Chaque règle teste des critères WCAG ou relève des bonnes pratiques : { wcag: { version: '2.2', level: 'AAA' }, bestPractices: true } exécute donc toutes les règles sauf celles de 4.1.1 Analyse syntaxique, que WCAG 2.2 a retiré. Pour une sélection plus fine, comme un seul critère, une liste de règles ou une exclusion, voir « Sélection avancée : tags et identifiants de règle » ; ces champs se combinent avec ceux ci-dessus. Une analyse qui nomme des règles rapporte aussi les critères qu’elles vérifient (« Les critères que rapporte une analyse »).

Sélectionner par cible WCAG (runOnly.wcag)

runOnly.wcag nomme une version et un niveau de conformité WCAG, et sélectionne les règles des critères de cette cible :

// WCAG 2.2 Level A and AA:
runDomRulesInPage(url, null, {}, { wcag: { version: '2.2', level: 'AA' } });
  • version vaut '2.0', '2.1' ou '2.2' ; level vaut 'A', 'AA' ou 'AAA' (quelle que soit la casse).
  • Une règle est sélectionnée quand l’un des critères qu’elle nomme fait partie de la cible : en vigueur dans cette version (introduit dans celle-ci ou avant, et pas retiré dans celle-ci ou avant), au niveau de la cible ou en dessous, selon le niveau qu’a le critère dans cette version. Les niveaux viennent de WCAG_CRITERIA dans @surea11y/core/wcag, qui a un niveau par version.
  • Ainsi, 4.1.1 Analyse syntaxique, que 2.2 a retiré, fait partie des cibles { version: '2.1' } et non des cibles { version: '2.2' } : duplicate-id s’exécute pour la première et pas pour la seconde, là où les tags de version l’exécuteraient.
  • Une règle qui ne nomme aucun critère (une règle de bonnes pratiques) ne fait partie d’aucune cible. Ajoutez celles-ci avec bestPractices: true (section suivante), ou des règles isolées avec tags ou includeRuleIds : à côté de wcag, tous sélectionnent des règles en plus, l’union des règles de la cible et de ce que les autres sélectionnent. excludeTags, excludeRuleIds et excludeTestIds s’appliquent ensuite, comme toujours.
  • La version de la cible est la cible WCAG de l’exécution (engine.wcagVersion), sauf si engineOptions.wcagVersion est défini ; son niveau détermine les agrégations WCAG rapportées, comme le fait un tag de niveau, sauf si engineOptions.wcagLevel est défini (« Donner le niveau d’une analyse qui nomme des vérifications »).
  • Une cible inclut des règles : elle les sélectionne donc à la place de engineOptions.profile, comme le font les tags. getProfileWcagTarget(profile) donne la cible à laquelle revient un profil (voir « Profils de conformité »), et getChecksForRunOnly(runOnly, engineOptions) liste les règles qu’exécute un runOnly avec wcag, et getRulesForRunOnly(runOnly, engineOptions) les agrégations qu’il rapporte.
  • Toute autre valeur lève INVALID_RUN_ONLY, en nommant ce qui ne va pas : runOnly.wcag.version must be "2.0", "2.1" or "2.2", not "3.0".
// WCAG 2.1 AA, plus the best-practice rules, without one of them:
{ wcag: { version: '2.1', level: 'AA' }, tags: ['best-practice'], excludeRuleIds: ['region'] }

Ajouter les bonnes pratiques (runOnly.bestPractices)

bestPractices: true sélectionne les règles de bonnes pratiques : les règles qui ne nomment aucun critère WCAG, comme region, landmark-one-main, heading-order, page-has-heading-one, skip-link, tabindex et image-redundant-alt. Ce sont les règles portant le tag best-practice : cela revient donc à ajouter ce tag. Un constat de l’une d’elles relève des bonnes pratiques, pas d’un échec WCAG : aucune agrégation WCAG ne le compte. La plupart renvoient cantTell ; celle dont le constat est en lui-même un obstacle renvoie fail, comme empty-heading pour un titre sans nom.

  • À côté de wcag, de tags ou d’identifiants de règle, elle ajoute ces règles à ce qu’ils sélectionnent ; les exclusions s’appliquent ensuite, si bien que excludeRuleIds: ['region'] écarte toujours region.
  • Seule, elle ne sélectionne que les règles de bonnes pratiques.
  • false, ou son omission, n’ajoute rien. Seul, { bestPractices: false } est une sélection vide : les règles par défaut s’exécutent donc, comme avec {}.
  • Toute autre valeur lève INVALID_RUN_ONLY : runOnly.bestPractices must be true or false, not "yes".
  • À côté d’engineOptions.profile, elle ajoute les règles de bonnes pratiques à celles du profil, et le profil s’applique toujours : ses règles, ses agrégations, ses correspondances et engine.profile, comme sans elle. { bestPractices: true } avec profile: 'en301549-v4.1.1' exécute cette norme et les bonnes pratiques. Les règles de bonnes pratiques ne comptent dans aucune des agrégations du profil, puisqu’elles ne testent aucun critère WCAG.
// WCAG 2.1 AA and the best practices, without one of them:
{ wcag: { version: '2.1', level: 'AA' }, bestPractices: true, excludeRuleIds: ['region'] }

Profils de conformité

engineOptions.profile nomme une cible de conformité au lieu d’en détailler le jeu de tags :

ProfilExécute les règles taguéesCible WCAGgetProfileWcagTargetPourquoi
wcag22-aawcag2a, wcag2aa, wcag21a, wcag21aa, wcag22a, wcag22aa2.2{ version: '2.2', level: 'AA' }WCAG 2.2 niveaux A et AA
en301549-v4.1.1comme wcag22-aa2.2{ version: '2.2', level: 'AA' }Le chapitre 9 d’EN 301 549 V4.1.1 reprend WCAG 2.2 A et AA
en301549-v3.2.1wcag2a, wcag2aa, wcag21a, wcag21aa2.1{ version: '2.1', level: 'AA' }Le chapitre 9 d’EN 301 549 V3.2.1 reprend WCAG 2.1 A et AA, y compris 4.1.1 Analyse syntaxique
section508wcag2a, wcag2aa2.0{ version: '2.0', level: 'AA' }Les normes révisées de la Section 508 intègrent WCAG 2.0 A et AA

getProfileWcagTarget(profile) donne la cible runOnly.wcag à laquelle revient le jeu de tags d’un profil, ou null pour un nom qui n’est pas un profil. Un profil sélectionne toujours par ses tags, comme dans le tableau : sous wcag22-aa, duplicate-id (4.1.1) s’exécute et ne peut pas donner fail, alors que { wcag: getProfileWcagTarget('wcag22-aa') } l’écarte.

runDomRulesInPage(url, null, { profile: 'en301549-v3.2.1' }, null);
Obsolète

en301549-v4.1.1 et en301549-v3.2.1 passent dans le pack d’EN 301 549, @surea11y/pack-en301549, et quittent core en 2.0.0. Ils fonctionnent comme avant, avec un console.warn par processus. Avec le pack (packs: [en301549]), une analyse prend chez lui les mêmes noms et n’affiche aucun avertissement ; les profils du pack rapportent un résultat par clause à la place des agrégations WCAG. Voir comment core rend une norme obsolète.

La norme ou la liste de contrôle d’un pack ajoute ses propres profils à cette liste.

La cible WCAG se déduit des tags comme pour un jeu écrit à la main (voir « Filtrer par version de WCAG » ci-dessous) : en301549-v3.2.1 cible WCAG 2.1 et en301549-v4.1.1 WCAG 2.2. Un id dupliqué peut encore donner fail sous en301549-v3.2.1 parce que son profil conserve le texte du 4.1.1 (keepsParsing, voir plus bas) ; sous en301549-v4.1.1, non. Le profil d’un pack peut plutôt nommer sa version, avec wcagVersion (Packs) : un profil qui n’exécute que les règles associées à sa norme n’a aucun tag WCAG pour en impliquer une. Les noms ne tiennent pas compte de la casse. Une exécution qui a utilisé un profil le renvoie dans engine.profile.

Priorité : tout ce qui inclut des règles les sélectionne à la place du profil — un runOnly avec wcag, tags, includeRuleIds ou includeTestIds, ou un include dans engineOptions.rules/.tags/.tests. Les exclusions s’appliquent toujours par-dessus le profil, qu’elles viennent de runOnly (excludeTags, excludeRuleIds, excludeTestIds : le disableTags() d’un binding restreint donc le profil au lieu de le remplacer) ou, en l’absence de filtre runOnly, d’engineOptions (tags.exclude, rules.exclude, tests.exclude), et un engineOptions.wcagVersion explicite l’emporte toujours sur la version qu’implique ou que nomme le profil. Un profil sans effet (nom inconnu, ou profil supplanté comme ci-dessus) n’est pas une erreur : l’exécution se poursuit comme si aucun n’avait été donné, émet un console.warn qui en donne la raison, et ne porte pas engine.profile. runOnly.bestPractices: true n’est pas une inclusion de ce type : elle ajoute les règles de bonnes pratiques à celles du profil, et le profil s’applique (« Ajouter les bonnes pratiques »).

Le profil d’une norme peut aussi écarter des règles, quand sa norme dispense d’un critère WCAG ou remplace une vérification WCAG par la sienne : les règles qu’il nomme, les règles dont il dispense de tous les critères et les agrégations WCAG des critères dispensés ne s’exécutent pas, et le catalogue des règles les écarte aussi. Une règle qui vérifie aussi un critère que le profil conserve s’exécute toujours (link-name-present, qui vérifie 2.4.4 et 4.1.2, sous un profil qui dispense de 2.4.4), et engine.profileExcludes nomme ce qui a été écarté. Aucun profil intégré n’écarte quoi que ce soit aujourd’hui.

Un profil ne fait que choisir les règles qui s’exécutent. Il ne dit rien sur le fait que les réussir satisfait la norme dont il porte le nom : la plupart des critères de succès exigent un jugement humain qu’aucune règle automatisée ne couvre (voir ce que ce moteur ne peut pas vous dire).

Un profil EN 301 549 active aussi les clauses d’EN 301 549 de la version qu’il vise, comme si mappings: ['en301549:V4.1.1'] (ou V3.2.1) avait été passé ; voir « Autres normes » ci-dessous. Le profil de toute norme active sa propre version de la même façon. Un profil avec mappedRules exécute aussi, quels que soient leurs tags, les règles auxquelles sa norme est associée : celles qui n’ont pas de correspondance WCAG, comme heading-order et skip-link, n’ont aucun tag WCAG par lequel être sélectionnées. Une norme dotée d’exigences propres, ou la liste de contrôle d’une organisation, arrive dans le moteur sous forme de pack, avec ses propres noms de profil, règles et agrégations. Le moteur n’en fournit lui-même aucune.

Règles optionnelles

Certaines règles vérifient des exigences propres à une norme, que WCAG n’impose pas : une norme nationale peut exiger un doctype, ou interdire des attributs de présentation comme bgcolor. Un fail d’une telle règle est un échec au regard de cette norme, pas de WCAG : ces règles sont donc désactivées par défaut. Chacune porte le tag de règle de sa norme et ne s’exécute que lorsque la sélection la demande :

  • par le profil de la norme, qui liste le tag,
  • par ce tag (tags: { include: '<tag>' }, seul ou avec d’autres),
  • par son id (rules: { include: '<rule id>' }, ou runOnly.includeRuleIds),
  • par l’id d’une agrégation propre à sa norme qui la regroupe (rules: { include: '<rollup id>' } exécute les règles de cette agrégation, optionnelles ou non), ou
  • en la débloquant avec engineOptions.optInRules, ci-dessous.

Rien d’autre ne la sélectionne : ni une exécution par défaut, ni un jeu de tags WCAG, ni un profil WCAG ou EN 301 549, ni un id d’agrégation WCAG. Les exclusions s’y appliquent comme à toute règle. Sélectionner le tag seul exécute les règles propres à cette norme, pas les règles WCAG auxquelles elle est aussi rattachée : cela répond à « qu’exige la norme au-delà de WCAG » et ne constitue pas un audit au regard de celle-ci. Pour un audit, utilisez le profil de la norme : sous le tag seul, ses propres agrégations renvoient cantTell pour toute exigence dont les règles ne se sont pas exécutées. Une règle ajoutée via customRules qui porte le tag suit le même verrou. Aucune des normes intégrées au moteur n’a de règles optionnelles aujourd’hui ; le tag d’espace de noms d’un pack fonctionne de cette façon.

Exécuter toutes les règles (optInRules)

Pour exécuter toutes les règles du moteur, quelle que soit la norme à laquelle elles appartiennent, omettez le profil et débloquez les règles optionnelles :

runDomRulesInPage(url, null, { optInRules: 'all' }, null);

'all' débloque les règles optionnelles de toutes les normes ; une liste de tags ne débloque que celles-ci. L’option lève le verrou, rien de plus : le reste de la sélection décide toujours des règles exécutées. Sans filtre ni profil, comme ci-dessus, l’exécution couvre donc toutes les règles et les agrégations de toutes les normes ; sous profile: 'wcag22-aa', elle n’exécute toujours que les règles WCAG, puisque les règles optionnelles ne portent aucun tag WCAG. Les exclusions s’appliquent comme d’habitude. Un tag qui n’est pas un tag de règle optionnelle est ignoré avec un console.warn. Seule cette option du moteur débloque : runOnly n’a pas de clé optInTags, si bien qu’une telle clé y est une clé inconnue, qui lève INVALID_RUN_ONLY si elle est seule et est ignorée avec un avertissement à côté de clés que runOnly lit, le message renvoyant à engineOptions.optInRules.

C’est fait pour voir tout ce que le moteur peut signaler, pendant le développement ou en explorant un site. Ce n’est pas une cible de conformité : une telle exécution fait échouer une page pour des choses que WCAG permet et que seule une autre norme interdit. Le résultat le dit : engine.optInRules liste les tags dont les règles se sont exécutées, et le rapport HTML, SARIF et JUnit l’affichent à côté du profil. Elle n’ajoute aux résultats les numéros d’aucune autre norme ; passez mappings pour cela, par exemple { optInRules: 'all', mappings: ['<key>'] }.

Autres normes (mappings)

Le meta.normativeMappings de chaque résultat nomme les critères de succès WCAG qu’il teste. engineOptions.mappings y ajoute l’exigence d’une autre norme qui correspond à chacun. Par défaut, il n’en ajoute aucune : une clause d’une norme au regard de laquelle vous n’auditez pas n’est que du bruit dans chaque tag SARIF, propriété JUnit et rapport.

ValeurAjoute
'en301549'La clause du chapitre 9 d’EN 301 549 pour chaque critère, dans toutes les versions qui la contiennent (V3.2.1 et V4.1.1)
'en301549:V3.2.1', 'en301549:V4.1.1'La même chose, pour cette version seulement
runDomRulesInPage(url, null, { mappings: ['en301549:V3.2.1'] }, null);
Obsolète

Les correspondances en301549 passent dans le pack d’EN 301 549, @surea11y/pack-en301549, et quittent core en 2.0.0. Elles fonctionnent comme avant, avec un console.warn par processus. Avec le pack, mappings: ['en301549'] prend le même nom et donne à chaque résultat les mêmes entrées, sans avertissement.

L’option accepte un tableau ou une chaîne séparée par des virgules, noms et versions sans tenir compte de la casse. Sous profile: 'en301549-v4.1.1', les clauses V4.1.1 sont présentes sans les demander, et mappings peut en ajouter d’autres. Un nom ou une version pour lesquels le moteur n’a pas de table est ignoré avec un console.warn. Une exécution qui porte une autre norme indique laquelle dans engine.mappings, sous forme canonique (["en301549:V3.2.1"], ou ["en301549"] pour toutes les versions).

Elle ne change que ce que nomme un résultat, jamais les règles exécutées ni leurs résultats. Le catalogue des règles suit la même option : getChecksCatalog(engineOptions), getCheckDefById(ruleId, engineOptions), getChecksForRunOnly(runOnly, engineOptions), getRulesCatalog(engineOptions), getRulesForRunOnly(runOnly, engineOptions) et getCompositeRuleById(id, engineOptions) nomment les normes que nommerait une analyse avec ces options, y compris un profil quand il s’appliquerait : une entrée du catalogue et un résultat concordent donc toujours. Sans options, ils ne nomment que WCAG. Une règle ajoutée via customRules garde exactement les correspondances qu’elle déclare. Ce que sont les clauses et ce qui distingue les deux versions est expliqué dans la correspondance WCAG.

Sélection avancée : tags et identifiants de règle

Les champs ci-dessous sélectionnent selon les tags que portent les règles et selon les identifiants de règle. Servez-vous-en pour ce qu’une cible WCAG et bestPractices ne disent pas : un seul critère (tags: ['wcag1412']), une liste de règles, une exclusion, ou un jeu de tags à vous. Ils se combinent avec wcag et bestPractices : ce qu’ils incluent s’ajoute à ce que ceux-ci sélectionnent, et ce qu’ils excluent est retiré ensuite.

Il existe deux façons indépendantes de les fournir : le 4ᵉ argument (runOnly), ou engineOptions.rules/.tags/.tests/.includeMode. Si runOnly contient un filtre quel qu’il soit, il l’emporte d’office ; sinon, le moteur se rabat sur engineOptions. Ne les combinez pas en comptant sur l’application des deux : choisissez-en une. engineOptions.rules, .tags et .tests acceptent { include, exclude } ; une liste ou une chaîne seule est la liste d’inclusion.

Avec runOnly (4ᵉ argument)

runDomRulesInPage(url, null, {}, {
includeRuleIds: ['img-alt-present', 'button-name-present'],
excludeRuleIds: ['region'],
tags: ['wcag412'],
excludeTags: ['best-practice'],
includeMode: 'and' // 'and' (default) | 'or' — see below
});

Un tableau ou une chaîne bruts servent de raccourci : runOnly: ['img-alt-present', 'button-name-present'] exécute ces règles (un id composite entraîne ses règles), et runOnly: ['wcag2a', 'wcag2aa'] équivaut à { tags: ['wcag2a', 'wcag2aa'] }. Un tableau ne peut pas mélanger ids de règle et tags, et chaque valeur doit nommer une règle (intégrée, composite ou l’une des engineOptions.customRules) ou un tag : toute autre valeur lève une exception, pour qu’une faute de frappe ne puisse pas exécuter en silence toutes les règles ou aucune. Utilisez la forme objet pour combiner ids et tags ou pour exclure. Avant la 1.10.0, un tableau brut était ignoré et toutes les règles s’exécutaient.

ChampTypeSignification
wcag{ version, level }Une cible de conformité WCAG : les règles des critères de cette version à ce niveau et en dessous. Voir Sélectionner par cible WCAG, plus haut.
bestPracticesbooleantrue ajoute les règles de bonnes pratiques. Voir Ajouter les bonnes pratiques, plus haut.
includeRuleIdsstring[]N’exécute que ces IDs de règle (plus, pour un ID composite, ses règles atomiques enfants).
excludeRuleIdsstring[]N’exécute jamais celles-ci ; appliqué après l’inclusion.
includeTestIds / excludeTestIdsstring[]Même correspondance que ci-dessus. Conservé comme champ distinct parce qu’en interne les règles s’appellent « tests » (l’unité exécutable atomique) ; fonctionnellement identique à includeRuleIds/excludeRuleIds aujourd’hui.
tagsstring[]N’exécute que les règles portant au moins un de ces tags (par ex. wcag412, wcag2aa, best-practice).
excludeTagsstring[]N’exécute jamais les règles portant l’un de ces tags ; appliqué après l’inclusion.
includeMode'and' | 'or'Lorsqu’une inclusion par ID et une inclusion par tag sont toutes deux fournies : 'and' (par défaut) exige qu’une règle satisfasse les deux ; 'or' exécute une règle si elle satisfait l’une ou l’autre. Sans objet si vous n’utilisez qu’une seule dimension.

Chacun de ces champs accepte un tableau ou une chaîne séparée par des virgules, comme la forme engineOptions ci-dessous : includeRuleIds: 'img-alt-present, button-name-present' et includeRuleIds: ['img-alt-present', 'button-name-present'] sont équivalents.

Les noms sont vérifiés, ici et dans engineOptions.rules/.tags ci-dessous. Une liste d’inclusion (includeRuleIds, includeTestIds, tags, l’ancien values, rules.include, tags.include) dont aucune valeur ne nomme une règle ou un tag (un tag que porte une règle ou une agrégation, si bien que le tag d’une norme est reconnu même si seules ses agrégations le portent) lève une exception, avec code: 'INVALID_RUN_ONLY' et un message qui nomme le champ et les valeurs, puisqu’elle ne sélectionnerait rien et qu’une exécution sans aucune règle se lit comme une analyse sans constat. Une sélection dont tous les noms existent mais dont les parties s’annulent (une règle à la fois incluse et exclue, des règles et des tags sans aucune règle en commun avec includeMode: 'and') n’exécute elle aussi aucune règle : l’analyse émet un avertissement qui nomme les parties, et lève INVALID_RUN_ONLY avec strictOptions. includeMode vaut 'and' ou 'or' ; toute autre valeur est lue comme 'and', avec un avertissement, et lève une erreur avec strictOptions ; dans un binding de navigateur, .includeMode('or') le définit. Une valeur qui ne nomme rien à côté d’autres qui nomment quelque chose, ou dans une liste d’exclusion, est ignorée avec un console.warn. Avant la 1.10.0, toutes ces valeurs étaient ignorées sans avertissement.

Les neuf tags de version et de niveau WCAG, de wcag2a à wcag22aaa, sont toujours reconnus, qu’une règle en porte un ou non. Certains n’en portent aucune aujourd’hui (wcag21aaa, wcag22a, wcag22aaa) : leurs critères n’ont aucune règle automatisée, ce qui n’est pas une faute de frappe. Dans une liste d’inclusion à côté d’autres valeurs, un tel tag est signalé par un console.info, sans avertissement :

[surea11y] runOnly.tags: no rules for WCAG 2.2 Level A ("wcag22a"); its criteria need manual review.

engineOptions.logUntestedWcag: false omet cette ligne. Quand toutes les valeurs d’une liste d’inclusion sont de tels tags, rien ne s’exécuterait : la liste lève donc toujours INVALID_RUN_ONLY, en indiquant pourquoi. À côté d’une cible runOnly.wcag ou de bestPractices: true, qui sélectionnent leurs propres règles, le tag est seulement signalé. Dans une liste d’exclusion, il est laissé tel quel.

Les IDs de règle sont nus (sans préfixe de moteur), par exemple 'img-alt-present'. Pour la rétrocompatibilité, la correspondance accepte aussi une ancienne forme du même id préfixée par a11ycore- ('a11ycore-img-alt-present'). Les ids sont reconnus quelle que soit la casse, comme les tags : 'IMG-ALT-PRESENT' sélectionne img-alt-present, dans toute liste qui nomme des règles (runOnly sous chacune de ses formes, engineOptions.rules et engineOptions.tests, inclusion et exclusion). Aucune paire de règles n’a d’ids qui ne diffèrent que par la casse : cela ne peut donc pas sélectionner la mauvaise.

La forme { type, values } est également acceptée comme valeur complète de runOnly : { type: 'tag', values: ['wcag2a', 'wcag2aa'] } équivaut à { tags: ['wcag2a', 'wcag2aa'] }, et { type: 'rule', values: ['img-alt-present'] } à { includeRuleIds: ['img-alt-present'] } ('tags' et 'rules' fonctionnent aussi). values peut être une chaîne séparée par des virgules, ses noms sont vérifiés comme tout identifiant de règle ou tag, et les autres clés qui l’accompagnent (excludeTags, excludeRuleIds...) s’appliquent toujours. Un Set de noms est lu comme la liste qu’il contient. Une clé que la forme objet ne lit pas, à côté de clés qu’elle lit, est ignorée avec un avertissement. Tout autre type lève INVALID_RUN_ONLY, de même qu’un runOnly qui est un nombre ou un booléen, ou un objet dont le moteur ne lit aucune clé ({ includeRuleId: [...] }) : chacun de ces cas exécutait auparavant toutes les règles. Un tableau, une chaîne ou un objet vides signifient toujours la sélection par défaut.

Les critères que rapporte une analyse

Une analyse rapporte l’agrégation d’un critère (rulesResults) quand la sélection la sélectionne : une cible WCAG ou un tag de version/niveau, un profil, l’id de l’agrégation elle-même, ou aucune sélection. Elle rapporte aussi l’agrégation de chaque critère que vérifie une règle que la sélection a nommée pour elle-même, quand cette règle s’est exécutée :

  • par son id (includeRuleIds, includeTestIds, rules.include, ou la liste de règles propre à un profil),
  • par un tag qui n’est pas un tag WCAG (celui d’une règle personnalisée, d’un pack ou d’une norme ; les tags de WCAG sont les tags de version/niveau, les tags de critère comme wcag244, et wcag22-removed, si bien qu’un tag personnalisé comme wcagteam n’en est pas un),
  • par optInRules, ou
  • en tant que règle de bonnes pratiques (bestPractices).

Une règle que le profil d’une norme exécute seulement parce que sa norme y est associée (mappedRules) est nommée pour cette norme, pas pour elle-même : elle ajoute les exigences de la norme et aucun de ses critères WCAG. Un profil qui n’exécute que ce que demande sa norme ne rapporte donc que cette norme (Packs) ; une règle que nomme sa liste rules ajoute aussi ses critères WCAG. Avant la 1.12.0, une analyse qui nommait des règles plutôt que des critères avait un rulesResults vide.

// The unnamed link fails link-name-present, and with it 2.4.4 and 4.1.2:
runDomRulesInPage(url, null, {}, { includeRuleIds: ['link-name-present'] });
// rulesResults: wcag-2.4.4-link-purpose-in-context: fail, wcag-4.1.2-name: fail
  • Le verdict est celui de l’agrégation. Une règle en échec fait échouer le critère. Une règle réussie ne le fait pas réussir quand l’analyse a laissé de côté les autres règles du critère : il est cantTell, avec le reasonCode composite.rollup.cantTell.notSelectedChild, et chaque règle que l’analyse n’a pas sélectionnée figure parmi les contributrices comme missing avec notSelected: true. Même avec toutes ses règles sélectionnées et réussies, un critère WCAG reste cantTell (composite.rollup.cantTell.partialCheck), car les règles d’aucun critère ne vérifient encore tout ce qu’il demande (voir Correspondance de conformité WCAG). Le rapport HTML affiche « Vérifié en partie : 1 contrôle sur 2 n’a pas été sélectionné pour cette analyse » sous lui. Voir le schéma de sortie.
  • Un tag de critère nomme son propre critère. tags: ['wcag244'] rapporte 2.4.4, et pas les autres critères que vérifient ses règles (4.1.2 pour link-name-present). Une règle amenée par l’id d’une agrégation, une cible WCAG, un tag de version ou de niveau, ou un profil WCAG n’ajoute aucun critère : c’est la sélection qui nomme alors les critères. Une analyse qui sélectionne ses critères rapporte donc ce qu’elle rapportait avant.
  • La version et le niveau WCAG de l’exécution s’appliquent. Un critère n’est ajouté que s’il est en vigueur dans engine.wcagVersion (duplicate-id seul n’ajoute pas 4.1.1 en 2.2, mais l’ajoute avec wcagVersion: '2.1'), et au niveau de l’exécution ou en dessous, comme le ferait une cible : engineOptions.wcagLevel s’il est donné (section suivante), sinon le niveau que donne la sélection (runOnly.wcag.level, ou les tags de version/niveau à côté des ids de règle). Sans l’un ni l’autre, tous les niveaux sont rapportés.
  • Les exclusions s’appliquent. Une agrégation que la sélection exclut (excludeRuleIds: ['wcag-4.1.2-name'], ou un tag) n’est pas ajoutée. Une règle que l’appelant a exclue compte comme non sélectionnée. Une règle qu’un profil écarte restreint le critère : elle est missing sans notSelected, et un critère dont le profil dispense n’est jamais ajouté.
  • Les agrégations propres d’une norme (une par exigence) sont ajoutées de la même façon, mais seulement tant que la norme est en jeu : le profil appliqué la vise, ou la sélection nomme son tag, ou optInRules la débloque. Une analyse WCAG n’affiche jamais les exigences d’une norme que personne n’a demandée. Un profil qui ne s’est pas appliqué, et mappings seul, ne mettent pas une norme en jeu. Sous son profil, seule la version du profil est ajoutée ; débloquée sans profil, chaque version l’est, comme les rapporte une analyse sélectionnée par son tag. Cela vaut pour une norme ou une liste de contrôle venue d’un pack comme pour une norme intégrée : un profil de liste de contrôle qui nomme ses règles (tags: [], rules: [...]) obtient leurs critères WCAG et ses propres éléments.
  • La liste avant l’analyse. getRulesForRunOnly(runOnly, engineOptions) liste les agrégations que rapporte une analyse avec cette sélection et ces options, y compris celles ajoutées, sous forme d’entrées de getRulesCatalog. Elle les calcule avec l’étape d’agrégation de l’analyse elle-même : les deux concordent donc ; getChecksForRunOnly liste les règles. getRulesCatalog(engineOptions) liste toutes les agrégations que les options rendent disponibles, quelle que soit la sélection. Un outil peut ainsi enregistrer ce qu’une analyse vérifie avant de la lancer ; les builders des bindings de navigateur donnent les mêmes listes avec plannedChecks() et plannedCriteria().

Donner le niveau d’une analyse qui nomme des vérifications (wcagLevel)

Une analyse prévue pour WCAG 2.2 AA qui n’exécute que certaines vérifications les nomme, et donne la version dans engineOptions.wcagVersion. Cela ne dit aucun niveau : les critères de ces vérifications sont donc rapportés à tous les niveaux. contrast-computable vérifie 1.4.3 (AA) et 1.4.6 (AAA), et les deux sont rapportés. engineOptions.wcagLevel donne le niveau :

// Only the contrast checks of a WCAG 2.2 AA scan: 1.4.3 is reported, 1.4.6 is not.
runDomRulesInPage(url, null,
{ wcagVersion: '2.2', wcagLevel: 'AA' },
{ includeRuleIds: ['contrast-computable'] });
  • Valeurs. 'A', 'AA' ou 'AAA', quelle que soit la casse. Toute autre valeur est ignorée avec un console.warn (engineOptions.wcagLevel: ignoring "AA+"; use "A", "AA" or "AAA". ...), et lève INVALID_ENGINE_OPTIONS avec strictOptions. L’exécution renvoie un niveau valide dans engine.wcagLevel, écrit 'A', 'AA' ou 'AAA' ; sans l’option, engine.wcagLevel est le niveau qu’implique la sélection, et il est absent quand elle n’en implique aucun.
  • Elle décide de ce qui est rapporté, jamais de ce qui s’exécute. La sélection décide de ce qui s’exécute, comme sans elle. Une vérification au-dessus du niveau que la sélection nomme s’exécute toujours et rapporte son propre résultat ; seuls ses critères au-dessus du niveau sont omis de rulesResults.
  • Elle s’applique à toutes les agrégations WCAG, pas seulement aux critères ajoutés pour des vérifications nommées. wcagLevel: 'AA' sur une analyse complète, un profil ou une sélection par tags omet les critères AAA de rulesResults, tandis que leurs vérifications s’exécutent toujours et rapportent dans checksResults. C’est la différence avec wcagVersion, qui ne retire jamais une agrégation que la sélection sélectionne : une analyse 2.2 rapporte toujours 4.1.1, ses règles étant ramenées à cantTell. Une analyse sans l’option rapporte ce qu’elle rapportait avant.
  • Elle l’emporte sur le niveau qu’implique la sélection (runOnly.wcag.level, ou le niveau le plus élevé que nomment ses tags de version/niveau), comme un wcagVersion explicite l’emporte sur la version qu’ils impliquent. Un conflit entre les deux ne donne lieu à aucun avertissement, pas plus qu’un conflit entre wcagVersion et la version d’une cible ; engine.wcagLevel indique le niveau qui a filtré les critères.
  • Une agrégation nommée par son id est rapportée telle que nommée. includeRuleIds: ['wcag-1.4.6-contrast-enhanced'] avec wcagLevel: 'AA' rapporte 1.4.6 : l’option dit quels critères rapporter, pas d’écarter celui que l’appelant a demandé. Un niveau qu’impliquent les propres tags de la sélection filtre toujours une agrégation nommée, comme sans l’option.
  • Les agrégations propres d’une norme n’ont pas de niveau WCAG et ne sont pas filtrées par elle. Le profil d’un pack ne peut pas donner de niveau (ses tags en impliquent un) ; son appelant peut passer wcagLevel.
  • getRulesForRunOnly(runOnly, engineOptions) en tient compte : elle liste donc ce que rapporte l’analyse, et avertit d’une valeur invalide comme le fait l’analyse, ou lève une erreur avec strictOptions.

Sans l’option, des tags de version/niveau à côté des ids de règle donnent aussi un niveau, puisque l’includeMode: 'and' par défaut garde les règles nommées qui portent l’un d’eux : { includeRuleIds: ['contrast-computable'], tags: wcagTags('2.2') } exécute contrast-computable et ne rapporte que 1.4.3. Mais les tags décident aussi de ce qui s’exécute : une règle nommée sans tag WCAG (une règle de bonnes pratiques comme region, une règle personnalisée sans tags WCAG), ou une règle qui ne vérifie que des critères AAA, est écartée de l’exécution, et un tag de version/niveau qu’aucune règle ne porte (wcag22a) est signalé par un console.info. wcagLevel donne le niveau sans changer ce qui s’exécute. Dans un binding de navigateur, le .wcagLevel('AA') du builder le définit.

Filtrer par version de WCAG (2.0, 2.1, 2.2)

Chaque règle et chaque composite porte exactement un tag de niveau indiquant sa version WCAG d’origine : wcag2a/wcag2aa/wcag2aaa pour un critère de succès du socle WCAG 2.0, wcag21a/wcag21aa/wcag21aaa pour un critère introduit par WCAG 2.1 (par ex. 1.3.5 Identifier la finalité des champs), wcag22a/wcag22aa/wcag22aaa pour un critère introduit par WCAG 2.2 (par ex. 2.5.8 Taille de la cible (minimum)). Une règle reçoit uniquement le tag de la version d’origine réelle de son critère : un critère introduit en 2.1 ne porte jamais aussi wcag2aa, puisqu’il n’existe pas pour une cible de conformité WCAG 2.0.

runOnly.wcag sélectionne une cible de conformité en un seul champ (plus haut), et écarte 4.1.1 sous 2.2. Avec des tags, comme les versions sont cumulatives (2.1 = 2.0 + nouveautés ; 2.2 = 2.0 + 2.1 + nouveautés), vous choisissez une cible de conformité par version WCAG en combinant des ensembles de tags. La correspondance en OU du moteur sur tags (une seule correspondance suffit à inclure la règle) fait le reste :

// 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 critère va dans l’autre sens. WCAG 2.2 a retiré le critère 4.1.1 Analyse syntaxique, le seul critère jamais supprimé plutôt qu’ajouté. Une règle qui lui est associée porte son tag d’origine 2.0 (wcag2a) comme toute autre règle du socle, plus wcag22-removed, et les ensembles de tags par version ci-dessus l’incluent donc sous une cible 2.2, où elle n’a pas sa place.

Vous n’avez rien à faire pour autant. Le moteur résout une version WCAG cible pour chaque exécution et, lorsque cette cible est 2.2, une règle wcag22-removed ne peut pas renvoyer fail : elle s’exécute toujours et signale toujours chaque occurrence trouvée, mais son résultat est converti en cantTell et le résultat porte un champ wcagVersionScope qui en explique la raison (voir le schéma de sortie). Rien n’est supprimé en silence, et une exécution 2.2 n’est pas bloquée par un critère que 2.2 ne contient pas.

Avec une cible 2.0 ou 2.1, il en va de même : les errata de 2023 à WCAG 2.0 et 2.1 considèrent le 4.1.1 comme toujours satisfait pour du contenu en HTML ou XML, si bien que la règle pose la question au lieu d’échouer, et wcagVersionScope.by vaut "errata". Seule exception : un profil dont la norme conserve le texte du 4.1.1, comme en301549-v3.2.1 (keepsParsing dans le profil de la norme, voir Packs) : un id dupliqué peut encore y donner fail. Core 1.14.0 et ultérieur ; avant, une cible 2.0 ou 2.1 le laissait échouer.

La version cible est résolue dans cet ordre :

  1. engineOptions.wcagVersion ('2.0', '2.1' ou '2.2'), si vous l’avez défini.
  2. Le wcagVersion du profil de pack qu’applique l’exécution, s’il en nomme un : une norme fondée sur WCAG 2.1 dont le profil n’exécute que les règles qui lui sont associées ne sélectionne aucun tag WCAG, et dit donc la version elle-même. Il l’emporte sur la version qu’impliquent les propres tags du profil. Un profil sans effet (supplanté par une sélection de votre part) n’apporte aucune version.
  3. La version d’une cible runOnly.wcag.
  4. Les tags d’origine de version de votre propre filtre : un ensemble qui s’arrête à wcag21a/wcag21aa se lit comme une cible 2.1, un ensemble qui contient un tag wcag22* comme une cible 2.2, un ensemble ne contenant que des tags wcag2* comme une cible 2.0. Seuls ces neuf tags comptent : un tag de critère (wcag411) ou best-practice ne dit rien d’une version.
  5. Sinon '2.2', la cible par défaut de ce moteur.
// 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 cible résolue est renvoyée dans chaque résultat sous engine.wcagVersion, ce qui vous permet de vérifier celle qu’une exécution a réellement utilisée.

Si vous préférez ne pas voir du tout la règle sous 2.2, excluez-la purement et simplement ; le tag existe toujours précisément pour cela :

// WCAG 2.2 AA conformance, with the removed criterion left out entirely:
{
tags: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22a', 'wcag22aa'],
excludeTags: ['wcag22-removed']
}

duplicate-id est aujourd’hui la seule règle qui porte ce tag. Si vous la laissez, elle signale toujours un vrai problème (un id dupliqué casse <label for>, les liens d’ancre et getElementById, quoi qu’en dise la norme) ; ce n’est simplement pas un échec de conformité 2.2.

Avec engineOptions (sans runOnly)

Le même filtrage, exprimé sous forme de chaînes séparées par des virgules (ou de tableaux) imbriquées dans 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) et includeMode au premier niveau reprennent exactement les champs runOnly ci-dessus. Les chaînes séparées par des virgules sont automatiquement nettoyées de leurs espaces, dédupliquées et débarrassées des éléments vides. Une liste ou une chaîne seule, comme rules: ['img-alt-present'], est lue comme la liste d’inclusion.

engineOptions : les autres options

const engineOptions = {
strictOptions: false, // default false — true throws on an unknown option or a value of the wrong type
locale: 'en', // default 'en'; de-DE falls back to de, then to en per string
wcagVersion: '2.2', // default '2.2' — the conformance target
wcagLevel: 'AA', // optional — the level of the criteria reported
profile: 'wcag22-aa', // optional — a named conformance target
mappings: ['en301549'], // optional — standards besides WCAG to name on each result
optInRules: 'all', // optional — also run rules other standards add beyond WCAG
messages: { de: { /* key: text */ } }, // optional caller-supplied dictionaries; win over built-in ones
includeHiddenElements: false, // default false — set true to evaluate hidden/collapsed subtrees too
includeShadowDom: true, // default true — opt OUT with false to skip open shadow roots
fragment: false, // default false — set true when the scan target isn't a real page
excludeSelectors: ['#cookie-banner', '.third-party-widget'], // array or comma-separated string
styleSheetTexts: [{ href: 'https://cdn.example/site.css', text: '/* its CSS */' }], // sheets the page can't read
timestamp: '2026-07-20T12:00:00Z', // optional — engine has no built-in clock
perfStats: false, // default false — internal timing counters, debug-only shape
profileRules: false, // default false — per-rule timings; needs perfStats
 
contrast: {
mode: 'strictConformance', // 'strictConformance' (default) | 'auditorAssist'
rootCanvasFallback: '#ffffff', // a canvas to measure on in place of the page's own; unset, the page's color scheme gives it
rootCanvasFallbackSet: true // echoed on each result: whether rootCanvasFallback was given; read back, false means the default
},
visibilityMode: undefined, // 'styleOnly' | 'styleAndGeometry'; unset: geometry with a layout, else styles
 
policyContract: 'a11y', // 'a11y' (default) | 'generic' | inline contract object
policy: { // optional overrides on top of policyContract
coerceManualFailToCantTell: true
},
 
output: {
includeSelector: true, // set false to suppress auto-filled selectors
includeHtml: true,
detail: 'full' // 'findings': pass/notApplicable results keep only rule, outcome, type
},
 
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 */ ],
packs: [ /* rules and standards from outside core, see Packs */ ],
 
// Only read by runa11yCoreAcrossFrames. Ignored by runDomRulesInPage/runa11yCoreInPage.
pingWaitTime: 500, // ms to wait for a child frame to answer a ping
frameWaitTime: 60000 // ms to wait for a child frame's full scan result
};
OptionSignification
strictOptionsPar défaut false. true vérifie chaque option avant l’analyse, et lève INVALID_ENGINE_OPTIONS pour une clé que le moteur ne lit pas ou une valeur de mauvais type ou hors de son ensemble ; voir « Repérer les erreurs dans les options » ci-dessous.
localeN’importe quelle chaîne. Un code avec une sous-étiquette se rabat d’abord sur sa langue de base, donc de-DE utilise de ; à défaut, l’anglais. Chaque chaîne se rabat ensuite de la même façon (langue choisie → en → texte anglais littéral de la règle), si bien qu’une langue partiellement traduite ne produit jamais de texte manquant. Rien de tout cela ne se voit dans les chaînes elles-mêmes, donc le résultat indique ce qui s’est réellement passé dans engine.locale : consultez-le si vous devez savoir si vous avez obtenu la langue demandée. Voir Langues.
wcagVersion'2.0', '2.1' ou '2.2' : la version de WCAG par rapport à laquelle l’exécution teste la conformité. Par défaut, celle que nomme le profil de pack appliqué (son wcagVersion), sinon celle qu’impliquent vos tags d’origine de version, et '2.2' s’ils n’impliquent rien. Elle change les critères que rapporte une analyse et, pour le critère 4.1.1 Analyse syntaxique, la raison pour laquelle une règle portant le tag wcag22-removed ne peut pas renvoyer fail : retiré en 2.2, et toujours satisfait en 2.0 et 2.1 selon leurs errata, sauf sous un profil dont la norme le conserve, comme en301549-v3.2.1 (voir « Filtrer par version de WCAG » plus haut). Toute autre valeur, quel que soit son type (le nombre 2.1, '3.0'), est ignorée avec un console.warn qui nomme la version visée à la place par l’exécution.
wcagLevelFacultatif : 'A', 'AA' ou 'AAA', quelle que soit la casse. Le niveau WCAG des critères que rapporte une exécution. Les agrégations WCAG au-dessus de ce niveau sont omises de rulesResults tandis que leurs règles s’exécutent toujours ; l’option ne change jamais ce qui s’exécute. Elle l’emporte sur le niveau qu’implique la sélection ; une agrégation nommée par son id est rapportée telle que nommée. Renvoyé dans engine.wcagLevel, qui, sans l’option, contient le niveau qu’implique la sélection, s’il y en a un. Toute autre valeur est ignorée avec un console.warn, et lève une erreur avec strictOptions. Voir « Donner le niveau d’une analyse qui nomme des vérifications » plus haut.
profileCible de conformité nommée, facultative : 'wcag22-aa', 'en301549-v4.1.1', 'en301549-v3.2.1', 'section508' ou celle d’un pack. Sélectionne les règles d’après le jeu de tags correspondant quand rien d’autre n’en inclut, et la cible WCAG découle de ces tags. Renvoyée dans engine.profile quand elle a pris effet ; sinon ignorée avec un avertissement dans la console. Voir « Profils de conformité » ci-dessus.
mappingsNormes facultatives autres que WCAG dont meta.normativeMappings nomme les exigences : 'en301549', 'en301549:V3.2.1' ou 'en301549:V4.1.1', ou la clé de la norme d’un pack, sous forme de tableau ou de chaîne séparée par des virgules. Aucune par défaut : les résultats ne nomment alors que WCAG ; un profil EN 301 549 ajoute sa propre version. Renvoyé dans engine.mappings quand une norme s’applique. Voir « Autres normes » ci-dessus.
logUntestedWcagPar défaut true. false omet la note console.info pour un tag de version et de niveau WCAG qu’aucune règle ne porte (wcag22a), dans une liste d’inclusion à côté d’autres tags ; voir « Avec runOnly » ci-dessus.
optInRulesFacultatif : 'all', ou une liste de tags de règles optionnelles, sous forme de tableau ou de chaîne séparée par des virgules. Débloque ces règles hors du profil de leur norme ; le reste de la sélection décide toujours de ce qui s’exécute. Renvoyé dans engine.optInRules quand il a exécuté une règle que la sélection n’aurait pas exécutée autrement ; les tags inconnus sont ignorés avec un avertissement dans la console. Voir « Exécuter toutes les règles » ci-dessus.
messages{ [locale]: { key: text } } facultatif. Consulté avant les tables propres au moteur, il peut donc remplacer des chaînes individuelles ou fournir une langue que le build n’embarque pas. Les clés omises se rabattent normalement, donc un remplacement partiel ne pose aucun problème. C’est ainsi que le bundle navigateur autonome reçoit un fichier de langue séparé, et c’est le seul moyen de faire entrer un dictionnaire dans le contexte d’une page, puisque le runner in-page est sérialisé et ne peut pas lire de fichiers. Contrairement aux autres options, elle n’est pas renvoyée dans l’engineOptions de chaque résultat ; engine.locale indique le dictionnaire utilisé. Voir Langues.
includeHiddenElementsPar défaut false : les requêtes des helpers excluent les éléments masqués par des mécanismes structurels/CSS comme display:none, [hidden], un <details> fermé (tout sauf son premier <summary>, à toute profondeur), le contenu en content-visibility:hidden et en hidden="until-found", le contenu d’un sous-arbre [inert] (que les règles portant sur l’apparence du contenu, comme le contraste et l’espacement du texte, jugent toujours, puisqu’il est dessiné), et les éléments hôtes masqués servant uniquement au rendu (leurs descendants sont exclus aussi). runa11yCoreAcrossFrames n’analyse pas un frame masqué de cette façon. Passez-la à true pour inclure ces sous-arbres masqués/repliés dans l’évaluation (comportement historique/balisage statique). Elle élargit ce que renvoient les requêtes partagées ; la plupart des règles ne jugent toutefois que ce qui figure dans l’arbre d’accessibilité, puisqu’un élément masqué n’a ni nom ni rôle à vérifier, et pour elles cela ne change rien. Elle compte pour les règles qui portent sur le balisage lui-même, comme une référence d’ID cassée.
includeShadowDomPar défaut true : les règles qui utilisent helpers.queryAllSmart descendent dans les shadow roots ouvertes. Passez false pour n’analyser que le light DOM. Les shadow roots fermées ne sont jamais accessibles, dans un cas comme dans l’autre : aucune API DOM ne les expose. Un élément dont le nom pourrait provenir d’un élément personnalisé sans contenu lisible par le moteur (un bouton, un lien, un élément de menu, une option, un onglet, un élément d’arbre, un summary ou une infobulle) fait l’objet d’une question (cantTell, name_closedContent), et non d’un échec.
fragmentPar défaut false. Quelques règles vérifient la présence d’une propriété qui existe une fois par page réelle (page-title-present, html-lang-attr-present, html-xml-lang-mismatch, aria-hidden-body, css-orientation-lock, meta-refresh-no-exceptions, meta-refresh-timing-absent, meta-viewport-zoom-enabled, meta-viewport-large, page-title-patterns, region, bypass-blocks-present, landmark-one-main, page-has-heading-one) et renvoient à juste titre notApplicable pour ces propriétés dès que contextSelector a restreint l’exécution à moins que le document entier (document.documentElement ne figure plus parmi les racines résolues), puisqu’on n’a jamais attendu d’un sous-arbre ciblé qu’il porte son propre <title>/<html lang>/etc. Passez fragment: true pour le cas que le ciblage seul ne peut pas détecter : une cible d’analyse qui est le document fourni tout entier, mais qui n’a jamais été censée représenter une vraie page (par exemple un extrait de composant brut parsé isolément). Cela impose le même filtrage notApplicable même sans ciblage.
excludeSelectorsLes éléments qui correspondent à l’un de ces sélecteurs (ainsi que leurs descendants, y compris le contenu de leurs shadow roots et les documents des frames qu’ils contiennent) sont entièrement ignorés, pour toutes les règles. Utile pour les bannières de cookies, les contenus tiers intégrés ou les widgets connus pour être bruyants que vous ne maîtrisez pas. Pour exclure un élément d’une seule règle, utilisez plutôt rules[ruleId].excludeSelectors ci-dessous. Un tableau de sélecteurs, ou une chaîne contenant une liste de sélecteurs : '#cookie-banner, img:not(.a, .b)' équivaut à ['#cookie-banner', 'img:not(.a, .b)'], puisque la chaîne n’est coupée qu’aux virgules qui séparent les sélecteurs. Chaque sélecteur s’applique séparément : un sélecteur qui n’est pas du CSS valide n’exclut rien, avec un console.warn, et les autres s’appliquent toujours.
styleSheetTextsLe texte des feuilles de style que la page ne peut pas lire, [{ href, text }] : une feuille d’une autre origine, par son URL, et son CSS. Les règles qui lisent le CSS (css-orientation-lock, css-hidden-focus, css-focus-indicator-suppressed) le lisent à la place de la feuille. Le engineOptions de chaque résultat le reprend sous forme des href des feuilles, [{ href }], sans leur texte. preloadStyleSheets() récupère, depuis la page, celles que le serveur partage, et runa11yCoreInPageAsync les récupère avant l’analyse ; un outil qui pilote le navigateur peut récupérer les autres de l’extérieur. Voir « Style sheets from another origin » dans le guide d’intégration de core sur GitHub. Core 1.14.0 et ultérieur.
timestampTransmis tel quel au champ timestamp de premier niveau du résultat ; le moteur n’en génère pas lui-même (déterministe par conception). Une valeur qui n’est pas une chaîne (une Date, un nombre) est ignorée avec un console.warn ; passez new Date().toISOString().
contrast.modestrictConformance (par défaut) : les règles de contraste restent muettes (notApplicable/ignorées) chaque fois que l’arrière-plan réellement rendu ne peut pas être calculé avec certitude, pour éviter les faux fail. auditorAssist : sacrifie une partie de cette marge de sécurité pour obtenir davantage de constats ; conçu pour un auditeur humain qui revérifiera les cas signalés, pas pour un contrôle en CI sans supervision. Une page qui ne peint aucun arrière-plan opaque n’est pas un de ces cas : le canevas derrière elle découle du schéma de couleurs de la page elle-même, si bien que son texte est mesuré dans les deux modes sans supposition. Là où le canevas de la page ne peut pas être connu (dans un frame, en couleurs forcées), auditorAssist mesure le texte sur contrast.rootCanvasFallback, consigné comme ROOT_CANVAS_FALLBACK.
contrast.rootCanvasFallbackUne couleur de canevas pour une page qui ne peint aucun arrière-plan opaque. Sans elle, les règles de contraste mesurent le texte d’une telle page sur le canevas que le navigateur peint derrière elle, dans les deux modes : le canevas du schéma de couleurs dans lequel la page est dessinée, que décide son propre color-scheme (sur la racine, ou sa forme <meta>). Une page qui n’en déclare aucun est dessinée en clair, sur du blanc, quelle que soit la préférence de l’utilisateur ou de l’utilisatrice ; light dark est dessinée sur du blanc pour une personne en mode clair et sur le canevas sombre pour une personne en mode sombre, et son texte est mesuré dans les deux schémas, en échec là où il échoue dans l’un ou l’autre (data.details.colorScheme nomme lequel). Sous jsdom, qui ne résout aucune couleur système, le moteur utilise celles de Chromium, et le texte dont il ne peut pas y déterminer les couleurs fait l’objet d’une question (COLOR_SCHEME_NOT_RENDERED). Si elle est donnée, blanc compris, la couleur tient lieu de canevas de la page dans tous les schémas : le texte est mesuré une fois dessus, consigné comme une hypothèse (ROOT_CANVAS_FALLBACK). Le canevas de la page ne peut pas être connu dans un frame, dont le canevas peut laisser transparaître la page parente, ni en couleurs forcées : là, contrast-computable pose la question et les règles de rapport laissent le texte de côté, sauf si auditorAssist le mesure sur cette couleur. Le engineOptions.contrast de chaque résultat reprend la couleur utilisée, du blanc si aucune n’a été donnée.
contrast.rootCanvasFallbackSetRepris dans chaque résultat : true quand contrast.rootCanvasFallback vient de l’appelant, false pour le blanc par défaut (y compris quand la valeur donnée n’était pas une couleur utilisable). Inutile en entrée : des options repassées depuis la copie d’un résultat sont lues telles quelles, si bien que false garde le blanc repris comme valeur par défaut. Une valeur autre que true ou false donne un avertissement et est lue comme non donnée. Core 1.14.0 et ultérieur.
visibilityModeDétermine le degré d’exigence des trois règles de contraste (contrast-minimum, contrast-enhanced, contrast-computable) pour décider qu’un nœud texte est réellement éligible à la vérification. Aucune autre règle ne la lit. Non définie (par défaut), elle vaut 'styleAndGeometry' là où la page a une mise en page, c’est-à-dire dans toute analyse en navigateur, et 'styleOnly' là où elle n’en a pas (jsdom) : un lecteur ne voit que le texte dessiné à un endroit visible, ce que seule la mise en page permet de savoir. 'styleOnly' : l’éligibilité repose uniquement sur le CSS (display, visibility, opacity, masquage par un ancêtre, etc.). 'styleAndGeometry' : ajoute par-dessus de vraies vérifications de mise en page (getClientRects()/getBoundingClientRect()) ; le texte sans client rects, ou de largeur/hauteur nulle, est lui aussi exclu, de même que le texte situé entièrement au-dessus ou à gauche de la page, qu’aucun défilement n’atteint (la technique left: -9999px), et le texte qu’un ancêtre en overflow: hidden ou clip coupe entièrement (height: 0; overflow: hidden, une diapositive de carrousel hors de vue). Une boîte qui défile (auto, scroll) n’exclut pas son texte, puisqu’un lecteur peut l’atteindre. Les options d’un <select> sont jugées d’après la place de leur <select> dans la page. Dans les deux modes, le texte de taille de police 0 ou de couleur entièrement transparente est exclu, puisque rien n’est dessiné ; le texte transparent qui laisse voir un arrière-plan à travers ses glyphes (background-clip: text, sur lui ou un ancêtre) ne l’est pas : il est mesuré dans la couleur de cet arrière-plan quand elle est unie, et fait l’objet d’une question quand c’est un dégradé ou une image. Définissez 'styleOnly' dans un navigateur pour vérifier le texte à partir des seuls styles calculés, comme le faisaient les versions précédentes ; sous jsdom seul (runDomRulesInPage), il n’y a pas de vrai moteur de mise en page, donc 'styleAndGeometry' ajoute surtout des vérifications de taille nulle via getBoundingClientRect(), pas une vraie détection du rognage ou du débordement (voir les limites connues).
policyContractVoir POLICY.md : détermine quelles valeurs de résultat/confiance sont autorisées et si les fail que produiraient les règles manuelles sont convertis en cantTell.
output.includeSelector / .includeHtmlDésactive le remplissage automatique de selector/html par le moteur. Depuis que toutes les règles ont été migrées pour signaler leur élément plutôt que construire leurs occurrences à la main (1.5.0), ce remplissage est le chemin que suivent presque toutes les règles : définir includeSelector: false / includeHtml: false retire les sélecteurs et les extraits HTML de presque toutes. Quelques-unes assemblent encore ces champs elles-mêmes dans runInPage et ne sont pas concernées, parmi lesquelles contrast-minimum/contrast-enhanced (dont les constats portent sur des segments de texte, pas sur des éléments), page-title-present et identical-links-same-purpose. Cela réduit donc nettement la sortie, mais ne garantit toujours pas l’absence totale de sélecteurs ou de HTML dans le résultat.
output.detail'full' (par défaut) ou 'findings' : un résultat plus léger, qui ne garde complets que les résultats de vérification qui signalent quelque chose (fail, cantTell, et tout résultat qui liste des occurrences) ; chaque autre pass et notApplicable garde son ruleId, son outcome et son type, ainsi que sa margin ou son error s’il en a. Les agrégations et les verdicts ne changent pas, et les reporters le lisent comme un résultat complet. Une règle que le catalogue de core ne pourrait pas compléter reste complète : chaque règle d’une analyse avec packs, une règle personnalisée, et une règle que les messages de l’analyse reformulent. Voir engine.outputDetail ; en TypeScript, un tel résultat est un CompactScanResult.
rules[ruleId]Transmis à cette règle sous la forme ctx.config et, pour excludeSelectors en particulier, lu par le moteur lui-même avant même que la règle s’exécute. Voir « excludeSelectors par règle » ci-dessous. Toute autre clé est simplement transmise : aucune règle livrée n’en tire quoi que ce soit d’autre. Les paramètres déclarés par une règle, comme les seuils de contrast-minimum, sont ceux de sa norme : une valeur fournie par l’appelant est donc ignorée, et un résultat qui nomme WCAG 1.4.3 est toujours décidé à 4,5:1. Une norme qui a d’autres seuils a sa propre règle, une variante, qu’un pack peut apporter. Une clé est reconnue comme l’est un identifiant de règle, quelle que soit la casse ou avec l’ancien préfixe a11ycore- ; une clé qui ne nomme aucune règle de l’analyse déclenche un avertissement avec l’identifiant le plus proche.
packsPacks facultatifs issus de @surea11y/core/pack : des règles, des variantes des règles de core, une norme ou une liste de contrôle et des messages venus de l’extérieur de core, qui rejoignent l’analyse. Une liste : les objets pack pour runDomRulesInPage et les fonctions de catalogue dans Node, et pour runa11yCoreInPage le name@version des packs qu’un packScript a enregistrés dans la page. Toute autre valeur n’exécute aucun pack, avec un avertissement, et lève une erreur avec strictOptions, avec code: 'INVALID_ENGINE_OPTIONS'. Renvoyé dans engine.packs. Voir Packs.
probesUn objet de preuves facultatif, sérialisable en JSON, que fournit votre application hôte, pour ce que l’analyse d’une seule page ne peut pas voir, comme les autres pages du site. Le moteur le plafonne avant que les règles ne le lisent (ctx.inputs.probes) : six niveaux de profondeur, 200 éléments par tableau, 50 clés par objet, 2 000 caractères par chaîne. page-title-patterns lit crawl.pageTitles ({ pages: [{ url, title }] }) pour repérer les titres génériques et issus de gabarits sur tout un site. Les règles d’un pack peuvent lire leurs propres probes, que le pack documente.
perfStats / profileRulesRéservé au débogage. perfStats: true renvoie des compteurs internes dans le champ perfStats du résultat ; profileRules: true y ajoute en plus une ventilation des temps par règle. profileRules seul ne fait rien : c’est perfStats qui crée l’objet dans lequel figure cette ventilation. Sa forme ne fait pas partie du contrat de sortie stable : ne construisez rien dessus. Notez aussi que profileRules est la seule option qui rend la sortie non déterministe : les compteurs sont stables d’une exécution identique à l’autre, les temps mesurés ne le sont pas. Laissez-la désactivée si vous comparez des résultats entre exécutions. Avec profileRules, les caches partagés (styles calculés et éligibilité dans l’arbre d’accessibilité) sont remplis avant la première règle et chronométrés à part dans perfStats.warmUpMs, pour que leur coût ne soit pas imputé à la première règle qui parcourt la page.
pingWaitTime / frameWaitTimeLues uniquement par runa11yCoreAcrossFrames (voir INTEGRATION.md) : combien de temps attendre qu’un frame enfant réponde à un ping (par défaut 500 ms) et à une requête d’exécution complète (par défaut 60000 ms) avant de le considérer comme injoignable. Ignorées par runDomRulesInPage/runa11yCoreInPage.

Repérer les erreurs dans les options (strictOptions)

Une option que le moteur ne lit pas est transmise telle quelle : une règle personnalisée peut lire ses propres réglages dans ctx.engineOptions, et une option retirée dans une version ultérieure ne casse pas une analyse. Par défaut, une option mal orthographiée est donc ignorée, et l’analyse s’exécute comme si elle n’était pas là. Deux choses le signalent :

  • Toujours, une clé à une ou deux lettres près d’une clé connue (quelle que soit la casse) déclenche un avertissement, clés imbriquées comprises : lcoale donne [surea11y] engineOptions: unknown option "lcoale" (did you mean "locale"?); ignored.
  • Toujours, une valeur de mauvais type déclenche un avertissement et est lue comme non fournie, de même qu’un engineOptions qui n’est pas un objet. Les options booléennes (includeShadowDom, includeHiddenElements, fragment, perfStats, profileRules, output.includeHtml, output.includeSelector) acceptent true ou false, ou 'true', 'false', '1', '0', 1 et 0 tels qu’une ligne de commande les écrit : perfStats: 'false' est désactivé.
  • Avec strictOptions: true (ou 'true', '1' ou 1), chaque option est vérifiée avant l’analyse, et toute erreur lève une exception avec code: 'INVALID_ENGINE_OPTIONS', qui nomme chacune, problems les listant sous la forme { path, kind, message, suggestion } : une clé inconnue (kind: 'unknown'), dans engineOptions ou dans l’un de ses objets, et une valeur de mauvais type ou hors de son ensemble (kind: 'invalid'), comme includeHiddenElements: 'yes' ou contrast.mode: 'strict'.
runDomRulesInPage(url, null, { strictOptions: true, lcoale: 'de' }, null);
// Error: engineOptions: unknown option "lcoale" (did you mean "locale"?). (strictOptions)

Les fonctions de catalogue qui acceptent des engineOptions (getChecksCatalog, getRulesCatalog, getCheckDefById, getCompositeRuleById, getChecksForRunOnly, getRulesForRunOnly, getTestsForRunOnly) les vérifient de la même façon, dans les mêmes termes : getRulesForRunOnly(runOnly, { wcagLevel: 'AA+' }) émet un avertissement, lit le niveau comme non donné, et liste ce que rapporte une analyse avec ces options ; avec strictOptions: true, elle lève INVALID_ENGINE_OPTIONS, comme l’analyse. Une sélection qui s’annule elle-même (une règle à la fois incluse et exclue, des parties sans aucune règle en commun) est signalée comme l’analyse la signale : un avertissement, et INVALID_RUN_ONLY sous strictOptions.

Non vérifiés : les réglages propres à une règle (rules[ruleId]), les clés de messages, et probes, que le moteur transmet sans les lire ; ainsi que les règles personnalisées, qui ont leurs propres vérifications (skippedCustomRules). En TypeScript, StrictEngineOptions est le même ensemble d’options sans la signature d’index qui laisse compiler n’importe quelle clé : une option mal orthographiée y est donc aussi une erreur.

excludeSelectors par règle

Le excludeSelectors de premier niveau s’applique à toutes les règles : impossible, avec lui seul, d’exclure un élément d’une seule règle tout en continuant à lui appliquer toutes les autres. rules[ruleId].excludeSelectors comble ce manque : il restreint les candidats pour cette seule règle, en plus de la liste globale (jamais à sa place).

const engineOptions = {
excludeSelectors: ['#cookie-banner'], // applies to every rule, as always
rules: {
'aria-required-children': {
excludeSelectors: ['mat-select', 'mat-stepper', 'mat-horizontal-stepper', 'mat-vertical-stepper']
},
'aria-allowed-attr': {
excludeSelectors: ['mat-progress-spinner']
}
}
};

Pourquoi en avoir besoin : le <mat-select> d’Angular Material construit sa structure ARIA interne d’une manière qui déclenche un faux positif sur aria-required-children en particulier, alors que le composant est correct par ailleurs. Avec le seul excludeSelectors global, l’unique moyen de faire taire ce faux positif est excludeSelectors: ['mat-select'], ce qui masque aussi mat-select à toutes les autres règles, dont contrast-minimum et aria-allowed-attr, et supprime en silence une couverture réelle avec laquelle ces vérifications n’avaient aucun problème. L’exemple ci-dessus laisse mat-select entièrement visible pour toutes les règles sauf celle qui se trompe à son sujet.

Les exclusions effectives d’une règle donnée sont l’union de la liste globale et de la liste propre à cette règle : un élément qui correspond à l’une ou à l’autre est retiré des candidats de cette règle. Une règle dont tous les éléments qui auraient échoué sont exclus de cette façon renvoie outcome: 'pass' ou 'notApplicable' (selon sa propre convention en l’absence de candidats), avec occurrences: [], et jamais outcome: 'fail' avec un tableau occurrences vide, puisque le fail d’une règle intégrée nomme toujours ce qui a échoué. (Une règle qui lève une exception est un autre cas : elle renvoie cantTell avec error renseigné, voir le schéma de sortie.)

Accepte les mêmes formes que l’option globale : un tableau (['mat-select', 'mat-stepper']) ou une chaîne séparée par des virgules ('mat-select, mat-stepper').

Si vous utilisez un package de binding (@surea11y/binding-base et ses wrappers Playwright/Puppeteer), vérifiez dans le README de ce binding si sa méthode de builder .exclude() dispose déjà d’une forme par règle : il s’agit d’une forme d’engineOptions documentée ici au niveau du moteur, et tous les bindings ne l’ont pas encore reprise.

Recettes : combiner les options pour des scénarios réels

Contrôle en CI : WCAG 2.2 AA uniquement, en ignorant un widget tiers que vous ne maîtrisez pas

runDomRulesInPage(url, null, {
excludeSelectors: ['#cookie-banner', '.intercom-launcher']
}, { wcag: { version: '2.2', level: 'AA' } });

Ajoutez bestPractices: true à côté de wcag pour exécuter aussi les règles de bonnes pratiques, qu’un contrôle WCAG rapporte en général sans échouer à cause d’elles. Certaines peuvent renvoyer fail (empty-heading) : un contrôle qui échoue sur tout fail échoue donc aussi sur celles-là.

Auditeur humain qui fait une passe approfondie sur le contraste dans un vrai navigateur : sacrifiez une partie de la protection contre les faux positifs pour obtenir davantage de constats. Dans un vrai navigateur, les règles de contraste vérifient déjà la mise en page, et pas seulement les styles calculés : seul le mode de contraste est à définir. Exemple avec page.evaluate de Puppeteer (qui accepte plusieurs arguments) ; si vous êtes sur Playwright, regroupez d’abord les quatre arguments positionnels dans un seul objet (voir INTEGRATION.md) :

const result = await page.evaluate(runa11yCoreInPage, url, null, {
contrast: { mode: 'auditorAssist' }
}, null);

Nouvelle analyse ciblée d’une zone après une modification de l’interface, sans le shadow DOM : utile dans un test au niveau composant où seul le widget que vous venez de modifier vous intéresse :

runDomRulesInPage(url, '#checkout-form', {
includeShadowDom: false
}, { includeRuleIds: ['form-control-programmatic-label-present', 'button-name-present'] });

Sortie pour les tests de snapshot : fixez un timestamp pour que l’horodatage propre au résultat ne change pas d’une exécution à l’autre, et laissez perfStats et profileRules désactivés : leurs temps varient, et leur forme ne fait pas partie de la sortie stable. Un snapshot exige aussi le même DOM, les mêmes options et les mêmes conditions de rendu (navigateur, viewport, polices) à chaque exécution ; le timestamp seul ne rend pas deux résultats identiques.

runDomRulesInPage(url, null, {
timestamp: '2026-01-01T00:00:00Z'
}, null);

Une règle personnalisée propre à votre organisation, à côté des règles intégrées, pour ce seul appel :

runDomRulesInPage(url, null, {
customRules: [{
id: 'org-no-inline-onclick',
meta: { title: 'No inline onclick handlers', defaultSeverity: 'moderate' },
runInPage(ctx) {
// queryAllSmart applies excludeSelectors, the scope's shadow roots and
// the hidden-content filter; queryAll is the plain light-DOM query.
// ctx.helpers.dom reads the DOM so the page's markup can't redirect it.
const dom = ctx.helpers.dom;
const els = ctx.helpers.queryAllSmart('[onclick]');
const occurrences = els.map((el) => ({
selector: ctx.helpers.buildSelector(el),
html: dom.outerHTML(el),
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 : règles enregistrées à l’exécution

Toutes les règles livrées sont intégrées à src/core.js au moment du build. engineOptions.customRules est la porte de sortie à l’exécution : un tableau de descripteurs de règles enregistrés pour ce seul appel. Rien n’est ajouté au catalogue statique et rien ne persiste d’un appel à l’autre. Les fonctions de catalogue acceptent les mêmes engineOptions et listent ce que contient une analyse faite avec elles : getChecksCatalog(engineOptions) liste les règles personnalisées valides, chaque surcharge à la place de sa règle intégrée, getCheckDefById(id, engineOptions) les trouve, getChecksForRunOnly(runOnly, engineOptions) les sélectionne comme le fait l’analyse, et getRulesForRunOnly(runOnly, engineOptions) liste les agrégations qu’elles ajoutent. C’est voulu, pas une limitation à contourner : surea11y reçoit déjà des engineOptions neuves à chaque appel, sans configuration globale mutable, et les règles personnalisées suivent ce même modèle par appel.

Appeler directement la bibliothèque n’est qu’une des possibilités ; la CLI expose aussi cette fonction via --custom-rules <path> (un fichier local, chargé une fois par analyse). Voir la documentation de la CLI.

Une règle personnalisée, comme celle d’un pack, s’exécute après les règles intégrées, et les ctx.helpers, ctx.engineOptions et ctx.inputs.probes qu’elle reçoit sont en lecture seule : y définir ou y supprimer quoi que ce soit lève une exception (la règle est signalée comme cantTell avec cette erreur), si bien qu’elle ne peut pas modifier ce que voient les autres règles. Elle ne doit pas modifier la page : une règle qui le fait est nommée dans un console.warn. Les résultats gardent l’ordre du catalogue. Quoi qu’elle lève ou renvoie, l’analyse renvoie un résultat, et le résultat de la règle est converti en données simples, si bien que le résultat de l’analyse peut être écrit en JSON et cloné.

Un descripteur a la même forme que l’export d’un module de règle interne : si vous savez déjà écrire un fichier de règle pour ce moteur, vous connaissez déjà cette API :

{
id: 'my-org-custom-rule', // required
meta: { 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 applicability
data: { /* optional, JSON-serializable */ }
}
  • runInPage/applicability peut être une vraie fonction ou une chaîne contenant le source d’une fonction (c’est-à-dire fn.toString()). Passez une vraie fonction lorsque engineOptions ne quitte jamais le realm JS courant (usage simple sous Node/jsdom). Passez une chaîne dans le cas contraire, par exemple lors d’un appel Playwright page.evaluate(runa11yCoreInPage, { engineOptions }), où engineOptions franchit une frontière JSON/structured clone qui ne peut pas transporter une référence Function vivante, mais peut transporter une chaîne. Le moteur reconstruit une chaîne via new Function, le même mécanisme que le build utilise pour intégrer le source de chaque règle intégrée dans le runner in-page. Le source de n’importe quelle fonction convient : une fonction fléchée, une déclaration function ou une méthode écrite en syntaxe abrégée comme dans l’exemple ci-dessus (runInPage(ctx) {…}, async aussi), dont le toString() n’est pas une expression à lui seul. La reconstruction exige que eval soit autorisé : une page dont la Content Security Policy interdit 'unsafe-eval' ignore une règle fournie sous forme de chaîne, et la raison indiquée dans skippedCustomRules désigne cette politique ; passez-y une vraie fonction.
  • meta bénéficie des mêmes valeurs par défaut et de la même validation qu’une règle intégrée au build : omettez tout ce dont vous n’avez pas besoin ; defaultSeverity vaut par défaut moderate, defaultConfidence medium, type automatic, etc. Une valeur hors de son ensemble (minor/moderate/serious/critical ; high/medium/low ; automatic/manual, sans tenir compte de la casse) est un meta qui échoue à la validation, et la règle est ignorée. Une severity renvoyée par une règle hors de l’ensemble est remplacée par sa valeur par défaut, avec une note dans error.
  • Une règle personnalisée dont l’id entre en collision avec celui d’une règle intégrée la remplace pour cette analyse, au lieu que les deux s’exécutent. Comme une règle personnalisée homonyme a autant de chances d’être une collision accidentelle qu’un remplacement délibéré, chaque collision est signalée de deux façons : un console.warn qui nomme le ou les ids, et un tableau overriddenBuiltinIds au premier niveau du résultat (vide en l’absence de collision). Voir le schéma de sortie. Les ids sont reconnus quelle que soit la casse : une règle personnalisée IMG-ALT-PRESENT remplace img-alt-present et prend son id, et un second avertissement nomme les deux graphies.
  • Une règle personnalisée qui s’est exécutée compte dans l’agrégation WCAG (rulesResults) de chaque critère auquel elle est associée, par meta.wcagSc ou par une entrée WCAG dans meta.normativeMappings, comme une règle intégrée : si elle échoue, l’agrégation échoue. L’agrégation la marque custom: true parmi ses contributrices et la liste dans data.details.customChecksIds. Une règle personnalisée que la sélection nomme pour elle-même (par son id ou l’un de ses tags) et qui s’est exécutée rapporte les agrégations de ses critères même quand la sélection n’en nomme aucun (« Les critères que rapporte une analyse »). Une surcharge compte là où sa propre correspondance l’indique : à la place de sa règle intégrée quand elle est associée à ce critère, et dans aucune agrégation quand elle n’est associée à aucun, avec un avertissement qui nomme les agrégations qu’elle a quittées.
  • Une règle personnalisée est sélectionnée comme une règle intégrée, par son id et par ses meta.tags. Un runOnly qui nomme une règle personnalisée que l’analyse a ignorée lève INVALID_RUN_ONLY, avec la raison pour laquelle elle a été ignorée. customRules est une liste : une règle fournie seule n’est pas exécutée, et elle est listée dans skippedCustomRules. Sous un profil ou une cible WCAG, une règle portant le tag best-practice, ou sans tag WCAG, ne fait pas partie de l’exécution : elle est listée dans skippedCustomRules, avec un console.warn, et s’exécute quand vous lui donnez les tags des critères qu’elle vérifie ('wcag111') ou ajoutez runOnly.bestPractices: true. Une règle personnalisée que la sélection exclut (excludeRuleIds: ['org-b'], rules.exclude...) y est listée aussi, avec la raison excluded by the selection. et excluded: true, sans avertissement. Une surcharge s’exécute partout où sa règle intégrée l’aurait fait.
  • Une règle personnalisée peut signaler une marge comme une règle intégrée : déclarez meta.margin ({ measure, unit, limit }) et renvoyez marginCandidates (voir RULE_AUTHORING.md, meta.margin, le guide d’écriture de règles sur GitHub). Un meta.margin mal formé est ignoré, et cette règle ne signale alors aucune marge ; l’analyse continue.
  • Un descripteur invalide (id absent ou qui n’est pas une chaîne, runInPage qui n’est ni une fonction ni une chaîne source reconstructible, applicability qui ne peut pas être converti en fonction, meta qui échoue à la validation, comme deprecated: true sans deprecation, id déjà porté par une autre règle personnalisée de la liste, ou id d’une règle composite) est ignoré avec un console.warn qui nomme la règle et la raison, et listé dans le skippedCustomRules du résultat ; le reste de l’analyse, y compris toutes les règles intégrées, s’exécute normalement. Ce n’est pas une lacune de validation à corriger : une règle personnalisée est du code arbitraire fourni par l’appelant, donc « écarter cette seule entrée, ne pas interrompre l’analyse » est le comportement par défaut le plus sûr, à l’image d’une règle intégrée qui lève une exception et qui est cantonnée à un cantTell pour cette règle au lieu de faire planter l’exécution.
  • Les règles s’exécutent de façon synchrone. Un runInPage ou un applicability qui renvoie une Promise (une fonction async) est signalé comme cantTell avec une error qui l’indique, tout comme un runInPage qui renvoie autre chose qu’un objet résultat. Un outcome hors de pass, fail, cantTell et notApplicable (comme 'failed') devient cantTell, et error nomme la valeur renvoyée. Le type d’une règle vient de son meta : un type dans ce qu’elle renvoie est ignoré, avec une note dans error.
  • Les résultats apparaissent dans checksResults exactement comme ceux de n’importe quelle autre règle, y compris le remplissage automatique de selector/html/structuralPath pour les occurrences fail/cantTell qui ne joignent que { __node } (voir le schéma de sortie).
  • Un fail nomme ce qui a échoué. Le fail d’une règle personnalisée sans occurrence (un échec de la page entière) est signalé avec une occurrence sur l’élément du document (selector: 'html', code de raison FAIL_WITHOUT_OCCURRENCE), pour que JUnit, SARIF, les baselines et le rapport HTML l’affichent comme l’échec qu’il est.

Packs : règles et normes venues de l’extérieur de core

Un pack apporte des règles, des variantes des règles de core, une norme ou une liste de contrôle, ainsi que les messages qu’ils utilisent, depuis un paquet qui lui est propre, sans build de core. Passez les packs à une analyse dans engineOptions.packs ; l’analyse s’exécute sur un moteur préparé avec eux, selon les étapes qu’utilise le build pour les règles propres à core, si bien que la norme, les profils, les agrégations et les règles optionnelles d’un pack se comportent comme ceux d’une norme intégrée. Le moteur est conservé pour la prochaine analyse avec les mêmes objets pack : un crawl qui passe les mêmes packs à chaque page ne les prépare qu’une fois.

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

Un pack incompatible avec core (sa plage core exclut cette version, un identifiant de règle sans l’espace de noms, une clé de dictionnaire que core possède déjà) est ignoré avec un console.warn, listé dans le skippedPacks du résultat sous la forme { name, reason }, et l’analyse s’exécute sans lui ; avec strictOptions, elle lève une erreur, avec code: 'INVALID_ENGINE_OPTIONS'. Des packs en conflit entre eux lèvent une erreur. Dans une page, un pack est d’abord enregistré avec packScript(packs) et nommé dans engineOptions.packs sous la forme name@version. Ce que contient un pack, comment l’utiliser depuis chaque binding, test-matchers et la CLI, et comment en écrire un sont décrits dans Packs.

contextSelector (2ᵉ argument du runner, pas un champ d’engineOptions)

Un sélecteur CSS (ou un tableau de sélecteurs) qui limite l’analyse à un ou plusieurs sous-arbres, résolu via document.querySelectorAll (toutes les correspondances, pas seulement la première). Passez null pour analyser le document entier.

  • Un sélecteur qui ne correspond à rien n’analyse rien. Toutes les règles renvoient notApplicable, y compris celles qui portent sur la page entière, et le contextMatch du résultat l’indique : { elementCount: 0, unmatchedSelectors: ['#mian'] } (voir le schéma de sortie). L’exécution émet aussi un console.warn. Un périmètre absent de la page n’est pas élargi à la page entière : un résultat sur un contenu que vous n’avez jamais demandé, avec un pass portant sur toute la page, se lirait comme un résultat sur la partie que vous avez demandée. Dans un tableau, un sélecteur qui ne correspond à rien est listé dans unmatchedSelectors tandis que les autres sont analysés normalement ; un contenu qui n’est présent que de temps en temps, comme une bannière de cookies, peut donc figurer dans la liste. Avant la 1.10.0, un périmètre sans correspondance se rabattait sur le document entier, sans rien en dire dans le résultat.
  • Un sélecteur que le navigateur ne sait pas analyser lève une exception, seul ou dans un tableau, avant l’exécution de toute règle : contextSelector: "#main[" is not a valid CSS selector. L’erreur porte code: 'INVALID_CONTEXT_SELECTOR' et selector, le sélecteur fautif. Contrairement à un sélecteur qui ne correspond à rien, c’est toujours une erreur dans l’appel. runa11yCoreAcrossFrames rejette avec la même erreur. Une erreur qui traverse page.evaluate (Playwright, Puppeteer) ne garde que son message ; c’est pourquoi le message nomme l’option et le sélecteur. Avant la 1.10.0, un sélecteur invalide était ignoré, si bien qu’employé seul il analysait le document entier.
  • Toute valeur autre qu’un sélecteur, un tableau de sélecteurs ou null lève la même erreur, en nommant ce qui a été passé : un élément, une NodeList, un objet { include, exclude }, un nombre, ou un élément de tableau qui n’est pas une chaîne (contextSelector[1] must be a CSS selector, not the number 5.). Passez plutôt un sélecteur qui correspond à l’élément, et placez ce qu’un exclude écarterait dans engineOptions.excludeSelectors.
  • Une chaîne unique peut elle-même être une liste de sélecteurs séparés par des virgules (sémantique d’union CSS ordinaire) : '#a, #b' analyse à la fois #a et #b.
  • Un tableau de chaînes analyse l’union des correspondances de chaque sélecteur : ['#a', '.card'] se comporte comme '#a, .card' ; la forme tableau existe pour les appelants qui construisent la liste par programme.
  • Shadow DOM. Avec includeShadowDom (par défaut), un périmètre inclut les shadow roots qu’il contient, et un périmètre qui est lui-même un shadow host inclut sa propre shadow root. Le sélecteur est évalué sur le document : il ne peut donc pas sélectionner un élément à l’intérieur d’une shadow root ; ciblez plutôt son hôte.
  • Les zones qui se chevauchent ou s’imbriquent sont automatiquement dédupliquées : un élément atteignable depuis plusieurs racines correspondantes n’est jamais signalé qu’une fois, et non une fois par zone.
  • Ce que signifie un résultat limité à un périmètre. La plupart des règles jugent chaque élément isolément, et jugent les éléments du périmètre exactement comme dans une analyse de la page entière. Deux sortes de règles dépendent de la page qui les entoure. Les règles portant sur la page entière (page-title-present, landmark-one-main et les autres listées sous fragment ci-dessus) renvoient notApplicable. Les règles qui comparent des éléments à l’échelle de la page (landmark-unique, landmark-no-duplicate-banner, -contentinfo et -main, landmark-banner-is-top-level, -complementary-, -contentinfo- et -main-is-top-level, accesskeys, heading-order, identical-links-same-purpose, identical-iframes-same-purpose) signalent toujours ce qu’elles trouvent dans le périmètre, puisque deux landmarks qui y partagent un nom le partagent sur n’importe quelle page, mais renvoient notApplicable au lieu de pass : l’analyse n’a vu qu’une partie de la page et ne peut donc pas affirmer que la page est conforme. duplicate-id et duplicate-id-aria comparent chaque élément du périmètre avec le document entier, puisque son doublon peut se trouver hors du périmètre, et signalent un élément du périmètre dont l’id apparaît ailleurs. Leur pass signifie qu’aucun élément du périmètre ne partage son id avec un autre élément de la page ; une paire de doublons entièrement hors du périmètre n’est pas signalée, comme pour toute règle, et ce pass ne dit donc pas que la page entière est exempte de doublons. target-size-minimum juge les cibles du périmètre par rapport à leurs voisines n’importe où dans le document, puisque l’exception d’espacement dépend de ce que la page place à côté d’une cible. text-spacing-content-loss juge le texte du périmètre et vérifie s’il en vient à chevaucher du texte ailleurs sur la page.