Schéma de sortie
Voici la forme exacte de l’objet renvoyé par runDomRulesInPage(...) / runa11yCoreInPage(...) (voir INTEGRATION.md pour savoir lequel appeler). Chaque exemple de cette page est une sortie réelle du moteur actuel (schemaVersion: "1.0.0"), pas un exemple écrit à la main. runa11yCoreAcrossFrames renvoie une forme différente, récursive, qui enveloppe celle-ci ; voir Résultat multi-frames ci-dessous.
Résultat de premier niveau
{engine: {tag: string,version: string, // the @surea11y/core release that produced the resultschemaVersion: string,locale: { requested: string, resolved: string, reason: string },wcagVersion: "2.0" | "2.1" | "2.2",wcagLevel?: "A" | "AA" | "AAA",profile?: string, // "wcag22-aa", "en301549-v4.1.1", "en301549-v3.2.1", "section508", or a registered standard's ownmappings?: string[], // e.g. ["en301549"] or ["en301549:V3.2.1"]profileExcludes?: { rules: string[], criteria: string[] },optInRules?: string[],outputDetail?: "findings",packs?: string[], // name@versionenvironment: {layout: boolean,viewport?: { width: number, height: number }, // CSS pxdevicePixelRatio?: number,colorScheme?: "light" | "dark",forcedColors?: "active" | "none",forcedColorsCss?: true, // under forced colors, when the page's own CSS reacts to themfonts?: "loaded" | "loading",images?: "loaded" | "loading",animationsSettled?: number,internalsHook?: "early" | "late", // only where the page runs the internals hooklistenerHook?: "early" | "late", // only where the page runs the listener hookliveRegionHook?: "early" | "late" // only where the page runs the live-region hook}},url: string | null,title: string | null,timestamp: string | null,perfStats: object | null,contextSelector: string | string[] | null,contextMatch: { elementCount: number, unmatchedSelectors: string[] } | null,checksResults: CheckResult[],rulesResults: CompositeResult[],standards?: Array<{ key: string, standard: string, titleLang?: string, note?: string }>,overriddenBuiltinIds: string[],skippedCustomRules: Array<{ id: string | null, reason: string, excluded?: true }>,skippedPacks?: Array<{ name: string | null, reason: string }>}
| Champ | Signification |
|---|---|
engine.tag | Le tag d’identité propre au moteur, actuellement "a11ycore". Chaque règle (intégrée ou personnalisée) le porte dans meta.tags ; les ruleId des règles, eux, sont nus (sans préfixe). |
engine.version | La version de @surea11y/core qui a produit le résultat, par exemple "1.11.1" : c’est elle qu’il faut citer dans un rapport de bug, et elle permet de distinguer des résultats de deux versions. Un résultat peut changer d’une version à l’autre sans que sa forme change, donc ce n’est pas schemaVersion. Les générateurs SARIF et EARL l’utilisent comme version de l’outil lorsque vous n’en indiquez aucune. |
engine.schemaVersion | La version du schéma de résultat ("1.0.0"). Elle change si la forme décrite ici évolue un jour de façon incompatible : fixez-vous dessus si vous analysez la sortie par programme. Voir API_STABILITY.md pour la liste complète des champs stables et instables et la politique d’incrément de version. |
engine.locale | Le dictionnaire que l’exécution a réellement utilisé. requested est votre engineOptions.locale après suppression des espaces ("en" si vous n’avez rien passé ou autre chose qu’une chaîne) ; resolved est la locale dont le dictionnaire a été utilisé ; reason explique la correspondance entre les deux. Comme le repli vers une autre langue se fait en douceur et chaîne par chaîne, demander une langue que le build n’embarque pas produit du texte en anglais, et non une erreur : ce champ vous permet de le savoir sans lire les chaînes. Indiqué une fois par résultat : une exécution utilise un seul dictionnaire du début à la fin. |
engine.locale.reason | "ok" : vous avez obtenu le dictionnaire demandé, et il contient toutes les clés. "primary-subtag" : votre code comportait une sous-étiquette sans dictionnaire propre, donc sa langue de base a été utilisée : "de-DE" se résout en "de". "dictionary-not-loaded" : le projet fournit cette langue, mais ce build ne l’embarque pas et aucune n’a été fournie (le bundle navigateur autonome, sans son fichier de langue séparé). "unknown-locale" : le projet n’a aucune traduction de ce type. "partial-dictionary" : le dictionnaire a été utilisé, mais il lui manque des clés présentes en anglais, si bien que ces chaînes se sont rabattues sur l’anglais. Considérez cet ensemble comme ouvert : de futures versions pourront le compléter. |
engine.wcagVersion | La version de WCAG par rapport à laquelle cette exécution a vérifié : votre engineOptions.wcagVersion, ou le wcagVersion du profil de pack que l’exécution a appliqué, ou ce qu’impliquaient vos tags d’origine de version, ou la valeur par défaut "2.2". Pour le critère 4.1.1 Analyse syntaxique, elle explique pourquoi une règle associée uniquement à ce critère ne peut pas renvoyer fail : la version 2.2 l’a retiré, et les errata de 2023 des versions 2.0 et 2.1 le considèrent comme toujours satisfait (voir checksResults[i].wcagVersionScope ci-dessous). Indiquée une fois par résultat : une exécution a une seule cible du début à la fin. |
engine.wcagLevel | Le niveau WCAG qui a filtré les critères signalés, "A", "AA" ou "AAA" : engineOptions.wcagLevel quand il a été fourni et est valide, sinon le niveau que donne la sélection (runOnly.wcag.level, ou le niveau le plus élevé que nomment ses tags de version/niveau, ceux d’un profil compris). Les agrégations WCAG au-dessus de ce niveau ne figurent pas dans rulesResults, bien que leurs règles se soient exécutées et figurent dans checksResults ; une agrégation que la sélection a nommée par son id est signalée quoi que dise engineOptions.wcagLevel. Absent quand l’analyse n’avait pas de niveau, comme une analyse complète ou une analyse qui ne nomme que des règles, qui signale tous les niveaux. Les reporters filtrent par ce niveau les critères qu’ils nomment. Voir Donner le niveau d’une analyse qui nomme des vérifications. |
engine.profile | Présent seulement quand un engineOptions.profile a sélectionné les règles de cette exécution ; nomme le profil, en minuscules. Absent quand aucun n’a été demandé, et aussi quand un profil a été demandé mais ne s’est pas appliqué (nom inconnu, ou un include dans runOnly ou engineOptions a choisi les règles à sa place) : sa présence est donc ce qui confirme qu’une exécution a vraiment visé ce profil. Voir Profils de conformité. |
engine.profileExcludes | Présent seulement quand le profil appliqué écarte des règles : { rules, criteria }, les règles qu’il nomme et les critères WCAG qu’il écarte. Les règles qu’il nomme, les règles dont il écarte tous les critères et les agrégations WCAG de ces critères ne se sont pas exécutées ; une règle qui vérifie aussi un critère que le profil conserve s’est exécutée, et compte pour ce critère. Cela restreint l’analyse sans changer ce que WCAG exige. Aucun profil intégré n’écarte quoi que ce soit. |
engine.optInRules | Présent seulement quand engineOptions.optInRules a exécuté au moins une règle optionnelle que le reste de la sélection n’aurait pas exécutée. Liste leurs tags. Sa présence signifie que le résultat inclut des règles pour des exigences au-delà de la norme visée : un échec peut donc ne pas être un échec WCAG. Voir Exécuter toutes les règles. |
engine.mappings | Présent seulement quand l’exécution nomme une norme autre que WCAG dans meta.normativeMappings, via engineOptions.mappings ou le profil d’une norme. Les liste sous forme canonique : "en301549" pour toutes les versions, "en301549:V3.2.1" pour une seule. Son absence signifie que chaque résultat ne nomme que WCAG. Voir Autres normes. |
engine.packs | Présent seulement quand l’analyse s’est exécutée avec engineOptions.packs : chaque pack qui s’est exécuté, sous la forme name@version, trié par nom. Voir Packs. |
engine.outputDetail | Présent seulement quand engineOptions.output.detail vaut 'findings', sous la forme "findings" : chaque résultat de vérification pass ou notApplicable qui ne liste aucune occurrence est compact, { ruleId, outcome, type } plus sa margin ou son error s’il en a, sans le titre, la description et le meta, qui appartiennent au catalogue et sont les mêmes sur chaque page. Les résultats fail et cantTell, les agrégations et engine restent complets : aucun verdict ne change. Les reporters (SARIF, JUnit, le rapport HTML, EARL, les fonctions de baseline) lisent un résultat compact comme le résultat complet, en le complétant à partir du catalogue de core. Un résultat que ce catalogue ne pourrait pas compléter reste complet : chaque résultat de vérification d’une analyse avec packs, celui d’une règle personnalisée, et celui dont les messages de l’analyse ont modifié le titre ou la description. Le résultat d’une page vide passe d’environ 263 Ko à 90 Ko. |
engine.environment | Les conditions dans lesquelles la page a été rendue, lues sur la page au début de l’analyse, avant l’exécution de toute règle. Les règles qui mesurent la mise en page, comme text-spacing-content-loss, peuvent donner un autre résultat pour le même balisage à une autre largeur de viewport ou pendant que des polices web sont encore en cours de chargement : c’est donc ce dont vous avez besoin pour reproduire une exécution. layout indique si la page avait une mise en page : false dans jsdom et les autres émulateurs de DOM, et le viewport et les autres champs de rendu sont alors omis, puisqu’ils ne décrivent rien de mesuré ; seuls internalsHook, listenerHook et liveRegionHook peuvent figurer à côté, là où la page les exécute. Dans un navigateur, viewport vaut innerWidth/innerHeight en pixels CSS, devicePixelRatio celui de la page (le zoom du navigateur s’y reflète aussi), colorScheme ce à quoi correspond prefers-color-scheme, forcedColors si forced-colors: active correspond ("active" sous le contraste élevé de Windows ou les couleurs forcées émulées de Chromium, "none" sinon ; les règles mesurent les propres couleurs de la page dans les deux cas, voir Forced colors), forcedColorsCss vaut true sous couleurs forcées lorsque le CSS de la page elle-même y réagit d’une façon que l’analyse ne peut pas neutraliser (une media query forced-colors, qui correspond toujours, ou une couleur système comme GrayText, qui prend toujours celle de la palette forcée), si bien que les résultats de contraste peuvent différer d’une analyse sans couleurs forcées, et fonts vaut "loading" tant qu’une des polices de la page est encore en cours de chargement, "loaded" sinon. Un champ que la page ne peut pas fournir est omis. Lire ces valeurs n’ajoute aucun non-déterminisme : comme le DOM, ce sont des entrées. Pour des résultats comparables d’une exécution à l’autre, attendez document.fonts.ready avant d’analyser. images vaut "loading" tant qu’une image est encore en cours de chargement, "loaded" sinon : une image qui arrive plus tard peut déplacer ce que les règles de mise en page ont mesuré, donc pour des résultats comparables, analysez après l’événement load de la page. Une image avec loading="lazy" est ignorée, puisqu’elle attend que le lecteur fasse défiler la page. animationsSettled est le nombre d’animations et de transitions en cours que l’analyse a figées : pour l’analyse, une animation finie (un fondu d’apparition) est lue à sa fin et une animation qui se répète indéfiniment (un texte défilant, un indicateur de chargement) à son début, et chacune est remise exactement où elle était ensuite, si bien qu’une page analysée pendant un fondu d’apparition donne le même résultat qu’une page analysée après. Le navigateur n’affiche jamais l’état figé, puisque l’analyse est une seule tâche synchrone. internalsHook n’est présent que là où la page exécute le hook d’internals optionnel (internalsHookScript(), voir INTEGRATION.md) dans le document analysé, avec ou sans mise en page : "early" quand il a été installé avant les scripts de la page et reste l’attachInternals de la page, si bien que l’analyse a lu les ElementInternals de chaque élément personnalisé et qu’un élément qui n’en a pas ne pose aucune question ; "late" quand il a été installé après eux, ou que la page a remplacé attachInternals après lui, si bien que seuls les internals attachés après lui ont été lus. Sans hook, le champ est absent et le résultat est le même qu’avant. listenerHook fonctionne de même pour le hook d’écouteurs optionnel (listenerHookScript(), voir INTEGRATION.md) : "early" quand il a été installé avant les scripts de la page et reste l’addEventListener de la page, si bien que les règles clavier ont vu chaque écouteur ajouté par script et qu’un écouteur absent prouve qu’il n’y en a pas ; "late" quand il a été installé après eux, ou que la page a remplacé addEventListener après lui. liveRegionHook n’est présent que là où la page exécute le hook de régions live optionnel (liveRegionHookScript(), voir INTEGRATION.md) : "early" quand il a été installé avant la fin du chargement du document, si bien qu’il a vu chaque changement des régions live de la page depuis ; "late" sinon. Chaque frame d’un résultat multi-frames porte le sien. |
url | L’argument pageUrl que vous avez passé, ou document.location.href si vous avez passé null ou l’avez omis (ou avez passé autre chose qu’une chaîne, ce qui journalise aussi un console.warn), ou null si ni l’un ni l’autre n’est disponible. |
title | document.title au moment de l’analyse, ou null. |
timestamp | Non généré automatiquement. Défini uniquement si vous passez engineOptions.timestamp sous forme de chaîne non vide : le moteur n’a pas d’horloge intégrée (déterministe par conception). Si vous voulez un horodatage d’analyse dans le résultat, fournissez-le vous-même. |
perfStats | null sauf si engineOptions.perfStats: true. Temps et compteurs internes ; forme non couverte par ce document, à considérer comme réservée au débogage. |
contextSelector | L’argument contextSelector que vous avez passé (sans espaces superflus) : une chaîne, un tableau de chaînes (analyse multizone, voir Options du moteur), ou null s’il est absent ou vide. |
contextMatch | La façon dont le contextSelector a été résolu, ou null s’il n’y en avait pas. elementCount est le nombre d’éléments distincts trouvés, les racines de l’analyse. unmatchedSelectors liste chaque sélecteur, tel que vous l’avez donné, qui ne correspondait à aucun élément ; une chaîne séparée par des virgules compte ici pour un seul sélecteur, comme elle compte pour un seul appel à querySelectorAll. Un elementCount de 0 signifie que rien n’a été analysé : la page n’a pas cette zone, chaque entrée de checksResults et de rulesResults vaut notApplicable, et le résultat ne dit rien de la page. Présentez-le comme une zone introuvable, pas comme une analyse sans constat. Un sélecteur que le navigateur ne sait pas analyser n’atteint jamais un résultat : l’analyse lève une erreur avec code: 'INVALID_CONTEXT_SELECTOR' (voir Options du moteur). |
checksResults | Une entrée par règle atomique exécutée (toute règle non écartée par runOnly, voir Options du moteur). Chaque règle chargée produit une entrée, même celles dont le résultat est notApplicable : ce n’est pas une liste limitée aux violations. |
rulesResults | Une entrée par règle composite (agrégation) exécutée ; voir Un résultat composite et Conformité. En principe une par critère de succès WCAG ; une exécution sous le profil d’une norme dotée de ses propres agrégations reçoit aussi celles-ci (avec meta.standard). Il contient les agrégations que la sélection retient, et les agrégations des critères vérifiés par une règle que la sélection a nommée seule (par son id, par exemple), qui peuvent être partiellement vérifiés (voir Les critères que rapporte une analyse). Tableau vide quand ni l’une ni l’autre n’en donne. |
standards | Présent seulement quand le résultat nomme une norme autre que WCAG dans une entrée de meta.normativeMappings ou dans le meta.standard d’une agrégation : un { key, standard, titleLang?, note? } par norme enregistrée qu’il nomme, y compris celle d’un pack, dans l’ordre du registre. key est la clé de la norme ("en301549"), standard le nom que portent ses entrées ("EN 301 549"), titleLang la langue dans laquelle sont rédigés les titres de ses propres agrégations quand ce n’est pas celle de l’analyse, et note le texte affiché au-dessus de ses propres agrégations dans le rapport HTML, dans la langue de l’analyse. Les reporters le lisent à la place du registre, si bien qu’un résultat enregistré s’affiche de la même façon où qu’il soit rendu, que cette installation connaisse la norme ou non. Une norme que seule une règle personnalisée nomme n’est pas listée. |
skippedPacks | Présent seulement quand un pack de engineOptions.packs ne s’est pas exécuté : chacun sous la forme { name, reason }, name valant null quand le pack n’en avait pas. Dans une page (runa11yCoreInPage), name est le name@version fourni, et la raison indique pourquoi la page ne l’a pas exécuté : aucun appel à packScript n’a enregistré exactement l’ensemble nommé, ou le script a été préparé avec une autre version de core. Chacun est aussi journalisé avec console.warn. |
overriddenBuiltinIds | Les ids de règle pour lesquels une entrée de engineOptions.customRules partageait son id avec une règle intégrée, si bien que l’implémentation personnalisée a remplacé la règle intégrée pour cette analyse (voir Options du moteur). Toujours un tableau ; vide en l’absence de collision. Également journalisé via console.warn au moment de l’analyse, puisqu’une règle personnalisée homonyme a autant de chances d’être une collision accidentelle qu’un remplacement délibéré. |
skippedCustomRules | Les entrées de engineOptions.customRules qui n’ont pas été exécutées, chacune sous la forme { id, reason } (id vaut null lorsque l’entrée n’en avait pas) : pas d’id, un runInPage qui n’est pas une fonction ou ne peut pas être reconstruit à partir de son code source, un meta qui échoue à la validation, un id qu’une autre règle personnalisée porte déjà, ou l’id d’une règle composite ; ou une règle valide que la sélection de l’exécution ne retient pas (sous un profil ou une cible WCAG, une règle portant le tag best-practice ou sans tag WCAG), dont la reason indique comment la sélectionner ; ou une règle que les exclusions de la sélection écartent (excludeRuleIds, excludeTestIds, excludeTags, rules.exclude…), dont la reason est excluded by the selection. et qui a aussi excluded: true, pour qu’un outil distingue le choix de l’appelant d’une lacune sans lire la raison. Toujours un tableau ; vide lorsque toutes les règles personnalisées ont été exécutées. Chacune est aussi journalisée avec console.warn, sauf une règle exclue, que l’appelant a demandé d’écarter. Une règle listée ici ne figure dans aucun résultat, ce qui permet de distinguer une analyse qui l’a écartée d’une analyse où elle a donné pass. |
Résultat multi-frames (runa11yCoreAcrossFrames)
runa11yCoreAcrossFrames (voir INTEGRATION.md) renvoie une forme différente, récursive, au lieu d’un simple résultat de premier niveau :
{topFrame: <the normal top-level result shape above>,frames: Array<| { url: string | null, selector: string | null, title: string | null,topFrame: <top-level result>, frames: [...same shape, recursively] }| { url: string | null, selector: string | null, title: string | null, error: string }>}
topFramea exactement la forme du résultat de premier niveau, pour le frame dans lequel la fonction a été appelée.framescontient une entrée par<iframe>/<frame>enfant direct dans le périmètre analysé (shadow roots ouvertes comprises, ainsi que le périmètre lui-même quand c’est un frame), sauf un frame que la page n’affiche pas et un frame qu’exclutexcludeSelectors, qui sont écartés avec tout leur document. Chaque entrée nomme son élément frame :selectorest un sélecteur CSS de l’<iframe>/<frame>dans le document parent, ettitleest l’attributtitlede l’élément, ounull, si bien que des frames qui chargent le même document se distinguent.urlest le document qu’affiche le frame ou, pour un frame qui n’a pas encore navigué, sonsrc. Un enfant joignable (qui a appeléa11yCoreEnableFrameResponder()) fournit son propre{ url, topFrame, frames }complet, y compris ses propresframesimbriqués, récursivement, puisqu’un petit-enfant plus profond n’est joignable que par l’intermédiaire de son parent immédiat. Un enfant injoignable (le cas courant pour la plupart des contenus tiers intégrés : pas de responder coopératif, ou délai dépassé) fournit à la place{ url, error }, sans interrompre le reste de l’analyse.- Il s’agit d’un arbre, pas d’une liste à plat : une différence délibérée avec le
.frames(true)du binding@surea11y/playwright, qui peut aplatir parce que lepage.frames()de Playwright fournit déjà tous les frames quelle que soit leur profondeur d’imbrication ; un relaispostMessagen’a pas cette vue globale, donc l’imbrication est exprimée par la structure. - Les générateurs de rapports prennent l’arbre tel quel.
renderHtmlReport,renderSarifReport,renderJunitReport,buildBaselineEntries,matchBaselineetrenderEarlReporten rapportent chaque frame : chaque frame qui a répondu, avec ses propres constats, localisés dans son propre document, et chaque frame qui n’a pas répondu comme non analysé, pour que le trou dans la couverture ne se lise pas comme une réussite. Un frame est nommé par son chemin, lesselectordes éléments frame qui y mènent depuis la page, du plus extérieur au plus intérieur (#checkout → iframe:nth-of-type(2)). Ils lèvent uneTypeErrorpour tout ce qui n’est ni un résultat d’analyse ni un résultat multi-frames (un tableau,nullouundefinedissu d’une analyse qui a échoué), au lieu de le présenter comme une analyse qui n’a rien trouvé. - Ils acceptent aussi l’analyse de frames d’un binding. Le
.frames(true)des bindings navigateur renvoie{ topFrame, frames }, chaque frame étant un résultat simple (ou{ url, error }) plutôt qu’une entrée imbriquée ; les générateurs de rapports,getMarginsetflattenCrossFrameResultlisent chacun de ces frames comme analysé, nommé par le chemin['iframe'], puisqu’il ne porte pas deselector. flattenCrossFrameResult(result), de@surea11y/core, liste les frames tels que les lisent les générateurs de rapports :[{ frame: { path, title, url }, result }], ouerrorà la place deresultpour un frame qui n’a pas répondu, la page d’abord et chaque frame avant les frames qu’il contient. Lepathde la page est[]. Un résultat d’analyse simple donne une seule entrée.
Un résultat de vérification (checksResults[i])
{ruleId: string,outcome: "pass" | "fail" | "cantTell" | "notApplicable",outcomeNormalized: "pass" | "fail" | "cantTell" | "inapplicable",severity: "minor" | "moderate" | "serious" | "critical",ruleSeverity?: "minor" | "moderate" | "serious" | "critical", // the rule's own, when a profile gave it anotherpolicyOutcome?: "pass" | "fail" | "cantTell" | "notApplicable", // what the rule found, when the policy made it cantTellconfidence: "high" | "medium" | "low",type: "automatic" | "manual",occurrences: Occurrence[],title: string,description: string,i18n: { titleKey: string, descriptionKey: string, variantOfTitleKey?: string } | null,meta: {ruleId: string,ruleInterfaceVersion: string,ruleVersion: string,normative: boolean,atomic: boolean,deprecated: boolean,deprecation: object | null,category: "perceivable" | "operable" | "understandable" | "robust" | null,helpUrl: string, // where to read how to fix it; "" when there is nonetags: string[], // the rule's tags, its own and the engine'snormativeMappings: Array<{ standard: string, version: string, requirement: string, title: string,conformanceLevel?: string, wcagSc?: string[], url?: string, understandingUrl?: string }>,standard: string | null,applicability: string,expectation: string,references: string[],requirements: object | null,mappings: object | null},engineOptions: object, // the resolved engineOptions this rule actually ran underschemaVersion: string,rollupIds: string[], // the rulesResults entries that group this rule in this run; [] if nonevariantOf?: string, // a pack's variant only: the id of the rule it runs at other settings; i18n.variantOfTitleKey is that rule's titleKeydata?: object, // page-level data a rule reports whatever its outcome; see belowmargin?: { // the closest a measurement came to its threshold while meeting it; see belowmeasure: string, // "contrast-ratio" | "overflow-px" | "target-size-px" | ... (open set)unit: "px" | "ratio", // open setlimit: "min" | "max",threshold: number,value: number,headroom: number, // >= 0measuredCount: number,selector?: string,shadowHostSelectors?: string[], // as on an occurrencestructuralPath?: number[],context?: object // rule-specific; not a stable contract},wcagVersionScope?: { // present only when the target WCAG version changed this outcometarget: "2.0" | "2.1" | "2.2",removedSc: string[],coercedFrom: "fail",by?: "errata" // 2.0 or 2.1: their errata count the criterion as always satisfied},error?: string // present when the rule did not complete, or the engine noted a change — see below}
Remarques :
outcomeetoutcomeNormalized: identiques, sauf quenotApplicabledevient"inapplicable"dansoutcomeNormalized. Les deux sont fournis pour que vous puissiez vous aligner soit sur votre propre vocabulaire, soit sur celui du moteur.- Sous la politique par défaut
a11y, les règlestype: "manual"renvoientcantTellplutôt quefail. Si la logique propre d’une règle manuelle aurait conclu àfail, le moteur le convertit encantTellet ajoute une note explicative àerror. Ce comportement est imposé de façon centralisée (policy.coerceManualFailToCantTell, activé par défaut sous le contrat de politiquea11y; voirPOLICY.md), et ce n’est pas à chaque règle d’y penser. Sous cette politique,failest réservé aux constats déterministestype: "automatic"uniquement. meta.normativeMappingsest ce qui relie un résultat de vérification à un critère de succès WCAG. Une liste vide signifie que la règle n’a aucune correspondance formelle dans ce résultat. Elle ne dit pas si la règle est automatique ou manuelle : c’esttypequi l’indique (aria-allowed-role, par exemple, est automatique et n’a aucune correspondance WCAG). Voir Conformité pour la façon dont elles sont agrégées. La liste n’est pas réservée à WCAG. Quand l’analyse demande une autre norme (engineOptions.mappings, ou le profil d’une norme), chaque critère WCAG est suivi de l’exigence correspondante de cette norme, comme la clause d’EN 301 549 qui le reprend ({ standard: "EN 301 549", version: "V3.2.1" | "V4.1.1", requirement: "9.1.1.1", title, wcagSc: ["1.1.1"] }, une entrée par version qui inclut le critère) ; une règle peut aussi citer les documents Understanding de WCAG (type: "Understanding"). Filtrez surstandard(et sur l’absence detype) avant de lirerequirementcomme un critère de succès. L’entrée WCAG d’un composite vient toujours en premier. Voir EN 301 549.rollupIdsliste les composites derulesResultsqui regroupent cette règle dans cette exécution, y compris celles signalées parce que la sélection a nommé la règle seule. Une liste vide signifie que les résultats de la règle n’apparaissent dans aucune agrégation : qui ne lit querulesResultsne les voit jamais ;heading-order, par exemple, n’appartient à aucune agrégation WCAG.wcagVersionScope: présent uniquement lorsque la version WCAG cible de l’exécution a transformé lefailde cette règle encantTell. Aujourd’hui, cela concerne une règle associée au critère 4.1.1 Analyse syntaxique (duplicate-id) : sous 2.2, qui a retiré ce critère, et sous 2.0 et 2.1, dont les errata de 2023 le considèrent comme toujours satisfait pour HTML et XML (by: "errata"). Un profil dont la norme conserve le texte du 4.1.1 (en301549-v3.2.1, ou le profil d’un pack aveckeepsParsing) n’est pas converti.removedScliste les critères concernés,targetest la version de l’exécution, etcoercedFromest le résultat que la règle a elle-même renvoyé. Les occurrences sont celles de la règle, inchangées : rien n’a été supprimé, seul le verdict de conformité l’a été. Absent de tous les autres résultats, et jamais signalé viaerror: rien ne s’est mal passé. Voir Filtrer par version de WCAG.data: présent seulement sur une règle qui rapporte quelque chose sur la page entière, quel que soit son résultat. Aucune règle du cœur ne le fait aujourd’hui. Commedata.detailssur une occurrence, ce n’est pas un contrat stable.margin: présent seulement sur une règle qui en déclare une dans le catalogue (getChecksCatalog()[i].margin,{ measure, unit, limit, band? }), lorsqu’au moins un élément a atteint le seuil de la règle et, si la déclaration a uneband, s’en est approché dans cette bande.band.relativeest une fraction du seuil :contrast-minimum,contrast-enhancedetlink-in-text-block, avec0.1, ne rapportent une marge que pour un ratio à moins de 10 % de celui requis (de 4,5 à 4,95 pour 4,5:1, de 3 à 3,3 pour 3:1).band.absoluteest une quantité enunit:target-size-minimum, avec6, ne rapporte une cible que jusqu’à 30 px, ettext-spacing-content-loss, avec8, une ligne qu’à partir de 6 px du bord de sa boîte. Un texte à 21:1 ou un bouton de 48 px n’est proche de rien, et aucune marge ne le dit. Il désigne l’élément qui s’est le plus approché du seuil tout en le respectant, si bien qu’unpassà 4,52:1 pour un seuil de 4,5:1, ou à 0,4 px de tronquer le texte, se distingue d’unpassconfortable. Il accompagne le résultat et n’est pas un constat : il n’ajoute, ne retire ni ne modifie jamais une occurrence, et les baselines, SARIF, JUnit et EARL l’ignorent. Il figure aussi sur les résultatsfailetcantTelllorsque certains éléments ont satisfait la règle.limitvautminquand la valeur doit atteindre le seuil (contraste, taille) etmaxquand elle doit rester en dessous (débordement) ;headroomest la marge restante, enunit, soitvalue - thresholdpourminetthreshold - valuepourmax, jamais négative.thresholdest le seuil appliqué à cet élément, qui peut varier d’un élément à l’autre (3:1 pour du texte de grande taille). Les pixels sont donnés à une décimale : les pixels CSS et des seuils comme la moitié d’une police de 15px sont fractionnaires, et un affichage les arrondit pour les personnes. Un rapport de contraste n’est pas arrondi, puisque les WCAG n’arrondissent pas le contraste. En cas d’égalité, c’est le premier élément dans l’ordre du document qui l’emporte, si bien qu’une page donne toujours la même marge.measureetunitsont des ensembles ouverts qui peuvent s’enrichir dans une version mineure ;contextest propre à la règle et, commedata.details, ce n’est pas un contrat stable.getMargins(result)renvoie toutes les marges d’un résultat sous la forme[{ ruleId, ...margin }], triées parruleId; pour un résultat multi-frames, celles de chaque frame, celles d’un frame enfant avecframe, le chemin qui y mène.error: présent quand la règle ne s’est pas terminée, ou quand le moteur a modifié ou ignoré une partie de ce que la règle a renvoyé. Une règle qui a levé une exception (ou renvoyé quelque chose qui n’est pas un résultat) apparaît toujours sous la formeoutcome: "cantTell"avecoccurrences: []eterrorcontenant le message de l’exception :cantTellsans occurrence et avec uneerrorpermet donc de reconnaître une règle qui ne s’est pas terminée ; le moteur ne laisse jamais une règle défaillante faire planter toute l’analyse. Sinon,errorcontient les notes du moteur, jointes par|après touteerrorque la règle a elle-même renvoyée : la conversion des échecs manuels décrite ci-dessus, unoutcomeou uneseverityhors de son ensemble, untypedifférent de celui de son meta, des occurrences qui ne sont pas des objets (écartées), unpolicyOutcomeou unruleSeverityrenvoyé par la règle (non retenu : le moteur définit les deux), ununcertainty.codehors de l’ensemble fermé, et unfailramené àcantTellparce que le parcours des ancêtres, limité à 200 étapes, s’est arrêté avant chacun des éléments signalés. Ces résultats gardent leurs occurrences : la règle s’est terminée.policyOutcome: présent quand lesallowedOutcomesde la politique n’autorisent pas le résultat trouvé par la règle. Le résultat est alorscantTell, avec ici le résultat propre de la règle et sanserror, pour que les générateurs de rapports ne le lisent pas comme une règle qui ne s’est pas terminée.engineOptionssur chaque résultat est l’objet d’options résolu (après application des valeurs par défaut de locale et de contraste), et non littéralement ce que vous avez passé. Utile pour vérifier ce qu’une règle donnée a réellement reçu, en particulier lalocalerésolue etcontrast.mode/contrast.rootCanvasFallback.contrast.rootCanvasFallbackvaut blanc lorsque l’appelant n’en a donné aucun, etcontrast.rootCanvasFallbackSetindique lequel des deux :truepour une couleur donnée par l’appelant,falsepour la valeur par défaut. Il ometmessages, les dictionnaires avec lesquels l’exécution a traduit (un fichier de locale séparé dans le bundle pour navigateur, ou vos propres chaînes) : ce sont des données, pas un réglage, et une copie sur chaque résultat faisait peser une analyse des dizaines de mégaoctets.engine.localeindique quel dictionnaire a été utilisé. Trois autres options sont reprises telles que les règles les ont vues, pour que le résultat reste de simples données queJSON.stringifyetstructuredClone(postMessage) peuvent transporter :probessous la forme de la copie plafonnée que lisent les règles,customRulessous la forme[{ id }], une entrée par règle personnalisée dotée d’un id, sans ses fonctions, etstyleSheetTextssous la forme[{ href }], une entrée par feuille fournie, sans son texte, puisque le CSS d’une page répété sur chaque résultat était multiplié par le nombre de règles.
Une occurrence (occurrences[i])
Un fail en a toujours au moins une : le fail d’une règle personnalisée qui ne nomme aucun élément en reçoit une sur l’élément du document (selector: "html", code de raison FAIL_WITHOUT_OCCURRENCE). Normalement présente uniquement lorsque outcome vaut fail ou cantTell : un résultat pass a occurrences: [], puisque ce moteur n’énumère pas les éléments qu’il a validés, seulement ceux qu’il a signalés. Les règles de contraste font exception : leur pass porte une occurrence qui décrit l’analyse (la quantité de texte éligible et calculable), avec un selector vide, pour qu’un pass sans aucun texte se distingue d’un pass sur une page pleine de texte.
notApplicable est la seule exception. Une règle qui n’avait rien à juger peut joindre une occurrence unique expliquant pourquoi, et c’est exactement ce que font les règles de contraste lorsqu’aucun texte n’avait d’arrière-plan calculable : la différence entre « vérifié, rien à signaler » et « impossible à vérifier » est une différence que ce moteur signale au lieu de la masquer. Une telle occurrence décrit l’analyse, pas un élément, donc son selector est vide. Ne lisez pas occurrences.length comme un nombre de violations sans vérifier d’abord outcome.
{selector: string,html: string,structuralPath: number[] | null,shadowHostSelectors?: string[],summary: string,hint: string,i18n: { summaryKey: string, hintKey: string, params: object } | null,occurrenceOutcome?: "fail" | "cantTell", // present when the rule graded its findings into tiersuncertainty?: { // present only on a cantTell-tier occurrencecode: "not-computable" | "runtime-dependent" | "spec-only"| "equivalence-unknown" | "judgement-required" | "out-of-scope",needed?: string, // what would settle the questionevidence?: object // what the rule did establish, rule-specific},lean?: { // present only on a cantTell-tier occurrence, and only with evidencetoward: "pass" | "fail",why: string, // localized: what in the page points that waysignals: { signal: string, value: string | number | boolean | null }[],caution?: string, // localized: what could make it wrongmeasured?: { agreed: number, cases: number, corpus?: string, coreVersion?: string },i18n?: { whyKey?: string, cautionKey?: string, params?: object }},howToCheck?: { // present only on a cantTell-tier occurrence, when the rule sayssteps: string[], // localized: what a person does, in orderpassWhen: string, // localized: what they see if it passesfailWhen: string // localized: what they see if it fails},data?: { // absent on manual-review's page-level findingvisibilityFilter?: { eligible?: boolean, targetSet: string, accEligible: boolean | null, reasons: string[] },details?: object // rule-specific, non-normative — see below}}
| Champ | Signification |
|---|---|
selector | Un sélecteur CSS construit au mieux pour retrouver l’élément signalé (voir helpers.buildSelector dans RULE_AUTHORING.md). Un id, un test id, un name ou un aria-label ne sert de point d’ancrage que lorsqu’aucun autre élément du document ne le porte, éléments masqués, exclus et hors périmètre compris, puisque querySelector le résout sur tout le document ; sinon, le sélecteur est un chemin. Son unicité n’est pas garantie pour toutes les structures DOM : un élément situé dans une shadow root est nommé dans son propre arbre (structuralPath le situe), et le chemin d’un élément très profond est raccourci par le haut. Sur 255 pages de test et pages réelles, 15 des 39 857 sélecteurs ne désignaient pas exactement leur élément, tous de ces types. L’exception est une règle dont le constat est un élément absent : page-title-present signale head > title avec un html valant <title>(missing)</title>, dont aucun n’existe sur la page. Ce sont deux constantes, donc l’empreinte qu’elles alimentent reste stable, mais ne considérez pas selector comme résolvable ni html comme du vrai balisage sans vérifier que la règle a signalé quelque chose qui existe. Pour un élément situé dans un arbre shadow, selector se résout dans sa shadow root, et shadowHostSelectors indique comment y parvenir. |
shadowHostSelectors | Présent uniquement pour un élément situé dans un arbre shadow : les sélecteurs des shadow hosts qui y mènent, du plus externe au plus interne, chacun résolu dans l’arbre qui le contient, de sorte que document.querySelector(s[0]).shadowRoot.querySelector(s[1])…shadowRoot.querySelector(selector) trouve l’élément. Deux composants au contenu identique donnent le même selector et se distinguent ici. Comme selector, il ne fait pas partie de l’identité d’un constat. Le rapport HTML, SARIF (logicalLocations), JUnit et les entrées de baseline affichent les deux comme un seul emplacement, les hôtes puis le sélecteur joints par >>> (#card >>> img). |
html | Un extrait outer-HTML de l’élément signalé, coupé à 2 000 caractères : utile pour montrer l’élément et, pour un élément situé dans un arbre shadow, à lire avec shadowHostSelectors. Pour <html>, <head> et <body>, qui contiennent toute la page, c’est la seule balise ouvrante (<html class="x">), si bien qu’un constat portant sur la page garde son identité quand le reste du contenu change. |
structuralPath | Le chemin d’indices de frères de l’élément signalé, depuis documentElement jusqu’à lui (par ex. [1, 0, 2]) ; [] si l’élément est documentElement, null s’il n’a pas pu être déterminé, ce qui inclut tout élément situé dans un arbre shadow (utilisez alors shadowHostSelectors et selector). Un mécanisme d’identification d’élément plus robuste que selector seul : il résiste à des changements du DOM qui casseraient une chaîne de sélecteur (le renommage d’un id ou d’une classe, par exemple), au prix de ne pas pouvoir servir de vrai sélecteur CSS. |
summary | Lisible par un humain, déjà localisé (« This button has no accessible name. »). |
hint | Conseil de correction lisible par un humain, déjà localisé. |
i18n | Les clés de traduction brutes derrière summary/hint, si vous voulez les afficher vous-même dans une autre langue sans relancer l’analyse. null si l’occurrence n’a pas utilisé d’i18n à base de clés. |
data.visibilityFilter | Présent sur la plupart des occurrences : pourquoi le moteur a jugé cet élément éligible (ou non) selon le modèle d’éligibilité utilisé par la règle. eligible en est le résultat (absent sur les constats de niveau page de bypass-blocks-present, html-lang-attr-present et page-title-present) ; targetSet indique quel modèle l’a produit ('dom' : visibilité DOM/CSS brute, pour la plupart des règles ; 'acc' : éligibilité selon l’arbre d’accessibilité). accEligible reprend eligible uniquement lorsque targetSet vaut 'acc', sinon null. reasons est une liste de codes d’exclusion lisibles par une machine lorsque eligible: false. |
data.details | Données structurées propres à la règle (métriques calculées, références résolues), non normatives : utiles pour construire une interface plus riche ou pour déboguer, mais elles ne changent jamais le sens de outcome/severity. Leur forme varie selon la règle ; considérez-les comme un contexte supplémentaire fourni au mieux, pas comme un contrat stable. La seule exception est data.details.reasonCode, qui est stable : il identifie lequel des constats d’une règle il s’agit, et forme avec ruleId et html l’empreinte sur laquelle reposent les baselines et SARIF. Une règle peut recevoir un nouveau code de raison dans une version mineure, ou en scinder un selon sa cause ; voir API_STABILITY.md. Les champs de taille et de ratio d’un échec de contraste (metrics.ratio, metrics.threshold, et, dans typography, fontSizePx, fontSizePt, fontWeight, isBold et isLargeText) sont stables eux aussi, pour qu’un rapport ou un pack sache quel type de texte a échoué, de même que element dans les constats de list-children-valid, parentElement dans ceux de listitem-parent-valid et role dans ceux de aria-role-name-present, qui indiquent quel type de liste ou de widget a échoué. La page de chaque règle liste les champs que portent ses constats, sous « Ce que rapporte un constat ». |
occurrenceOutcome | Le niveau auquel appartient cette occurrence, pour une règle qui a réparti ses constats entre un niveau fail certain et un niveau cantTell à revoir. Une règle qui ne signale qu’un seul niveau l’omet ; dans ce cas, l’outcome du résultat lui-même est le niveau de l’occurrence. occurrenceOutcome(check, occurrence), de @surea11y/core, donne le niveau tel que le lit chaque reporter, dans les deux cas. Le résultat concorde toujours avec les niveaux : toute occurrence de niveau fail le rend fail, et un fail dont toutes les occurrences sont de niveau cantTell devient cantTell, si bien que SARIF, JUnit, EARL et les baselines donnent une seule réponse. Le moteur tranche pour une règle personnalisée ou de pack qui les renvoie en désaccord ; sous la politique par défaut, une règle manuelle n’est jamais fail. Les reporters lisent un résultat enregistré avant que le moteur ne le fasse selon la même règle, sous la politique que consignent ses engineOptions, et recalculent ses agrégations WCAG à partir de ce qu’ils ont lu. |
uncertainty | Pourquoi ce constat n’a pas pu être tranché ; voir Codes d’incertitude ci-dessous. |
lean | De quel côté penche ce constat non tranché, lorsque la page fournit des indices dans un sens ; voir Tendances ci-dessous. |
howToCheck | Comment une personne tranche ce constat ; voir Comment vérifier ci-dessous. |
Codes d’incertitude
Un cantTell indique que le moteur n’a pas tranché. uncertainty indique pourquoi, à partir d’un vocabulaire fermé, pour qu’un consommateur puisse agir selon la raison plutôt que d’analyser une chaîne de résumé. Il n’est présent que sur une occurrence de niveau cantTell : une occurrence de niveau fail affirmerait que la règle a à la fois tranché et pas tranché, donc le moteur le retire. Une incertitude dont le code ne fait pas partie des codes ci-dessous est également écartée, et l’error de la règle nomme ce code.
| code | Signification | Cas typique |
|---|---|---|
not-computable | Les éléments dont la règle avait besoin n’ont pas pu être lus dans cet environnement. | Une feuille de style d’une autre origine, une couleur d’arrière-plan qui ne se résout en aucune valeur, un src qui ne se résout pas. |
runtime-dependent | Le balisage ne permet pas de trancher, car c’est un script qui décide à l’exécution. | Un aria-controls qui désigne un élément que le widget construit à son ouverture. |
spec-only | Une vraie violation de la spécification, mais le nom, le rôle et la valeur exposés n’en souffrent pas : aucun critère de succès n’est donc établi comme non satisfait. | Un attribut ARIA dont la spécification fournit une valeur par défaut en cas d’absence. |
equivalence-unknown | Deux éléments servent peut-être le même objectif, peut-être pas, et ni le balisage ni le contenu ne permettent de trancher. | Deux frames qui partagent un nom accessible mais intègrent des ressources différentes. |
judgement-required | La question relève par nature d’une décision humaine. | Savoir si une cible trop petite est essentielle ; toute règle type: "manual". |
out-of-scope | Le constat est réel, mais sort du cadre de la norme visée par cette exécution. | Une règle associée uniquement à un critère que la version WCAG cible a retiré. |
needed indique, en une phrase, ce qui permettrait de trancher : ce que la personne qui relit doit aller vérifier. evidence contient ce que la règle a établi, pour que cette personne parte du travail du moteur au lieu de le refaire ; sa forme est propre à la règle et, comme data.details, ne constitue pas un contrat stable. Le code, lui, en est un : de nouveaux codes peuvent être ajoutés dans une version mineure, mais un code existant ne change pas de sens. Prévoyez donc un traitement pour les codes que vous connaissez et traitez un code inconnu comme « à revoir » plutôt que comme une erreur.
Toute règle automatique susceptible de renvoyer cantTell le fournit, et un test y veille pour qu’une nouvelle règle ne puisse pas arriver sans lui. Le code out-of-scope est ajouté par le moteur et non par une règle, sur les mêmes occurrences que celles qui produisent un wcagVersionScope au niveau du résultat. Les règles manuelles ne le portent pas : judgement-required est précisément ce que signifie déjà type: "manual", donc le répéter pour chaque occurrence n’apporterait rien que le résultat ne dise déjà.
Tendances
Un cantTell peut tout de même pencher d’un côté. lean indique lequel, lorsque la page fournit des indices en ce sens : toward est la réponse que ces indices rendent probable, why dit en quoi consistent ces indices, et signals liste les faits que la règle a lus, chacun sous la forme d’un nom en kebab-case et d’une valeur simple, pour qu’un outil puisse les afficher ou les pondérer. caution dit ce qui pourrait rendre la tendance fausse, lorsque uncertainty.needed ne le dit pas clairement. Le résultat reste cantTell : une tendance est un conseil, pas un verdict, et rien de ce qui lit les résultats ou les niveaux ne change à cause d’elle. LEANS.md est le guide pour les personnes qui lisent des rapports et celles qui construisent des outils : quels constats penchent et pourquoi, comment une tendance est mesurée, et quelle confiance lui accorder.
lean n’est présent que là où la règle dispose d’indices. Son absence est aussi une information : seule une personne peut juger ce constat. Il n’est présent que sur une occurrence de niveau cantTell, comme uncertainty ; le moteur le retire d’une occurrence tranchée, et retire une tendance mal formée (un toward autre que pass ou fail, ou sans why) sans toucher au constat. measured apparaît une fois qu’une tendance a été comptée sur au moins cinq cas dont la réponse est connue : combien de fois elle a eu raison (agreed) sur combien de cas (cases), de quel corpus ils proviennent (corpus, avec la date de lecture) et quelle version de core les a comptés (coreVersion). Les comptes se font par règle, code de raison et sens, à partir des cas de test des règles ACT du W3C, et le moteur les embarque, si bien qu’un résultat obtenu par n’importe quel point d’entrée les a. Une tendance jamais comptée, ou comptée sur moins de cas, n’en a pas ; il n’y a pas de score de confiance. Aucune n’en a encore : le premier comptage, dans jsdom, a vu 9 tendances, aucune sur cinq cas.
why et caution sont dans la langue de l’analyse ; i18n contient leurs clés et paramètres, comme le fait le i18n d’une occurrence pour summary et hint. Le moteur fait lui-même pencher un constat : une règle associée uniquement à un critère que la version WCAG cible a retiré, ou que les errata de 2023 considèrent comme satisfait, penche vers pass à côté de son incertitude out-of-scope.
reviewGroupOf(check, occurrence), de @surea11y/core, indique où une occurrence se place dans une revue : 'fail', 'leanFail', 'leanPass' ou 'open' (seule une personne peut juger), les groupes du rapport HTML. leaningFailures(result, { wcagOnly }) liste les constats qui penchent vers l’échec, ceux que compte un contrôle avec leanFailAs (JUnit, SARIF).
De nouvelles tendances arrivent dans les versions mineures, règle par règle, et une tendance peut être retirée si la mesure montre qu’elle se trompe trop souvent : traitez une tendance comme un conseil présent ou absent, jamais comme une partie de l’identité d’un constat. Elle n’entre pas dans l’empreinte.
Comment vérifier
Un cantTell confie une question à une personne. howToCheck lui dit comment y répondre : steps, quelques actions dans l’ordre, en partant de l’élément que nomme le constat (« Tab to the element. », « Watch whether the focus indicator stays on it. »), puis passWhen et failWhen, ce qu’elle constate si la vérification réussit et si elle échoue, en nommant le critère en cause. Chaque règle de core susceptible de renvoyer cantTell les rédige pour chacun de ses codes de raison (son meta.review), et chaque occurrence de niveau cantTell reçoit l’entrée de son code, dans la langue de l’analyse. Le constat d’une variante (variantOf) reçoit celle de sa règle de base lorsqu’il n’en a pas de propre. Une règle de pack peut aussi les rédiger, en anglais (voir Packs) ; ses constats les reçoivent lorsque les packs sont connus, comme runDomRulesInPage avec engineOptions.packs et addHowToCheck(result, { packs }) les connaissent. Il est absent d’une occurrence tranchée, du constat d’une règle personnalisée, de celui d’une règle de remplacement (une règle personnalisée ou de pack dans overriddenBuiltinIds), et de celui d’une règle de pack qui n’en rédige pas.
Les étapes sont ajoutées après l’analyse, dans Node, et non par le moteur dans la page : le moteur est injecté dans chaque page et chaque frame qu’ouvre une analyse, et n’a que faire d’un millier de phrases qu’aucune règle ne lit. runDomRulesInPage les ajoute à son résultat. Un résultat de runa11yCoreInPage exécuté dans une page (celui d’un binding, d’une extension) revient sans elles ; addHowToCheck(result) en renvoie une copie qui les contient, et howToCheckFor(ruleId, reasonCode, locale) donne celles d’un constat. Les deux lisent les dictionnaires de revue de core sur le disque, et ont donc besoin de Node ; un binding dont le code s’exécute dans un navigateur (Cypress) appelle applyHowToCheck(result, { en, fr }) de @surea11y/core/how-to-check avec les dictionnaires qu’il importe de @surea11y/core/review/<locale>, et obtient les mêmes étapes (BINDING_AUTHORS_GUIDE.md montre comment un binding s’y prend). Le rapport HTML (renderHtmlReport) les ajoute dans Node à un résultat qui arrive sans elles ; dans le navigateur (surea11y.report.js), il affiche celles que porte le résultat. Un outil qui affiche un résultat dans une autre langue que celle de l’analyse passe { locale } à addHowToCheck ou applyHowToCheck (addHowToCheck(result, { locale: 'de' }), ou une liste de langues, la plus souhaitée en premier ; renderHtmlReport n’en prend aucune) : les étapes sont données dans cette langue, y compris celles déjà présentes, avec l’anglais pour toute étape qui manque dans la langue.
Les étapes supposent un navigateur, ses outils de développement (l’arbre d’accessibilité), un clavier et, pour certaines, un lecteur d’écran. Contrairement à uncertainty.needed, qui dit en une phrase ce qui trancherait la question, elles disent comment la trancher. Comme une tendance, elles sont une aide, pas une partie de l’identité d’un constat, et leur formulation peut s’améliorer dans n’importe quelle version.
Un résultat composite (rulesResults[i])
Les composites agrègent plusieurs règles atomiques en un seul critère de succès WCAG (par ex. wcag-1.1.1-non-text-content agrège 21 règles atomiques). Sous le profil d’une norme dotée de ses propres agrégations, il y a aussi celles-ci, avec la formulation de la norme comme title et son nom comme meta.standard. Leur forme reprend l’enveloppe d’un résultat de vérification, avec un data.details propre aux composites :
{ruleId: string, // e.g. "wcag-1.1.1-non-text-content"outcome: "pass" | "fail" | "cantTell" | "notApplicable",severity, confidence, type, title, description, meta, engineOptions, schemaVersion, // as on a check result, except that// meta.normativeMappings names the criterion in a shorter form:// { standard, requirement, level }occurrences: [], // always empty — composites are rollups, not element-level findingsdata: {details: {reasonCode: string, // e.g. "composite.rollup.fail.anyFail"standard?: string, // a standard's own rollup only, with version and criterionversion?: string,criterion?: string,checksIds: string[], // every atomic ruleId it rolls up, custom rules mapped to its criteria includedcustomChecksIds?: string[], // the custom rules among them; absent when nonerestatesWcag?: string[], // a standard's rollup that restates WCAG criteria (applicability: 'wcag'): those criteriapartial?: true, // a standard's requirement its rules don't check in full (partial: true)contributors: Array<{ testId: string, outcome: string, severity?: string | null, custom?: true, notSelected?: true,byReasonCode?: true, checkOutcome?: string, requirements?: string[] }>,metrics: { failCount, cantTellCount, notApplicableCount, passCount, missingCount }}}}
Ordre de priorité de l’agrégation (déterministe, dans cet ordre) : au moins une règle contributrice en fail → composite fail ; sinon au moins un cantTell (ou une règle contributrice qui ne s’est pas exécutée du tout, missingCount > 0) → composite cantTell ; sinon toutes les contributrices en notApplicable → composite notApplicable, lorsque les règles trouvent chaque cas auquel le critère s’applique, et cantTell (partialCoverage) sinon ; sinon pass, lorsque les règles vérifient tout ce que demande le critère, et cantTell (partialCheck) sinon. Aucun critère WCAG n’est encore répertorié comme entièrement vérifié, si bien qu’aucune agrégation WCAG ne donne pass aujourd’hui. Voir Conformité pour ce que cela implique pour une déclaration de conformité globale.
L’agrégation propre à une norme peut juger une règle d’après ses constats plutôt que dans son ensemble (voir Packs) : sa contributrice indique alors byReasonCode: true, outcome est le résultat de la règle dans cette agrégation (un fail dont tous les constats relèvent d’ailleurs y est un pass) et checkOutcome le résultat propre de la règle. Lorsque les constats qui comptent pour l’agrégation nomment l’exigence à laquelle ils se rattachent, requirements les liste, et le meta.normativeMappings de l’agrégation nomme celles-là pour cette règle, et non toutes les exigences que la règle vérifie.
Les règles d’une agrégation WCAG sont sa propre liste plus chaque règle de engineOptions.customRules qui s’est exécutée et qui est associée à l’un de ses critères (par meta.wcagSc ou une entrée WCAG dans meta.normativeMappings), marquée custom: true parmi les contributrices et listée dans customChecksIds, pour qu’un lecteur voie quand une règle personnalisée a déterminé le résultat. Les agrégations propres à une norme gardent leurs listes.
Un critère partiellement vérifié. Une analyse qui nomme des règles plutôt que des critères (includeRuleIds: ['link-name-present']) reçoit aussi les agrégations des critères que ces règles vérifient, quand la sélection ne les retient pas elle-même (voir Les critères que rapporte une analyse). La priorité ci-dessus les tranche comme les autres : une règle en échec fait échouer le critère, et les règles que l’analyse n’a pas sélectionnées l’empêchent de donner pass. Chacune d’elles est une contributrice avec outcome: "missing" et notSelected: true, comptée dans missingCount comme avant. Quand ce sont elles qui rendent l’agrégation cantTell, son reasonCode est composite.rollup.cantTell.notSelectedChild, et le rapport HTML indique « Vérifié en partie : 1 contrôle sur 2 n’a pas été sélectionné pour cette analyse ». composite.rollup.cantTell.missingChild désigne toujours une règle qui ne s’est pas exécutée pour une autre raison, comme une règle que le profil exclut (engine.profileExcludes), qui n’est jamais marquée notSelected.
| reasonCode | Résultat | Quand |
|---|---|---|
composite.rollup.fail.anyFail | fail | Une contributrice a échoué. |
composite.rollup.cantTell.anyCantTell | cantTell | Aucune n’a échoué, et l’une est cantTell. |
composite.rollup.cantTell.notSelectedChild | cantTell | Aucune n’a échoué ni n’est cantTell, et certaines ne se sont pas exécutées parce que l’analyse ne les a pas sélectionnées : partiellement vérifié. |
composite.rollup.cantTell.missingChild | cantTell | Aucune n’a échoué ni n’est cantTell, et certaines ne se sont pas exécutées pour une autre raison. |
composite.rollup.notApplicable.allInapplicable | notApplicable | Toutes les contributrices sont notApplicable, et elles trouvent chaque cas auquel le critère s’applique. |
composite.rollup.cantTell.partialCoverage | cantTell | Toutes les contributrices sont notApplicable, mais le critère (ou l’exigence d’une norme marquée partial) peut s’appliquer à des contenus qu’elles ne recherchent pas : il n’a donc pas été montré que la page n’en a aucun. |
composite.rollup.cantTell.emptyComposite | cantTell | L’agrégation n’a aucune règle. |
composite.rollup.cantTell.partialCheck | cantTell | Aucune n’a échoué ni n’est cantTell, et toutes ne sont pas notApplicable, mais les règles ne vérifient qu’une partie de ce que demande le critère (ou l’exigence d’une norme marquée partial) : une personne vérifie le reste. |
composite.rollup.pass.otherwise | pass | Dans tous les autres cas. |
Valeurs de résultat
| Résultat | Signification | Possible avec type: "manual" ? |
|---|---|---|
fail | La règle signale que l’exigence qu’elle vérifie n’est pas satisfaite. Lisez sa correspondance, sa confiance et ses occurrences pour interpréter le constat. | Non, sous la politique par défaut (converti en cantTell) |
pass | La ou les cibles applicables de la règle existent et aucune n’a été signalée. | Oui |
cantTell | Nécessite un jugement humain : soit c’est réellement ambigu, soit c’est le constat indicatif d’une règle manuelle. | Oui |
notApplicable | La règle n’a trouvé aucun élément auquel elle s’applique sur cette page ou dans ce périmètre. | Oui |
Dans les règles intégrées à core, fail est volontairement le résultat le plus étroit et le plus exigeant : il est réservé aux violations normatives déterministes ; la recherche de couverture ne doit jamais le diluer.
Valeurs de sévérité et de confiance
severity:minor<moderate<serious<critical; l’évaluation par l’auteur de la règle de l’impact sur les utilisateurs, indépendante deconfidence. Un profil peut donner une autre gravité à une règle (leseverityd’un profil dans une norme ou dans la liste de contrôle d’un pack) : sous ce profil, le résultat affiche celle du profil, garde celle de la règle dansruleSeverity(présent seulement dans ce cas), et les agrégations prennent celle du profil.confidence:low<medium<high; le degré de certitude du moteur quant à l’exactitude d’un verdictfail/cantTell. Ce sont deux métadonnées informatives servant à prioriser ; aucune ne change le sens deoutcome.
Un fail n’est pas toujours confidence: "high", et ce n’est pas une contradiction. Le résultat décrit la procédure de décision (elle a tranché la question sans deviner), tandis que confidence décrit le modèle par rapport auquel cette décision a été prise. Quelques règles automatiques décident de façon déterministe par rapport à quelque chose qui est lui-même une approximation (les tables de rôles WAI-ARIA maintenues à la main, les correspondances de rôles natifs, un arbre d’accessibilité déduit du balisage statique) et renvoient medium : aria-required-children, aria-prohibited-children, aria-required-parent, aria-allowed-attr, form-control-programmatic-label-present, identical-iframes-same-purpose, svg-image-text-alternative-present, video-poster-text-alternative-present, target-size-minimum et text-spacing-content-loss. confidence figure sur chaque résultat, donc un consommateur qui ne veut que les échecs les plus certains peut filtrer directement dessus ; policy.allowedConfidence ne le fera pas pour vous, puisqu’une valeur non autorisée est remplacée par le defaultConfidence propre à la règle au lieu de changer le résultat (voir POLICY.md).
Exemple commenté
Analyse de <img src="logo.png"> (sans alt) et de <button></button> (sans nom accessible), limitée à ces deux règles via runOnly: { includeRuleIds: [...] } (voir Options du moteur ; depuis la 1.10.0, un simple tableau d’ids de règles fait la même chose) :
const result = runDomRulesInPage('https://example.test/',null,{},{ includeRuleIds: ['img-alt-present', 'button-name-present'] });
{"engine": {"tag": "a11ycore","version": "1.14.0","schemaVersion": "1.0.0","locale": { "requested": "en", "resolved": "en", "reason": "ok" },"wcagVersion": "2.2","environment": { "layout": false }},"url": "https://example.test/","title": "Example","timestamp": null,"perfStats": null,"contextSelector": null,"contextMatch": null,"checksResults": [{"ruleId": "button-name-present","outcome": "fail","severity": "serious","confidence": "high","type": "automatic","occurrences": [{"selector": "html > body > button","html": "<button></button>","structuralPath": [1, 1],"summary": "This button has no accessible name.","hint": "Give the button visible text, or name it with aria-label, so screen reader users know what it does.","data": {"visibilityFilter": { "eligible": true, "reasons": [], "targetSet": "acc", "accEligible": true },"details": { "reasonCode": "name_missing" }}}]},{"ruleId": "img-alt-present","outcome": "fail","severity": "serious","confidence": "high","type": "automatic","occurrences": [{"selector": "html > body > img","html": "<img src=\"logo.png\">","structuralPath": [1, 0],"summary": "This <img> has no alt attribute.","hint": "Add an alt attribute that says what the image shows, or alt=\"\" if the image is purely decorative.","data": {"details": { "reasonCode": "TEXT_ALTERNATIVE_MISSING" },"visibilityFilter": { "eligible": true, "reasons": [], "targetSet": "acc", "accEligible": true }}}]}],"rulesResults": [{"ruleId": "wcag-1.1.1-non-text-content","outcome": "fail","severity": "serious","title": "Non-text content: text alternatives","occurrences": [],"data": {"details": {"reasonCode": "composite.rollup.fail.anyFail","checksIds": ["area-alt-present", "img-alt-decorative", "img-alt-quality", "..."],"contributors": [{ "testId": "area-alt-present", "outcome": "missing", "notSelected": true },"...",{ "testId": "img-alt-present", "outcome": "fail", "severity": "serious" },"..."],"metrics": { "failCount": 1, "cantTellCount": 0, "notApplicableCount": 0, "passCount": 0, "missingCount": 20 }}}},{"ruleId": "wcag-4.1.2-name","outcome": "fail","severity": "serious","title": "Name, role, value: accessible name","occurrences": [],"data": {"details": {"reasonCode": "composite.rollup.fail.anyFail","metrics": { "failCount": 1, "cantTellCount": 0, "notApplicableCount": 0, "passCount": 0, "missingCount": 23 }}}}],"overriddenBuiltinIds": [],"skippedCustomRules": []}
(Version abrégée pour la lisibilité : le résultat réel inclut aussi title/description/i18n/meta/engineOptions/schemaVersion sur chaque entrée, conformément à la forme complète ci-dessus, et chaque contributrice de chaque agrégation. rulesResults contient les critères que vérifient les deux règles nommées : 1.1.1 pour img-alt-present et 4.1.2 pour button-name-present, chacun fail parce que sa règle a échoué. Les autres règles de ces critères n’ont pas été sélectionnées, elles sont donc missing avec notSelected: true ; sans règle en échec, les critères seraient cantTell (notSelectedChild), partiellement vérifiés. Le rollupIds de chaque résultat de vérification nomme son agrégation.)