Utilisation en ligne de commande

Une commande plutôt que du code : pas de framework de test ni de script Node nécessaire, idéal pour une étape de pipeline ou une vérification rapide d’un artefact de build.

Première analyse

$ npm install -g @surea11y/cli
$ surea11y scan ./index.html
$ surea11y scan https://example.com/ --wcag 2.2

Nécessite Node.js ^20.19.0 || ^22.13.0 || >=24.0.0. Le résumé nomme la version de @surea11y/core qui a exécuté l’analyse :

$ surea11y scan ./index.html
surea11y scan: file:///path/to/index.html
engine: @surea11y/core 1.14.1
pass: 16 fail: 4 cantTell: 4 notApplicable: 121
occurrences by tier: fail: 4 cantTell: 4
needs review: likely fail: 1 likely pass: 0 needs a person: 3
FAIL (4 rule(s)):
button-name-present (serious, 1 fail occurrence(s))
- html > body > main > button
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.
contrast-enhanced (serious, 1 fail occurrence(s))
- html > body > main > p
Element has insufficient color contrast (AAA) of 2.85:1 (foreground:
#999999, background: #ffffff, font size: 16px, font weight: normal).
Expected contrast ratio of 7:1 (normal text).
hint: Change the text color, the background color, or both, so the
contrast ratio reaches at least 7:1.
contrast-minimum (serious, 1 fail occurrence(s))
- html > body > main > p
Element has insufficient color contrast of 2.85:1 (foreground: #999999,
background: #ffffff, font size: 16px, font weight: normal). Expected
contrast ratio of 4.5:1 (normal text).
hint: Change the text color, the background color, or both, so the
contrast ratio reaches at least 4.5:1.
img-alt-present (serious, 1 fail occurrence(s))
- html > body > main > img
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.
LIKELY FAIL — confirm (1 occurrence(s)):
- heading-order (minor): html > body > main > h3
This heading jumps from level 1 to level 3, skipping a level.
why: The heading skips a level after the one before it.
cantTell — needs human review (4 rule(s)): heading-order, link-name-quality,
manual-review, page-title-patterns
criteria (WCAG 2.2):
fail 1.1.1 Non-text content: text alternatives; 1.4.3 Contrast: minimum;
1.4.6 Contrast: enhanced; 4.1.2 Name, role, value: accessible name
cantTell 1.2.1 Audio-only and video-only (prerecorded): transcript; 1.2.2
Captions (Prerecorded); 1.3.1 Info and Relationships; 1.3.4
Orientation; 1.3.5 Identify Input Purpose; 1.4.1 Use of Color;
1.4.2 Audio Control; 1.4.4 Resize Text; 1.4.5 Images of Text;
1.4.12 Text Spacing; 2.1.1 Keyboard; 2.1.3 Keyboard (No Exception);
2.2.1 Timing Adjustable; 2.2.2 Pause, Stop, Hide; 2.2.4
Interruptions; 2.4.1 Bypass Blocks; 2.4.2 Page titled; 2.4.3 Focus
order; 2.4.4 Link Purpose (In Context); 2.4.6 Headings and Labels;
2.4.7 Focus visible; 2.4.9 Link Purpose (Link Only); 2.5.8 Target
size: minimum; 3.1.1 Language of page; 3.1.2 Language of Parts;
3.2.5 Change on Request; 3.3.1 Error Identification; 3.3.2 Labels
or Instructions; 3.3.8 Accessible Authentication (Minimum); 4.1.2
Name, role, value: ARIA validity; 4.1.3 Status Messages
notApplicable: 2
A criterion that passes here means the checks that ran found nothing in it,
not that the page conforms.

La ligne needs review classe les questions selon ce qu’en fait la personne qui vérifie : likely fail (les éléments de la page indiquent un échec, comme le saut de niveau de titre de cet exemple), likely pass et needs a person. Les échecs probables sont listés avec la raison pour laquelle ils penchent de ce côté, et chaque question, dans --json et dans les rapports, porte les étapes pour y répondre (howToCheck).

Le bloc criteria lit la même analyse par critère WCAG, comme on pose généralement la question (« le 1.4.3 échoue-t-il ? ») : fail d’abord, puis cantTell, et un décompte de notApplicable. Depuis @surea11y/core 1.14, aucun critère n’apparaît en pass : un critère ne réussit que si ses règles vérifient tout ce qu’il demande, et aucun n’est encore recensé comme entièrement vérifié, si bien que, lorsque toutes ses règles réussissent, il reste en cantTell, pour qu’une personne vérifie le reste. Le bloc ne change jamais le code de sortie, et --compact l’affiche sous forme de décomptes. Sans aucune règle en échec, le résumé se termine en précisant que cela ne signifie pas que la page est conforme, et nomme ce qu’une personne vérifie encore.

Plusieurs pages, ou un sitemap

Nommez plusieurs pages, listez-les dans un fichier (un chemin ou une URL par ligne, avec des commentaires #), ou pointez l’analyse vers un sitemap :

$ surea11y scan ./dist/index.html ./dist/pricing.html
$ surea11y scan --urls pages.txt
$ surea11y scan --sitemap https://example.com/sitemap.xml # up to 50 pages; --max-pages to change it

Chaque page a sa propre section dans le résumé, une dernière ligne compte les pages en échec, et l’exécution échoue dès qu’une page échoue. --json affiche un tableau, --sarif et --junit écrivent un seul fichier couvrant toutes les pages, --html écrit un rapport par page (report.html, report-2.html…), et une baseline retient la page de chaque échec connu. Un sitemap peut être une URL ou un fichier local, compressé en gzip ou non, et un index de sitemaps est suivi sur un niveau. Une page qui ne peut pas être analysée arrête l’exécution avec le code 2, en la nommant, avant que rien ne soit écrit.

Dans un vrai navigateur

Par défaut, la CLI lit du HTML statique. Pour les pages dont le contenu est rendu par leur JavaScript, installez Playwright dans votre projet et ajoutez --browser : chaque page s’ouvre dans Chromium headless, ses scripts s’exécutent, et l’analyse voit ce qui est rendu, avec une vraie mise en page pour les règles qui la mesurent. Le moteur est le même @surea11y/core, et toutes les options fonctionnent, y compris les rapports, les baselines, les packs et les règles personnalisées.

$ npm install --save-dev playwright
$ npx playwright install chromium
$ surea11y scan https://app.example.com/ https://app.example.com/settings --browser

Une application qui récupère ses données après le chargement peut faire attendre l’analyse, et une page peut être analysée à la largeur d’un téléphone, en mode sombre, ou dans toutes les combinaisons de plusieurs valeurs :

$ surea11y scan https://app.example.com/orders --browser --wait-for "#orders-table"
$ surea11y scan https://example.com/ --browser --width 320 --color-scheme dark
$ surea11y scan https://example.com/ --browser --width 1280,320 --color-scheme light,dark

--wait-for attend jusqu’à 30 secondes (--wait-timeout change ce délai) et arrête l’exécution avec le code 2 si l’élément n’apparaît jamais. Plusieurs largeurs ou schémas de couleurs chargent la page une fois et fusionnent les analyses en un seul résultat par page : le pire résultat de chaque règle, et chaque constat une seule fois, avec les combinaisons où il a été vu (seen in:).

Avant le chargement de chaque page, la CLI installe les hooks d’internals et d’écouteurs de core, si bien que l’analyse juge les noms que les composants web se donnent via ElementInternals et les gestionnaires de clic ajoutés depuis un script : un role="button" qu’Entrée et Espace n’atteignent jamais échoue. --live-regions ajoute le hook de régions live, si bien qu’un message d’état ajouté avec son texte déjà dedans, que les lecteurs d’écran n’annoncent souvent pas, fait l’objet d’une question ; --no-hooks laisse de côté les deux premiers. Le résumé nomme le viewport, le schéma de couleurs et les hooks de chaque analyse. La CLI ne clique pas, ne saisit rien et ne se connecte pas : pour un contenu qui n’apparaît qu’après cela, pilotez le navigateur avec l’un des bindings. Les frames de la page ne sont pas encore analysés.

Choisir ce qui s’exécute

--wcag sélectionne les règles d’une cible de conformité WCAG, selon le niveau de chaque critère dans cette version ; --wcag-level fixe son niveau (AA sauf indication contraire). --best-practices ajoute les règles qui ne testent aucun critère WCAG, comme region et heading-order ; seule, elle n’exécute que celles-ci. --profile exécute un profil de conformité, comme en301549-v4.1.1 ou celui d’un pack.

$ surea11y scan ./dist/index.html --wcag 2.2 # WCAG 2.2, Level A and AA
$ surea11y scan ./dist/index.html --wcag 2.1 --wcag-level A # WCAG 2.1, Level A
$ surea11y scan ./dist/index.html --wcag 2.2 --best-practices # and the best practices

4.1.1 Analyse syntaxique, que WCAG 2.2 a retiré, fait partie d’une cible 2.1 et non d’une cible 2.2. --rules et --tags s’ajoutent aux règles d’une cible, et --exclude-rules s’applique ensuite. À côté de --profile, --best-practices ajoute ses règles et le profil s’applique toujours ; --wcag, --rules et --tags choisissent les règles à la place d’un profil : --profile combiné à l’un d’eux se termine donc avec le code 2. Ces flags correspondent à runOnly.wcag, runOnly.bestPractices et engineOptions.profile (voir Options du moteur).

Nommer vous-même les vérifications

Quand les deux sont donnés, --rules et --tags exécutent les règles qui correspondent aux deux. Pour dire « ce domaine, plus ces vérifications », passez --include-mode or, qui exécute les règles qui correspondent à l’un ou à l’autre. Sans lui, l’analyse ci-dessous n’exécute aucune règle, puisque contrast-computable ne porte pas l’étiquette forms, et se termine avec 2 plutôt que de réussir sans rien vérifier.

$ surea11y scan ./dist/form.html --tags forms --rules contrast-computable --include-mode or

Une analyse qui nomme des vérifications rapporte les critères WCAG auxquels elles appartiennent, à tous les niveaux. contrast-computable vérifie le 1.4.3 (AA) et le 1.4.6 (AAA), si bien qu’un site conforme au niveau AA peut afficher en échec un critère AAA qu’il n’a jamais visé. Sans --wcag, --wcag-level fixe le niveau des critères rapportés, sans changer les règles exécutées :

$ surea11y scan ./index.html --rules contrast-computable --wcag-level AA
surea11y scan: file:///path/to/index.html
engine: @surea11y/core 1.14.1
WCAG level reported: AA
pass: 1 fail: 0 cantTell: 0 notApplicable: 0
occurrences by tier: fail: 0 cantTell: 0
criteria (WCAG 2.2 AA):
cantTell 1.4.3 Contrast: minimum (partly checked: 1 of 2 checks not selected)
A criterion that passes here means the checks that ran found nothing in it,
not that the page conforms.
No rule failed, which is not a claim that the page conforms: the automated rules
found nothing definite. A person still checks the cantTell findings, keyboard
use, how a screen reader reads the page, and the content itself.

Le 1.4.3 est vérifié en partie : son autre vérification, contrast-minimum, n’a pas été sélectionnée, donc son résultat ne couvre que la vérification exécutée, et comme aucune règle n’a échoué, le résumé se termine en rappelant que cela ne signifie pas que la page est conforme. Les identifiants de règle et les niveaux sont lus sans tenir compte de la casse (--rules IMG-ALT-PRESENT, --wcag-level aa). --include-mode correspond à engineOptions.includeMode, et --wcag-level sans --wcag à engineOptions.wcagLevel.

Packs

--pack charge un pack (des règles, une norme ou une liste de contrôle, avec leurs profils) par nom de paquet, résolu depuis le projet du répertoire de travail, ou par chemin ; --profile exécute l’un de ses profils. Répétez --pack pour analyser avec plusieurs packs.

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

Le résumé nomme le profil et les packs qui se sont exécutés, et --json les contient sous engine.profile et engine.packs. Un pack introuvable ou impossible à charger, ou que le moteur ignore, termine la commande avec le code 2 sans écrire de rapport, si bien qu’une analyse ne réussit jamais sans que les vérifications du pack aient été exécutées.

N’échouer que sur les problèmes graves

--fail-on-severity (critical, serious, moderate ou minor) ne fait échouer l’exécution que sur les règles en échec à cette gravité ou au-dessus ; avec --baseline, que sur les nouvelles. Chaque échec reste signalé, et le résumé marque ceux qui sont sous le niveau, pour qu’une équipe commence par bloquer les problèmes graves et resserre le seuil à mesure que l’arriéré diminue.

$ surea11y scan --urls pages.txt --fail-on-severity serious

Questions en échec probable

--fail-on-lean fait aussi échouer l’exécution sur les questions en échec probable (leaningFailures() de core), à la gravité de --fail-on-severity ou au-dessus si vous en donnez une ; --lean-fail-wcag-only ne compte que celles des règles associées à un critère WCAG, sans les bonnes pratiques. Avec cette option, --junit signale une telle règle comme un test en échec et --sarif un tel constat comme une error, et --write-baseline enregistre aussi les échecs probables actuels, pour que --baseline ne bloque que les nouveaux. C’est facultatif, car une tendance est un indice, pas une preuve : qu’un constat penche, et de quel côté, peut changer dans une version mineure de core.

$ surea11y scan ./dist/index.html --fail-on-lean --lean-fail-wcag-only
$ surea11y scan ./dist/index.html --fail-on-lean --write-baseline baseline.json # once, commit it
$ surea11y scan ./dist/index.html --fail-on-lean --baseline baseline.json # in CI, from then on

Résultats compacts

--compact réduit chaque résultat pass et notApplicable à sa règle, son résultat et son type (engineOptions.output.detail: 'findings') : un --json plus léger, pour un résultat conservé comme artefact de CI. Le résumé, le code de sortie, la baseline et les rapports HTML, SARIF et JUnit sont les mêmes que sans cette option.

$ surea11y scan ./dist/index.html --compact --json > result.json

Codes de sortie et limites

cantTell n’affecte pas le code de sortie, sauf avec --fail-on-lean : 0 si aucun fail, 1 s’il y a au moins un fail sur une page (ou, avec --baseline, au moins un fail nouveau : c’est le cas du contrôle en CI ; --fail-on-severity ne compte que ceux de son niveau ou au-dessus ; voir Baseline / allowlist pour ce qui compte comme « connu » ou « nouveau »), 2 en cas d’erreur d’utilisation ou d’analyse, ou pour une analyse qui n’aurait rien vérifié ou moins que demandé : une liste --rules/--tags qui ne nomme aucune règle ni aucun tag connu, un sélecteur --context qui ne correspond à rien, une règle personnalisée ou un pack qui ne s’est pas exécuté, un --profile qui n’a pas été appliqué. Une baseline écrite avec core 1.13 correspond toujours aux constats dont la 1.14 a scindé les codes de raison ; --migrate-baseline baseline.json la réécrit avec les nouveaux codes.

Sans --browser, la CLI lit uniquement du HTML statique, qu’il s’agisse d’un fichier local ou du corps brut d’une requête HTTP(S) GET, et n’exécute jamais le JavaScript de la page. Aucun des deux modes ne clique, ne saisit ni ne se connecte : pour un contenu qui n’apparaît qu’après cela, utilisez l’un des bindings, ou appelez directement le moteur dans un navigateur que vous pilotez (Pattern 2 dans INTEGRATION.md, le guide d’intégration sur GitHub).

Référence complète des flags, contrat des règles personnalisées, packs et recettes de CI : docs/CLI.md. Pour les modèles de pipeline de CI en particulier, voir Intégration CI.