pour-engine 1.21.0 → 1.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,6 @@
6
6
  export default {
7
7
  engine: {
8
8
  name: 'pour engine',
9
- version: '1.21.0',
9
+ version: '1.22.0',
10
10
  },
11
11
  };
package/engine/AUDIT.html CHANGED
@@ -684,7 +684,7 @@
684
684
  <p>What to trust and how much: the ten headline defects were re-verified line by line in the source by a second pass and are safe to act on. Per-rule ratings are judgments, not measurements; the boundary between "minor gaps" and "under-tests" is taste. Over-violation claims describe constructible scenarios, not measured frequency on real pages: an over-flag that fires on rare markup matters less in practice than the rating implies, and the lab benchmark, not this audit, is the source of truth for how often each fires. Two flagged behaviours (subtitles-as-captions, the auth-field both-attributes fail) are pinned by spec cases, meaning they are standing policy: this report flags them as spec deviations, and changing them is a product decision, not a bug fix.</p>
685
685
  <p>Timeline: the audit ran against a clean tree with 140 pinned cases, changing nothing. The same day, the proposed batch was implemented (eight rules, four catalog flips), adversarially audited, corrected, and re-pinned: 176 cases now pass, along with all testbeds, frame checks, and the four-surface smoke; the mass lab gate was not run. Coverage was regenerated and site plus extension bundles rebuilt with the new engine. The pre-existing confirmed defects in the findings above are deliberately untouched: fixing them is the proposed next batch, and a separate decision from adding rules.</p>
686
686
 
687
- <p class="foot">Pour engine spec sweep, 10 August 2026. Catalog verified against the W3C Recommendation of 12 December 2024 and its errata. This report lives at src/engine/AUDIT.html and is regenerated by running the spec-sweep skill; it is the only home of the audit, never published externally.</p>
687
+ <p class="foot">Pour engine spec sweep, 10 August 2026. Catalog verified against the W3C Recommendation of 12 December 2024 and its errata. This report lives at src/engine/AUDIT.html and is regenerated by running the spec-sweep skill; it ships alongside the engine source (the engine mirror repo includes it), so it is written to be read.</p>
688
688
  </main>
689
689
  </body>
690
690
  </html>
@@ -1,4 +1,6 @@
1
1
  // WCAG SC 1.1.1 Non-text Content (Level A)
2
+ import { labelledByName } from '../../lib/accessible-name.js';
3
+
2
4
  export default {
3
5
  id: 'svg-img-alt',
4
6
  impact: 'serious',
@@ -10,6 +12,21 @@ export default {
10
12
  // because browsers expose it to AT as a graphic even without a role.
11
13
  selector: 'svg[role="img"], svg[role="graphics-document"], svg[role="graphics-symbol"], [role="img"]:not(img):not(svg), svg:not([role])',
12
14
  evaluate(element, { accessibleName }) {
15
+ // A non-svg role="img" host names from author only, and the role makes
16
+ // its subtree presentational (ARIA: children presentational) — so a
17
+ // descendant <svg><title> or text content never reaches AT as a name,
18
+ // even though a generic contents walk would find it.
19
+ if (element.tagName.toLowerCase() !== 'svg' && element.getAttribute('role') === 'img') {
20
+ const authorName = labelledByName(element)
21
+ || element.getAttribute('aria-label')?.trim()
22
+ || element.getAttribute('title')?.trim();
23
+ if (authorName) return { status: 'pass' };
24
+ return {
25
+ status: 'fail',
26
+ message: 'This element is marked as an image but has no accessible name — for role="img", content inside it (including an inner SVG’s <title>) does not count as a name.',
27
+ fix: 'Add aria-label="…" or aria-labelledby pointing at visible text.',
28
+ };
29
+ }
13
30
  if (accessibleName(element)) return { status: 'pass' };
14
31
  if (!element.getAttribute('role') && element.tagName.toLowerCase() === 'svg') {
15
32
  if (element.parentNode instanceof SVGElement) return { status: 'pass' }; // nested svg: part of the outer graphic
@@ -24,12 +24,15 @@
24
24
  // already fixed, and must not be flagged. That also means an aria-label
25
25
  // which is itself generic is caught, which is the more common mistake.
26
26
  //
27
- // English only, deliberately. A phrase list cannot be quietly extended to
28
- // other languages without a speaker of each checking it, and a wrong entry
29
- // would fail correct links. Non-English pages get no findings here rather
30
- // than bad ones.
27
+ // The list grows one language at a time, and only with a speaker of that
28
+ // language checking every entry — a wrong entry would fail correct links.
29
+ // English and French are covered; pages in other languages get no findings
30
+ // here rather than bad ones. Entries are stored in NORMALIZED form (see
31
+ // normalizeName below): apostrophes become spaces, so "plus d informations"
32
+ // is how "plus d'informations" — straight or typographic — must be listed.
31
33
 
32
34
  const GENERIC = new Set([
35
+ // English
33
36
  'read more', 'read more about this', 'more', 'much more', 'learn more',
34
37
  'click here', 'click', 'click this', 'tap here', 'press here',
35
38
  'here', 'this', 'this link', 'link', 'this page', 'go', 'go here',
@@ -37,6 +40,13 @@ const GENERIC = new Set([
37
40
  'see more', 'view more', 'show more', 'find out more', 'discover more',
38
41
  'continue', 'continue reading', 'keep reading', 'full story', 'full article',
39
42
  'details', 'see details', 'view details', 'more details',
43
+ // French — contributed and checked by a native speaker
44
+ 'en savoir plus', 'savoir plus', 'lire la suite', 'lire plus', 'plus',
45
+ 'cliquez ici', 'cliquer ici', 'clique ici', 'ici', 'ce lien', 'lien',
46
+ 'cette page', 'voir plus', 'en voir plus', 'afficher plus', 'afficher',
47
+ 'voir', 'voir le détail', 'voir les détails', 'plus de détails',
48
+ 'plus d informations', 'plus d infos',
49
+ 'en savoir davantage', 'découvrir', 'découvrez', 'continuer', 'suite',
40
50
  ]);
41
51
 
42
52
  /** Lowercased, punctuation and arrows dropped, spaces collapsed, so
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pour-engine",
3
- "version": "1.21.0",
3
+ "version": "1.22.0",
4
4
  "description": "Accessibility (WCAG 2.2) audit engine written from the W3C specifications — runs entirely in the browser, no dependencies.",
5
5
  "type": "module",
6
6
  "main": "engine/index.js",