pour-engine 1.1.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 (86) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +99 -0
  3. package/config/project.config.js +11 -0
  4. package/engine/COVERAGE.md +115 -0
  5. package/engine/README.md +115 -0
  6. package/engine/index.js +258 -0
  7. package/engine/lib/accessible-name.js +126 -0
  8. package/engine/lib/contrast.js +628 -0
  9. package/engine/lib/dom.js +116 -0
  10. package/engine/lib/roles.js +125 -0
  11. package/engine/rules/best-practice/accesskeys.js +29 -0
  12. package/engine/rules/best-practice/aria-label-misuse.js +22 -0
  13. package/engine/rules/best-practice/button-type.js +15 -0
  14. package/engine/rules/best-practice/empty-heading.js +19 -0
  15. package/engine/rules/best-practice/fieldset-legend.js +35 -0
  16. package/engine/rules/best-practice/heading-order.js +30 -0
  17. package/engine/rules/best-practice/landmark-one-main.js +22 -0
  18. package/engine/rules/best-practice/landmark-top-level.js +46 -0
  19. package/engine/rules/best-practice/landmark-unique.js +84 -0
  20. package/engine/rules/best-practice/multiple-labels.js +23 -0
  21. package/engine/rules/best-practice/new-window-link.js +21 -0
  22. package/engine/rules/best-practice/no-autofocus.js +16 -0
  23. package/engine/rules/best-practice/page-heading-one.js +17 -0
  24. package/engine/rules/best-practice/positive-tabindex.js +17 -0
  25. package/engine/rules/best-practice/redundant-alt-phrase.js +22 -0
  26. package/engine/rules/best-practice/redundant-aria-label.js +18 -0
  27. package/engine/rules/best-practice/redundant-aria.js +32 -0
  28. package/engine/rules/best-practice/redundant-image-alt.js +20 -0
  29. package/engine/rules/best-practice/redundant-role.js +62 -0
  30. package/engine/rules/best-practice/redundant-tabindex.js +15 -0
  31. package/engine/rules/best-practice/region.js +73 -0
  32. package/engine/rules/index.js +179 -0
  33. package/engine/rules/wcag/1.1.1-area-alt.js +18 -0
  34. package/engine/rules/wcag/1.1.1-image-alt.js +48 -0
  35. package/engine/rules/wcag/1.1.1-input-image-alt.js +17 -0
  36. package/engine/rules/wcag/1.1.1-object-alt.js +17 -0
  37. package/engine/rules/wcag/1.1.1-svg-img-alt.js +34 -0
  38. package/engine/rules/wcag/1.2.2-media-captions.js +19 -0
  39. package/engine/rules/wcag/1.3.1-aria-required-children.js +66 -0
  40. package/engine/rules/wcag/1.3.1-aria-required-parent.js +49 -0
  41. package/engine/rules/wcag/1.3.1-definition-list.js +21 -0
  42. package/engine/rules/wcag/1.3.1-dlitem-parent.js +17 -0
  43. package/engine/rules/wcag/1.3.1-form-label.js +81 -0
  44. package/engine/rules/wcag/1.3.1-list-structure.js +59 -0
  45. package/engine/rules/wcag/1.3.1-listitem-parent.js +33 -0
  46. package/engine/rules/wcag/1.3.1-p-as-heading.js +25 -0
  47. package/engine/rules/wcag/1.3.1-table-headers.js +61 -0
  48. package/engine/rules/wcag/1.3.5-autocomplete-valid.js +38 -0
  49. package/engine/rules/wcag/1.4.1-link-in-text-block.js +74 -0
  50. package/engine/rules/wcag/1.4.10-reflow.js +25 -0
  51. package/engine/rules/wcag/1.4.11-non-text-contrast.js +109 -0
  52. package/engine/rules/wcag/1.4.12-text-spacing.js +71 -0
  53. package/engine/rules/wcag/1.4.2-audio-control.js +40 -0
  54. package/engine/rules/wcag/1.4.3-color-contrast.js +462 -0
  55. package/engine/rules/wcag/1.4.3-control-contrast.js +72 -0
  56. package/engine/rules/wcag/1.4.4-meta-viewport.js +22 -0
  57. package/engine/rules/wcag/1.4.6-contrast-enhanced.js +11 -0
  58. package/engine/rules/wcag/2.1.1-scrollable-region-focusable.js +48 -0
  59. package/engine/rules/wcag/2.2.1-meta-refresh.js +20 -0
  60. package/engine/rules/wcag/2.2.2-pause-stop-hide.js +25 -0
  61. package/engine/rules/wcag/2.4.1-bypass-blocks.js +36 -0
  62. package/engine/rules/wcag/2.4.11-focus-not-obscured.js +80 -0
  63. package/engine/rules/wcag/2.4.2-document-title.js +18 -0
  64. package/engine/rules/wcag/2.4.4-link-name.js +19 -0
  65. package/engine/rules/wcag/2.4.7-focus-visible.js +46 -0
  66. package/engine/rules/wcag/2.5.3-label-in-name.js +88 -0
  67. package/engine/rules/wcag/2.5.5-target-size-enhanced.js +14 -0
  68. package/engine/rules/wcag/2.5.8-target-size.js +159 -0
  69. package/engine/rules/wcag/3.1.1-html-lang.js +31 -0
  70. package/engine/rules/wcag/3.1.2-valid-lang-parts.js +20 -0
  71. package/engine/rules/wcag/3.3.8-auth-field-obstruction.js +37 -0
  72. package/engine/rules/wcag/4.1.2-aria-allowed-attr.js +44 -0
  73. package/engine/rules/wcag/4.1.2-aria-attr-valid.js +50 -0
  74. package/engine/rules/wcag/4.1.2-aria-field-name.js +54 -0
  75. package/engine/rules/wcag/4.1.2-aria-hidden-focus.js +48 -0
  76. package/engine/rules/wcag/4.1.2-aria-valid-refs.js +61 -0
  77. package/engine/rules/wcag/4.1.2-button-name.js +17 -0
  78. package/engine/rules/wcag/4.1.2-dialog-name.js +35 -0
  79. package/engine/rules/wcag/4.1.2-frame-title.js +17 -0
  80. package/engine/rules/wcag/4.1.2-label-for-valid.js +25 -0
  81. package/engine/rules/wcag/4.1.2-nested-interactive.js +30 -0
  82. package/engine/rules/wcag/4.1.2-role-required-aria.js +36 -0
  83. package/engine/rules/wcag/4.1.2-valid-role.js +52 -0
  84. package/engine/wcag22.js +116 -0
  85. package/engine/wcag30-draft.js +149 -0
  86. package/package.json +34 -0
@@ -0,0 +1,149 @@
1
+ // The WCAG 3.0 guideline catalog — derived from the W3C Working DRAFT of
2
+ // 3 March 2026 (https://www.w3.org/TR/wcag-3.0/). WCAG 3.0 is a work in
3
+ // progress: the W3C says it "may be updated, replaced, or obsoleted" and it
4
+ // is not appropriate for conformance claims. Everything this module exports
5
+ // is therefore marked draft, and every UI that shows it must say so.
6
+ //
7
+ // The draft has no A/AA/AAA levels and no success criteria; it organizes
8
+ // outcome-style guidelines (with core/supplemental requirements below them)
9
+ // under twelve functional groups. Our automated rules are written against
10
+ // WCAG 2.x; `scToGuideline` maps each 2.x criterion we automate to the
11
+ // draft guideline that covers the same user need, judged by reading both
12
+ // specs side by side. Revisit the mapping with every new draft.
13
+
14
+ export const draftInfo = {
15
+ id: 'wcag30-draft',
16
+ name: 'WCAG 3.0',
17
+ status: 'W3C Working Draft',
18
+ dated: '2026-03-03',
19
+ draft: true,
20
+ url: 'https://www.w3.org/TR/wcag-3.0/',
21
+ note: 'A draft under active development: findings are WCAG 2.x rule results mapped to draft guidelines. Not suitable for conformance claims.',
22
+ };
23
+
24
+ const g = (num, name, group, anchor) => ({
25
+ num,
26
+ name,
27
+ group,
28
+ url: `https://www.w3.org/TR/wcag-3.0/#${anchor}`,
29
+ });
30
+
31
+ // All 44 guidelines of the 2026-03-03 draft, in document order.
32
+ export const guidelines = [
33
+ // 2.1 Images and media
34
+ g('2.1.1', 'Image alternatives', 'Images and media', 'image-alternatives'),
35
+ g('2.1.2', 'Media alternatives', 'Images and media', 'media-alternatives'),
36
+ g('2.1.3', 'Non-text alternatives', 'Images and media', 'non-text-alternatives'),
37
+ g('2.1.4', 'Captions', 'Images and media', 'captions'),
38
+ g('2.1.5', 'Audio descriptions', 'Images and media', 'audio-descriptions'),
39
+ g('2.1.6', 'Figure captions', 'Images and media', 'figure-captions'),
40
+ g('2.1.7', 'Single sense', 'Images and media', 'single-sense'),
41
+ // 2.2 Text and wording
42
+ g('2.2.1', 'Text appearance', 'Text and wording', 'text-appearance'),
43
+ g('2.2.2', 'Text-to-speech', 'Text and wording', 'text-to-speech'),
44
+ g('2.2.3', 'Clear language', 'Text and wording', 'clear-language'),
45
+ // 2.3 Interactive components
46
+ g('2.3.1', 'Keyboard focus appearance', 'Interactive components', 'keyboard-focus-appearance'),
47
+ g('2.3.2', 'Pointer focus appearance', 'Interactive components', 'pointer-focus-appearance'),
48
+ g('2.3.3', 'Navigating content', 'Interactive components', 'navigating-content'),
49
+ g('2.3.4', 'Expected behavior', 'Interactive components', 'expected-behavior'),
50
+ g('2.3.5', 'Control information', 'Interactive components', 'control-information'),
51
+ // 2.4 Input / operation
52
+ g('2.4.1', 'Keyboard interface input', 'Input and operation', 'keyboard-interface-input'),
53
+ g('2.4.2', 'Physical or cognitive effort when using keyboard', 'Input and operation', 'physical-or-cognitive-effort-when-using-keyboard'),
54
+ g('2.4.3', 'Pointer input', 'Input and operation', 'pointer-input'),
55
+ g('2.4.4', 'Speech and voice input', 'Input and operation', 'speech-and-voice-input'),
56
+ g('2.4.5', 'Input operation', 'Input and operation', 'input-operation-0'),
57
+ g('2.4.6', 'Authentication', 'Input and operation', 'authentication'),
58
+ // 2.5 Error handling
59
+ g('2.5.1', 'Correct errors', 'Error handling', 'correct-errors'),
60
+ g('2.5.2', 'Prevent errors', 'Error handling', 'prevent-errors'),
61
+ // 2.6 Animation and movement
62
+ g('2.6.1', 'Avoid physical harm', 'Animation and movement', 'avoid-physical-harm'),
63
+ // 2.7 Layout
64
+ g('2.7.1', 'Recognizable layouts', 'Layout', 'recognizable-layouts'),
65
+ g('2.7.2', 'User orientation', 'Layout', 'user-orientation'),
66
+ g('2.7.3', 'Structure', 'Layout', 'structure'),
67
+ g('2.7.4', 'No obstruction', 'Layout', 'no-obstruction'),
68
+ // 2.8 Consistency across views
69
+ g('2.8.1', 'Consistency', 'Consistency across views', 'consistency'),
70
+ // 2.9 Process and task completion
71
+ g('2.9.1', 'Avoid exclusionary cognitive tasks', 'Process and task completion', 'avoid-exclusionary-cognitive-tasks'),
72
+ g('2.9.2', 'Adequate time', 'Process and task completion', 'adequate-time'),
73
+ g('2.9.3', 'Avoid deception', 'Process and task completion', 'avoid-deception'),
74
+ g('2.9.4', 'Retain information', 'Process and task completion', 'retain-information'),
75
+ g('2.9.5', 'Complete tasks', 'Process and task completion', 'complete-tasks'),
76
+ g('2.9.6', 'Unnecessary steps', 'Process and task completion', 'unnecessary-steps'),
77
+ // 2.10 Policy and protection
78
+ g('2.10.1', 'Risk', 'Policy and protection', 'risk'),
79
+ g('2.10.2', 'Algorithms', 'Policy and protection', 'algorithms'),
80
+ // 2.11 Help and feedback
81
+ g('2.11.1', 'Help available', 'Help and feedback', 'help-available'),
82
+ g('2.11.2', 'Feedback', 'Help and feedback', 'feedback'),
83
+ // 2.12 User control
84
+ g('2.12.1', 'Assistive technology control', 'User control', 'assistive-technology-control'),
85
+ g('2.12.2', 'Control text', 'User control', 'control-text'),
86
+ g('2.12.3', 'Adjustable viewport', 'User control', 'adjustable-viewport'),
87
+ g('2.12.4', 'Media control', 'User control', 'media-control'),
88
+ g('2.12.5', 'Content changes', 'User control', 'content-changes'),
89
+ ];
90
+
91
+ // WCAG 2.x criterion (as tagged on rules: wcag111 = SC 1.1.1) → the draft
92
+ // guideline covering the same user need. Only criteria with automated rules
93
+ // appear; the judgment is ours, made by reading both documents.
94
+ export const scToGuideline = {
95
+ 111: '2.1.1', // Non-text Content → Image alternatives
96
+ 122: '2.1.4', // Captions (Prerecorded) → Captions
97
+ 131: '2.7.3', // Info and Relationships → Structure
98
+ 135: '2.4.5', // Identify Input Purpose → Input operation
99
+ 141: '2.2.1', // Use of Color (links in text) → Text appearance
100
+ 142: '2.12.4', // Audio Control → Media control
101
+ 143: '2.2.1', // Contrast (Minimum) → Text appearance
102
+ 144: '2.12.3', // Resize Text (viewport zoom) → Adjustable viewport
103
+ 146: '2.2.1', // Contrast (Enhanced) → Text appearance
104
+ 211: '2.4.1', // Keyboard → Keyboard interface input
105
+ 221: '2.9.2', // Timing Adjustable → Adequate time
106
+ 222: '2.12.4', // Pause, Stop, Hide → Media control
107
+ 241: '2.3.3', // Bypass Blocks → Navigating content
108
+ 242: '2.7.2', // Page Titled → User orientation
109
+ 244: '2.3.5', // Link Purpose (In Context) → Control information
110
+ 255: '2.4.3', // Target Size (Enhanced) → Pointer input
111
+ 258: '2.4.3', // Target Size (Minimum) → Pointer input
112
+ 311: '2.2.2', // Language of Page → Text-to-speech
113
+ 312: '2.2.2', // Language of Parts → Text-to-speech
114
+ 332: '2.3.5', // Labels or Instructions → Control information
115
+ 412: '2.12.1', // Name, Role, Value → Assistive technology control
116
+ };
117
+
118
+ const guidelineByNum = new Map(guidelines.map((entry) => [entry.num, entry]));
119
+ const partlyAutomated = new Set(Object.values(scToGuideline));
120
+
121
+ /** The draft guideline (num/name/group/url) a rule's wcagNNN tag maps to. */
122
+ export function guidelineForTags(tags) {
123
+ for (const tag of tags ?? []) {
124
+ const sc = tag.match(/^wcag(\d{3,4})$/)?.[1];
125
+ if (sc && scToGuideline[sc]) return guidelineByNum.get(scToGuideline[sc]);
126
+ }
127
+ return null;
128
+ }
129
+
130
+ /**
131
+ * The whole draft as a review checklist. Every guideline is listed — the
132
+ * draft is too young for any of it to be silently skipped — shaped like the
133
+ * 2.2 manual-review entries so existing UIs render it unchanged: `level` has
134
+ * no meaning in the draft (no conformance levels yet) and `principle`
135
+ * carries the draft's functional group.
136
+ */
137
+ export function manualReviewChecklist() {
138
+ return guidelines.map(({ num, name, group, url }) => ({
139
+ num,
140
+ name,
141
+ level: 'draft',
142
+ principle: group,
143
+ url,
144
+ automation: partlyAutomated.has(num) ? 'partial' : 'manual',
145
+ draft: true,
146
+ }));
147
+ }
148
+
149
+ export default { draftInfo, guidelines, scToGuideline, guidelineForTags, manualReviewChecklist };
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "pour-engine",
3
+ "version": "1.1.0",
4
+ "description": "Accessibility (WCAG 2.2) audit engine written from the W3C specifications \u2014 runs entirely in the browser, no dependencies.",
5
+ "type": "module",
6
+ "main": "engine/index.js",
7
+ "exports": {
8
+ ".": "./engine/index.js"
9
+ },
10
+ "files": [
11
+ "engine",
12
+ "config",
13
+ "README.md",
14
+ "LICENSE"
15
+ ],
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/pourdev/pour-engine.git"
19
+ },
20
+ "homepage": "https://pour.dev",
21
+ "author": "David Yarham",
22
+ "license": "MIT",
23
+ "keywords": [
24
+ "accessibility",
25
+ "a11y",
26
+ "wcag",
27
+ "wcag22",
28
+ "audit",
29
+ "testing"
30
+ ],
31
+ "engines": {
32
+ "node": ">=18"
33
+ }
34
+ }