pour-engine 1.40.0 → 1.42.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.
Files changed (59) hide show
  1. package/config/project.config.js +1 -1
  2. package/engine/AUDIT.html +24 -690
  3. package/engine/COVERAGE.md +17 -15
  4. package/engine/README.md +10 -5
  5. package/engine/index.js +34 -8
  6. package/engine/lib/accessible-name.js +69 -63
  7. package/engine/lib/contrast.js +111 -10
  8. package/engine/lib/dom.js +78 -18
  9. package/engine/lib/roles.js +5 -2
  10. package/engine/rules/best-practice/fieldset-legend.js +11 -5
  11. package/engine/rules/best-practice/region.js +1 -1
  12. package/engine/rules/index.js +29 -0
  13. package/engine/rules/wcag/1.1.1-area-alt.js +6 -1
  14. package/engine/rules/wcag/1.1.1-image-alt.js +2 -1
  15. package/engine/rules/wcag/1.1.1-svg-img-alt.js +14 -17
  16. package/engine/rules/wcag/1.2.3-video-audio-description.js +42 -0
  17. package/engine/rules/wcag/1.3.1-aria-required-children.js +20 -8
  18. package/engine/rules/wcag/1.3.1-aria-required-parent.js +7 -10
  19. package/engine/rules/wcag/1.3.1-form-label.js +5 -29
  20. package/engine/rules/wcag/1.3.1-list-structure.js +4 -3
  21. package/engine/rules/wcag/1.3.1-table-headers.js +23 -2
  22. package/engine/rules/wcag/1.3.4-orientation.js +117 -48
  23. package/engine/rules/wcag/1.3.5-autocomplete-valid.js +21 -8
  24. package/engine/rules/wcag/1.3.6-region-purpose.js +35 -0
  25. package/engine/rules/wcag/1.4.1-link-in-text-block.js +19 -3
  26. package/engine/rules/wcag/1.4.10-reflow.js +3 -3
  27. package/engine/rules/wcag/1.4.11-non-text-contrast.js +9 -1
  28. package/engine/rules/wcag/1.4.12-text-spacing.js +15 -2
  29. package/engine/rules/wcag/1.4.3-color-contrast.js +79 -7
  30. package/engine/rules/wcag/1.4.3-control-contrast.js +35 -6
  31. package/engine/rules/wcag/1.4.4-meta-viewport.js +7 -2
  32. package/engine/rules/wcag/1.4.8-text-justified.js +49 -0
  33. package/engine/rules/wcag/2.1.1-scrollable-region-focusable.js +5 -5
  34. package/engine/rules/wcag/2.2.1-meta-refresh.js +10 -4
  35. package/engine/rules/wcag/2.2.2-pause-stop-hide.js +5 -5
  36. package/engine/rules/wcag/2.2.4-meta-refresh-no-exceptions.js +50 -0
  37. package/engine/rules/wcag/2.4.1-bypass-blocks.js +2 -2
  38. package/engine/rules/wcag/2.4.10-section-heading.js +86 -0
  39. package/engine/rules/wcag/2.4.11-focus-not-obscured.js +134 -78
  40. package/engine/rules/wcag/2.4.12-focus-not-obscured-enhanced.js +17 -0
  41. package/engine/rules/wcag/2.4.2-document-title.js +7 -8
  42. package/engine/rules/wcag/2.4.4-link-text-generic.js +6 -5
  43. package/engine/rules/wcag/2.4.6-heading-label-placeholder.js +69 -0
  44. package/engine/rules/wcag/2.4.9-link-text-generic-only.js +8 -23
  45. package/engine/rules/wcag/2.5.1-pointer-gesture-alternative.js +98 -0
  46. package/engine/rules/wcag/2.5.8-target-size.js +161 -45
  47. package/engine/rules/wcag/3.3.4-financial-form-confirmation.js +32 -0
  48. package/engine/rules/wcag/3.3.8-auth-field-obstruction.js +10 -13
  49. package/engine/rules/wcag/3.3.9-captcha-alternative.js +35 -0
  50. package/engine/rules/wcag/4.1.2-aria-attr-valid.js +4 -0
  51. package/engine/rules/wcag/4.1.2-aria-field-name.js +6 -5
  52. package/engine/rules/wcag/4.1.2-aria-valid-refs.js +8 -3
  53. package/engine/rules/wcag/4.1.2-button-name.js +1 -1
  54. package/engine/rules/wcag/4.1.2-composite-widget-name.js +1 -1
  55. package/engine/rules/wcag/4.1.2-invoker-target.js +2 -1
  56. package/engine/rules/wcag/4.1.2-label-for-valid.js +2 -2
  57. package/engine/rules/wcag/4.1.2-valid-role.js +6 -1
  58. package/engine/wcag22.js +19 -19
  59. package/package.json +1 -1
package/engine/wcag22.js CHANGED
@@ -4,8 +4,8 @@
4
4
  //
5
5
  // `automation` is an honest assessment:
6
6
  // 'auto' — rules can meaningfully test this
7
- // 'partial' — rules catch some failures; humans must still review
8
- // 'manual' — requires human judgment (most of the spec!)
7
+ // 'partial' — rules check aspects or nominate review candidates; humans still review
8
+ // 'manual' — no active rule; assessed through the human checklist
9
9
  // The coverage report (scripts/wcag-coverage.js → src/engine/COVERAGE.md) joins
10
10
  // this catalog with the rule registry to show real coverage.
11
11
 
@@ -27,9 +27,9 @@ export default [
27
27
  sc('1.1.1', 'Non-text Content', 'A', 'partial'),
28
28
  sc('1.2.1', 'Audio-only and Video-only (Prerecorded)', 'A', 'partial'), // partial: the audio-only transcript question is askable; silent-video detection is not
29
29
  sc('1.2.2', 'Captions (Prerecorded)', 'A', 'partial'),
30
- sc('1.2.3', 'Audio Description or Media Alternative (Prerecorded)', 'A', 'manual'),
30
+ sc('1.2.3', 'Audio Description or Media Alternative (Prerecorded)', 'A', 'partial'), // partial: video-audio-description asks per sound-capable video without a descriptions track; whether description or a text alternative exists, or is needed at all, is the reviewer's
31
31
  sc('1.2.4', 'Captions (Live)', 'AA', 'manual'),
32
- sc('1.2.5', 'Audio Description (Prerecorded)', 'AA', 'manual'),
32
+ sc('1.2.5', 'Audio Description (Prerecorded)', 'AA', 'partial'), // partial: same question as 1.2.3 (video-audio-description); H96 descriptions track is the one DOM-visible pass
33
33
  sc('1.2.6', 'Sign Language (Prerecorded)', 'AAA', 'manual'),
34
34
  sc('1.2.7', 'Extended Audio Description (Prerecorded)', 'AAA', 'manual'),
35
35
  sc('1.2.8', 'Media Alternative (Prerecorded)', 'AAA', 'manual'),
@@ -37,17 +37,17 @@ export default [
37
37
  sc('1.3.1', 'Info and Relationships', 'A', 'partial'),
38
38
  sc('1.3.2', 'Meaningful Sequence', 'A', 'partial'), // partial: CSS-reorder divergence is measurable; whether meaning survives is the reader's call
39
39
  sc('1.3.3', 'Sensory Characteristics', 'A', 'manual'),
40
- sc('1.3.4', 'Orientation', 'AA', 'partial', '2.1'), // orientation-lock (2026-08-01) proves CSS root hides/rotations; script locks stay a human check
41
- sc('1.3.5', 'Identify Input Purpose', 'AA', 'partial', '2.1'), // partial: wrong tokens are provable; MISSING autocomplete on identity fields needs judgment
42
- sc('1.3.6', 'Identify Purpose', 'AAA', 'manual', '2.1'),
40
+ sc('1.3.4', 'Orientation', 'AA', 'partial', '2.1'), // orientation-lock asserts CSS root hides/rotations as ACT b33eff does, essential exception assumed absent; script locks stay a human check
41
+ sc('1.3.5', 'Identify Input Purpose', 'AA', 'partial', '2.1'), // partial: wrong tokens are asserted as ACT 73f2c2 does (search boxes asked); MISSING autocomplete on identity fields needs judgment
42
+ sc('1.3.6', 'Identify Purpose', 'AAA', 'partial', '2.1'), // partial: region-purpose asks once per page with no landmark at all (ARIA11 is the regions technique); component and icon purpose stays human
43
43
  sc('1.4.1', 'Use of Color', 'A', 'partial'), // link-in-text-block automates the link case
44
- sc('1.4.2', 'Audio Control', 'A', 'partial'), // partial: declarative autoplay is provable; JS-initiated audio is not
44
+ sc('1.4.2', 'Audio Control', 'A', 'partial'), // declarative autoplay identifies candidates; audibility and alternative controls need review
45
45
  sc('1.4.3', 'Contrast (Minimum)', 'AA', 'partial'),
46
46
  sc('1.4.4', 'Resize Text', 'AA', 'partial'),
47
47
  sc('1.4.5', 'Images of Text', 'AA', 'manual'),
48
48
  sc('1.4.6', 'Contrast (Enhanced)', 'AAA', 'partial'),
49
49
  sc('1.4.7', 'Low or No Background Audio', 'AAA', 'manual'),
50
- sc('1.4.8', 'Visual Presentation', 'AAA', 'manual'),
50
+ sc('1.4.8', 'Visual Presentation', 'AAA', 'partial'), // partial: text-justified asks about justified running text (F88); the other four requirements are met by browser mechanisms and not judged
51
51
  sc('1.4.9', 'Images of Text (No Exception)', 'AAA', 'manual'),
52
52
  sc('1.4.10', 'Reflow', 'AA', 'partial', '2.1'), // partial: reflow (current-viewport overflow heuristic); full test is at 320px
53
53
  sc('1.4.11', 'Non-text Contrast', 'AA', 'partial', '2.1'), // partial: non-text-contrast covers field boundaries; icons/focus indicators need eyes
@@ -61,7 +61,7 @@ export default [
61
61
  sc('2.2.1', 'Timing Adjustable', 'A', 'partial'), // meta-refresh automates the redirect/refresh case
62
62
  sc('2.2.2', 'Pause, Stop, Hide', 'A', 'partial'),
63
63
  sc('2.2.3', 'No Timing', 'AAA', 'manual'),
64
- sc('2.2.4', 'Interruptions', 'AAA', 'manual'),
64
+ sc('2.2.4', 'Interruptions', 'AAA', 'partial'), // partial: meta-refresh-no-exceptions asserts any timed refresh or redirect (F40, F41); scripted interruptions are invisible
65
65
  sc('2.2.5', 'Re-authenticating', 'AAA', 'manual'),
66
66
  sc('2.2.6', 'Timeouts', 'AAA', 'manual', '2.1'),
67
67
  sc('2.3.1', 'Three Flashes or Below Threshold', 'A', 'manual'),
@@ -72,15 +72,15 @@ export default [
72
72
  sc('2.4.3', 'Focus Order', 'A', 'partial'), // visual-order-divergence flags CSS order/*-reverse against DOM order; whether an order preserves MEANING stays human
73
73
  sc('2.4.4', 'Link Purpose (In Context)', 'A', 'partial'),
74
74
  sc('2.4.5', 'Multiple Ways', 'AA', 'manual'),
75
- sc('2.4.6', 'Headings and Labels', 'AA', 'manual'),
75
+ sc('2.4.6', 'Headings and Labels', 'AA', 'partial'), // partial: heading-label-placeholder asserts template text left in a heading or label and asks about single generic words; whether real words describe the topic needs a reader
76
76
  sc('2.4.7', 'Focus Visible', 'AA', 'partial'), // partial: focus-visible flags outline suppression; the indicator itself needs eyes
77
77
  sc('2.4.8', 'Location', 'AAA', 'manual'),
78
- sc('2.4.9', 'Link Purpose (Link Only)', 'AAA', 'partial'), // partial: contentless link names are provable; whether a real name describes its destination needs a reader
79
- sc('2.4.10', 'Section Headings', 'AAA', 'manual'),
80
- sc('2.4.11', 'Focus Not Obscured (Minimum)', 'AA', 'partial', '2.2'), // partial: focus-not-obscured catches fully-covered targets at rest
81
- sc('2.4.12', 'Focus Not Obscured (Enhanced)', 'AAA', 'manual', '2.2'),
78
+ sc('2.4.9', 'Link Purpose (Link Only)', 'AAA', 'partial'), // generic wording nominates candidates; whether wording explains the destination needs a reader
79
+ sc('2.4.10', 'Section Headings', 'AAA', 'partial'), // partial: section-heading asks about a declared <section> of running text with no heading of its own; where sections begin in undeclared writing stays human
80
+ sc('2.4.11', 'Focus Not Obscured (Minimum)', 'AA', 'partial', '2.2'), // covered resting targets are candidates; actual focus/scroll behavior needs review
81
+ sc('2.4.12', 'Focus Not Obscured (Enhanced)', 'AAA', 'partial', '2.2'), // partial: focus-not-obscured-enhanced reviews any overlap by an opaque fixed panel, 2.4.11's geometry without the containment requirement
82
82
  sc('2.4.13', 'Focus Appearance', 'AAA', 'manual', '2.2'),
83
- sc('2.5.1', 'Pointer Gestures', 'A', 'manual', '2.1'),
83
+ sc('2.5.1', 'Pointer Gestures', 'A', 'partial', '2.1'), // partial: pointer-gesture-alternative asks about surfaces whose touch-action hands swipes or every touch to script (carousels, maps, canvases); pointer-event gesture logic with no declaration is invisible
84
84
  sc('2.5.2', 'Pointer Cancellation', 'A', 'manual', '2.1'),
85
85
  sc('2.5.3', 'Label in Name', 'A', 'manual', '2.1'), // manual WHILE label-in-name is parked (re-parked 2026-08-01, see rules/index.js) — 'auto' with no active rule would make this SC vanish from results AND the manual checklist
86
86
  sc('2.5.4', 'Motion Actuation', 'A', 'manual', '2.1'),
@@ -99,17 +99,17 @@ export default [
99
99
  sc('3.2.2', 'On Input', 'A', 'partial'), // on-input-navigation flags the inline jump-menu signature; "advised beforehand" stays a human call
100
100
  sc('3.2.3', 'Consistent Navigation', 'AA', 'manual'),
101
101
  sc('3.2.4', 'Consistent Identification', 'AA', 'manual'),
102
- sc('3.2.5', 'Change on Request', 'AAA', 'manual'),
102
+ sc('3.2.5', 'Change on Request', 'AAA', 'partial'), // partial: meta-refresh-no-exceptions (F40, F41); scripted context changes and pop-ups on load are invisible
103
103
  sc('3.2.6', 'Consistent Help', 'A', 'manual', '2.2'),
104
104
  sc('3.3.1', 'Error Identification', 'A', 'partial'), // error-message-linkage proves broken aria-errormessage targets; only in force when the page is captured mid-error
105
105
  sc('3.3.2', 'Labels or Instructions', 'A', 'partial'),
106
106
  sc('3.3.3', 'Error Suggestion', 'AA', 'manual'),
107
- sc('3.3.4', 'Error Prevention (Legal, Financial, Data)', 'AA', 'manual'),
107
+ sc('3.3.4', 'Error Prevention (Legal, Financial, Data)', 'AA', 'partial'), // partial: financial-form-confirmation asks once per form that takes card details; legal commitments and data deletion have no markup signature
108
108
  sc('3.3.5', 'Help', 'AAA', 'manual'),
109
109
  sc('3.3.6', 'Error Prevention (All)', 'AAA', 'manual'),
110
110
  sc('3.3.7', 'Redundant Entry', 'A', 'partial', '2.2'), // partial: duplicate autocomplete purposes in one form are visible; cross-page processes are not
111
111
  sc('3.3.8', 'Accessible Authentication (Minimum)', 'AA', 'partial', '2.2'), // partial: auth-field-obstruction catches paste blocking; alternatives need judgment
112
- sc('3.3.9', 'Accessible Authentication (Enhanced)', 'AAA', 'manual', '2.2'),
112
+ sc('3.3.9', 'Accessible Authentication (Enhanced)', 'AAA', 'partial', '2.2'), // partial: auth-field-obstruction's F109 findings apply unchanged (twin tag) and captcha-alternative asks about a CAPTCHA beside a credential, the object-recognition exception 3.3.9 removes
113
113
  // 4. Robust (4.1.1 Parsing was removed in WCAG 2.2)
114
114
  sc('4.1.2', 'Name, Role, Value', 'A', 'partial'),
115
115
  sc('4.1.3', 'Status Messages', 'AA', 'manual', '2.1'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pour-engine",
3
- "version": "1.40.0",
3
+ "version": "1.42.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",