@diegovelasquezweb/a11y-engine 0.6.0 → 0.6.2

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.
package/README.md CHANGED
@@ -37,6 +37,10 @@ import {
37
37
  getChecklist,
38
38
  getRemediationGuide,
39
39
  getSourcePatterns,
40
+ getScannerHelp,
41
+ getPersonaReference,
42
+ getUiHelp,
43
+ getKnowledge,
40
44
  } from "@diegovelasquezweb/a11y-engine";
41
45
  ```
42
46
 
@@ -113,6 +117,18 @@ These functions render final artifacts from scan payload data.
113
117
  | `getRemediationGuide(payload, options?)` | `{ markdown, contentType }` | Markdown remediation guide |
114
118
  | `getSourcePatterns(projectDir, options?)` | `{ findings, summary }` | Source code pattern analysis |
115
119
 
120
+ ### Knowledge API
121
+
122
+ These functions expose scanner help content, persona explanations, and UI copy
123
+ so frontends or agents can render tooltips and guidance from engine-owned data.
124
+
125
+ | Function | Returns | Description |
126
+ | :--- | :--- | :--- |
127
+ | `getScannerHelp(options?)` | `{ locale, version, title, engines, options }` | Scanner option and engine help metadata |
128
+ | `getPersonaReference(options?)` | `{ locale, version, personas }` | Persona labels, descriptions, and mapping hints |
129
+ | `getUiHelp(options?)` | `{ locale, version, tooltips, glossary }` | Shared tooltip copy and glossary entries |
130
+ | `getKnowledge(options?)` | `{ locale, version, scanner, personas, tooltips, glossary }` | Full documentation pack for UI or agent flows |
131
+
116
132
  See [API Reference](docs/api-reference.md) for exact options and return types.
117
133
 
118
134
  ## Optional CLI
@@ -0,0 +1,385 @@
1
+ export default {
2
+ version: "1.0.0",
3
+ locales: {
4
+ en: {
5
+ scanner: {
6
+ title: "Scanner Help",
7
+ engines: [
8
+ {
9
+ id: "axe",
10
+ label: "axe-core",
11
+ description: "Primary WCAG rule engine for rendered DOM violations.",
12
+ coverage: "Broadest WCAG rule coverage with over 90 checks across all four principles. Catches missing labels, contrast failures, landmark structure, and ARIA misuse.",
13
+ speed: "Fast",
14
+ defaultEnabled: true,
15
+ },
16
+ {
17
+ id: "cdp",
18
+ label: "CDP",
19
+ description: "Chrome accessibility tree checks for name/role/focus gaps.",
20
+ coverage: "Inspects the browser accessibility tree directly via Chrome DevTools Protocol. Catches name and role issues that axe misses at the DOM level.",
21
+ speed: "Medium",
22
+ defaultEnabled: true,
23
+ },
24
+ {
25
+ id: "pa11y",
26
+ label: "pa11y",
27
+ description: "HTML CodeSniffer checks to complement axe coverage.",
28
+ coverage: "Runs HTML CodeSniffer against the rendered page. Complements axe with additional HTML-level checks and alternative rule interpretations.",
29
+ speed: "Medium",
30
+ defaultEnabled: true,
31
+ },
32
+ ],
33
+ options: [
34
+ {
35
+ id: "maxRoutes",
36
+ label: "Max routes",
37
+ description: "Limits how many same-origin pages are scanned.",
38
+ defaultValue: 10,
39
+ type: "number",
40
+ },
41
+ {
42
+ id: "crawlDepth",
43
+ label: "Crawl depth",
44
+ description: "Controls how many link levels are explored from the start page.",
45
+ defaultValue: 1,
46
+ type: "number",
47
+ },
48
+ {
49
+ id: "waitUntil",
50
+ label: "Load strategy",
51
+ description: "Navigation readiness event before scanning each page.",
52
+ defaultValue: "domcontentloaded",
53
+ type: "enum",
54
+ allowedValues: [
55
+ {
56
+ value: "domcontentloaded",
57
+ label: "DOM Ready",
58
+ description: "Fires when the initial HTML is parsed. Fastest — use for server-rendered pages.",
59
+ },
60
+ {
61
+ value: "load",
62
+ label: "Page Load",
63
+ description: "Waits for all resources (images, scripts) to finish loading. Good for pages with critical above-the-fold assets.",
64
+ },
65
+ {
66
+ value: "networkidle",
67
+ label: "Network Idle",
68
+ description: "Waits until no network requests for 500ms. Best for SPAs and pages with deferred content loading.",
69
+ },
70
+ ],
71
+ },
72
+ {
73
+ id: "timeoutMs",
74
+ label: "Timeout",
75
+ description: "Maximum time to wait for each page to load before aborting.",
76
+ defaultValue: 30000,
77
+ type: "number",
78
+ },
79
+ {
80
+ id: "viewport",
81
+ label: "Viewport",
82
+ description: "Browser window size used during the audit.",
83
+ defaultValue: "1280x800",
84
+ type: "text",
85
+ },
86
+ {
87
+ id: "colorScheme",
88
+ label: "Color scheme",
89
+ description: "Emulates light or dark mode during the scan.",
90
+ defaultValue: "light",
91
+ type: "enum",
92
+ allowedValues: [
93
+ { value: "light", label: "Light" },
94
+ { value: "dark", label: "Dark" },
95
+ ],
96
+ },
97
+ {
98
+ id: "axeTags",
99
+ label: "Conformance tags",
100
+ description: "WCAG tag filters sent to axe-core.",
101
+ defaultValue: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22a", "wcag22aa"],
102
+ type: "string[]",
103
+ },
104
+ ],
105
+ },
106
+ conformanceLevels: [
107
+ {
108
+ id: "A",
109
+ label: "Level A",
110
+ badge: "Minimum",
111
+ description: "The baseline: essential requirements that remove the most severe barriers.",
112
+ shortDescription: "Minimum baseline",
113
+ hint: "Failing Level A means some users cannot access the content at all.",
114
+ tags: ["wcag2a", "wcag21a", "wcag22a"],
115
+ },
116
+ {
117
+ id: "AA",
118
+ label: "Level AA",
119
+ badge: "Standard",
120
+ description: "The recommended target for most websites — required by most accessibility laws.",
121
+ shortDescription: "Recommended for most websites",
122
+ hint: "Referenced by ADA, Section 508, EN 301 549, and EAA.",
123
+ tags: ["wcag2a", "wcag21a", "wcag22a", "wcag2aa", "wcag21aa", "wcag22aa"],
124
+ },
125
+ {
126
+ id: "AAA",
127
+ label: "Level AAA",
128
+ badge: "Enhanced",
129
+ description: "The highest conformance level — not required but beneficial for specialized audiences.",
130
+ shortDescription: "Strictest — not required by most regulations",
131
+ hint: "Full AAA conformance is not recommended as a general policy for entire sites.",
132
+ tags: ["wcag2a", "wcag21a", "wcag22a", "wcag2aa", "wcag21aa", "wcag22aa", "wcag2aaa"],
133
+ },
134
+ ],
135
+ wcagPrinciples: [
136
+ {
137
+ id: "perceivable",
138
+ name: "Perceivable",
139
+ description: "Information and UI components must be presentable to users in ways they can perceive.",
140
+ criterionPrefix: " 1.",
141
+ number: 1,
142
+ },
143
+ {
144
+ id: "operable",
145
+ name: "Operable",
146
+ description: "UI components and navigation must be operable.",
147
+ criterionPrefix: " 2.",
148
+ number: 2,
149
+ },
150
+ {
151
+ id: "understandable",
152
+ name: "Understandable",
153
+ description: "Information and the operation of the UI must be understandable.",
154
+ criterionPrefix: " 3.",
155
+ number: 3,
156
+ },
157
+ {
158
+ id: "robust",
159
+ name: "Robust",
160
+ description: "Content must be robust enough to be interpreted by a wide variety of user agents.",
161
+ criterionPrefix: " 4.",
162
+ number: 4,
163
+ },
164
+ ],
165
+ severityLevels: [
166
+ {
167
+ id: "Critical",
168
+ label: "Critical",
169
+ shortDescription: "Functional blockers",
170
+ description: "Blocks key user tasks with no practical workaround.",
171
+ order: 1,
172
+ },
173
+ {
174
+ id: "Serious",
175
+ label: "Serious",
176
+ shortDescription: "Serious impediments",
177
+ description: "Major barrier with difficult workaround or significant friction.",
178
+ order: 2,
179
+ },
180
+ {
181
+ id: "Moderate",
182
+ label: "Moderate",
183
+ shortDescription: "Significant friction",
184
+ description: "Usability degradation that still allows task completion.",
185
+ order: 3,
186
+ },
187
+ {
188
+ id: "Minor",
189
+ label: "Minor",
190
+ shortDescription: "Minor violations",
191
+ description: "Lower-impact issue that still reduces quality and consistency.",
192
+ order: 4,
193
+ },
194
+ ],
195
+ personas: {
196
+ screenReader: {
197
+ label: "Screen Reader Users",
198
+ description: "People who rely on spoken output and semantic structure.",
199
+ },
200
+ keyboard: {
201
+ label: "Keyboard-Only Users",
202
+ description: "People who navigate and operate controls using the keyboard only.",
203
+ },
204
+ vision: {
205
+ label: "Low Vision Users",
206
+ description: "People affected by low contrast, scaling, and visual clarity issues.",
207
+ },
208
+ cognitive: {
209
+ label: "Cognitive & Learning Users",
210
+ description: "People who benefit from predictable behavior and clear instructions.",
211
+ },
212
+ },
213
+ concepts: {
214
+ score: {
215
+ title: "Compliance Score",
216
+ body: "Weighted score from severity totals. It is a prioritization signal, not legal certification.",
217
+ context: "Based on automated accessibility technical checks.",
218
+ },
219
+ wcagStatus: {
220
+ title: "WCAG Status",
221
+ body: "Pass = no issues. Conditional Pass = only Moderate/Minor. Fail = any Critical/Serious remaining.",
222
+ },
223
+ severityBreakdown: {
224
+ title: "Severity Breakdown",
225
+ body: "Issue counts grouped by user impact and task completion risk.",
226
+ },
227
+ personaImpact: {
228
+ title: "Persona Impact",
229
+ body: "Shows which user groups are most affected by current findings.",
230
+ },
231
+ quickWins: {
232
+ title: "Quick Wins",
233
+ body: "Top Critical/Serious findings that already include concrete fix code.",
234
+ context: "High-priority issues with ready-to-use code fixes for immediate remediation.",
235
+ },
236
+ findingsFilter: {
237
+ title: "Findings Filter",
238
+ body: "Filter findings by severity or WCAG principle to focus remediation.",
239
+ },
240
+ },
241
+ outputs: {
242
+ pdf: {
243
+ title: "Stakeholder Report",
244
+ description: "A formal PDF accessibility compliance report.",
245
+ detail: "Designed for clients, non-technical stakeholders, project managers, and auditors. Includes the compliance score, WCAG status, severity breakdown, and a prioritized list of findings with recommended fixes — formatted for sharing and sign-off.",
246
+ },
247
+ checklist: {
248
+ title: "Manual Checklist",
249
+ description: "An interactive WCAG 2.2 AA manual testing checklist.",
250
+ detail: "Automated scanners catch around 30–40% of accessibility issues. This checklist covers the remaining manual checks — keyboard navigation, screen reader behavior, focus management, motion, zoom, and cognitive accessibility. Use it alongside the automated findings for a complete audit.",
251
+ },
252
+ json: {
253
+ title: "JSON Export",
254
+ description: "Machine-readable snapshot of all findings, scores, and metadata.",
255
+ detail: "Useful for integrating with CI/CD pipelines, tracking compliance over time, importing into dashboards, or diffing two audits programmatically. Includes all enriched findings with WCAG mappings, fix code, severity, and page location.",
256
+ },
257
+ remediation: {
258
+ title: "Remediation Guide",
259
+ description: "A structured Markdown guide for AI agents and developers.",
260
+ detail: "Contains prioritized fixes with ready-to-use code snippets, framework-specific guardrails, WCAG mappings, selector context, and verification commands. Designed to be fed directly into an AI coding agent (Claude, Copilot, Cursor) or used by developers for systematic remediation.",
261
+ },
262
+ },
263
+ docs: {
264
+ sections: [
265
+ {
266
+ id: "understanding-wcag",
267
+ heading: "Understanding WCAG",
268
+ groups: [
269
+ {
270
+ id: "wcag-versions",
271
+ label: "WCAG Versions",
272
+ articles: [
273
+ {
274
+ id: "wcag-2-0",
275
+ title: "WCAG 2.0",
276
+ badge: "2008",
277
+ summary: "The original W3C recommendation that established the foundation for web accessibility.",
278
+ body: "Introduced the four principles (Perceivable, Operable, Understandable, Robust) and three conformance levels (A, AA, AAA). Covers core requirements like text alternatives, keyboard access, color contrast, and form labels. Still widely referenced in legal frameworks worldwide.",
279
+ },
280
+ {
281
+ id: "wcag-2-1",
282
+ title: "WCAG 2.1",
283
+ badge: "2018",
284
+ summary: "Extended 2.0 with 17 new success criteria for mobile, low vision, and cognitive disabilities.",
285
+ body: "Added criteria for touch targets (2.5.5), text spacing (1.4.12), content reflow (1.4.10), orientation (1.3.4), and input purpose (1.3.5). Required by the European Accessibility Act (EAA) and referenced in updated ADA guidance. All 2.0 criteria remain \u2014 2.1 is a superset.",
286
+ },
287
+ {
288
+ id: "wcag-2-2",
289
+ title: "WCAG 2.2",
290
+ badge: "2023",
291
+ summary: "The latest version, adding 9 new criteria focused on cognitive accessibility and consistent help.",
292
+ body: "Key additions include consistent help (3.2.6), accessible authentication (3.3.8), dragging movements (2.5.7), and focus appearance (2.4.11/2.4.12). Removed criterion 4.1.1 (Parsing) as it\u2019s now handled by modern browsers. Supersedes both 2.0 and 2.1 \u2014 all prior criteria are included.",
293
+ },
294
+ ],
295
+ },
296
+ {
297
+ id: "conformance-levels",
298
+ label: "Conformance Levels",
299
+ articles: [
300
+ {
301
+ id: "level-a",
302
+ title: "Level A",
303
+ badge: "Minimum",
304
+ summary: "The baseline: essential requirements that remove the most severe barriers.",
305
+ body: "Covers fundamentals like non-text content alternatives (1.1.1), keyboard operability (2.1.1), page titles (2.4.2), and language of the page (3.1.1). Failing Level A means some users cannot access the content at all. Every site should meet Level A at minimum.",
306
+ },
307
+ {
308
+ id: "level-aa",
309
+ title: "Level AA",
310
+ badge: "Standard",
311
+ summary: "The recommended target for most websites \u2014 required by most accessibility laws.",
312
+ body: "Includes all Level A criteria plus requirements for color contrast (1.4.3 \u2014 4.5:1 ratio), resize text (1.4.4), focus visible (2.4.7), error suggestion (3.3.3), and consistent navigation (3.2.3). Referenced by ADA, Section 508, EN 301 549, and EAA. This is the standard the scanner defaults to.",
313
+ },
314
+ {
315
+ id: "level-aaa",
316
+ title: "Level AAA",
317
+ badge: "Enhanced",
318
+ summary: "The highest conformance level \u2014 not required but beneficial for specialized audiences.",
319
+ body: "Adds stricter contrast (1.4.6 \u2014 7:1 ratio), sign language for audio (1.2.6), extended audio description (1.2.7), and reading level (3.1.5). Full AAA conformance is not recommended as a general policy because some criteria cannot be satisfied for all content types. Useful for targeted sections like education or government services.",
320
+ },
321
+ ],
322
+ },
323
+ ],
324
+ },
325
+ {
326
+ id: "how-it-works",
327
+ heading: "How It Works",
328
+ articles: [
329
+ {
330
+ id: "load-render",
331
+ title: "Load & Render",
332
+ icon: "globe",
333
+ summary: "The scanner loads your URL in a real browser (Chromium) and waits for full render.",
334
+ body: "A headless Chromium instance navigates to the target URL, executes JavaScript, waits for network idle, and captures the fully rendered DOM. This ensures dynamic content (SPAs, lazy-loaded elements) is included in the analysis.",
335
+ },
336
+ {
337
+ id: "multi-engine-scan",
338
+ title: "Multi-Engine Scan",
339
+ icon: "cpu",
340
+ summary: "Multiple engines (axe-core, CDP, pa11y) run in parallel for broader coverage.",
341
+ body: "Each engine uses different detection techniques: axe-core runs DOM-based rule checks, CDP inspects accessibility tree properties via Chrome DevTools Protocol, and pa11y validates rendered HTML against WCAG criteria. Combined, they catch issues that any single engine would miss.",
342
+ },
343
+ {
344
+ id: "merge-deduplicate",
345
+ title: "Merge & Deduplicate",
346
+ icon: "git-merge",
347
+ summary: "Findings from all engines are normalized, deduplicated, and scored by severity.",
348
+ body: "Results are merged by selector + rule ID to eliminate duplicates. Each finding is mapped to its WCAG criterion, assigned a severity level (Critical/Serious/Moderate/Minor), and enriched with persona impact data showing which disability groups are affected.",
349
+ },
350
+ {
351
+ id: "ai-enrichment",
352
+ title: "AI Enrichment",
353
+ icon: "sparkles",
354
+ summary: "Each issue gets code fixes, MDN references, and framework-specific guidance.",
355
+ body: "The intelligence layer generates actionable fix descriptions, ready-to-use code snippets, links to relevant MDN documentation, and effort estimates. Quick Wins are identified as high-impact issues with low-effort fixes for immediate remediation.",
356
+ },
357
+ ],
358
+ },
359
+ ],
360
+ },
361
+ glossary: [
362
+ {
363
+ term: "Critical",
364
+ definition: "Blocks key user tasks with no practical workaround.",
365
+ },
366
+ {
367
+ term: "Serious",
368
+ definition: "Major barrier with difficult workaround or significant friction.",
369
+ },
370
+ {
371
+ term: "Moderate",
372
+ definition: "Usability degradation that still allows task completion.",
373
+ },
374
+ {
375
+ term: "Minor",
376
+ definition: "Lower-impact issue that still reduces quality and consistency.",
377
+ },
378
+ {
379
+ term: "Conditional Pass",
380
+ definition: "No Critical/Serious findings remain, but Moderate/Minor issues still exist.",
381
+ },
382
+ ],
383
+ },
384
+ },
385
+ };
@@ -1,6 +1,6 @@
1
1
  # API Reference
2
2
 
3
- **Navigation**: [Home](../README.md) • [Architecture](architecture.md) • [API Reference](api-reference.md) • [CLI Handbook](cli-handbook.md) • [Output Artifacts](outputs.md) • [Engine Manifest](engine-manifest.md) • [Testing](testing.md)
3
+ **Navigation**: [Home](../README.md) • [Architecture](architecture.md) • [Intelligence](intelligence.md) • [API Reference](api-reference.md) • [CLI Handbook](cli-handbook.md) • [Output Artifacts](outputs.md) • [Engine Manifest](engine-manifest.md) • [Testing](testing.md)
4
4
 
5
5
  ---
6
6
 
@@ -102,6 +102,36 @@ Returns: `Promise<RemediationGuide>` (`{ markdown, contentType }`)
102
102
 
103
103
  Returns: `Promise<SourcePatternResult>`
104
104
 
105
+ ## Knowledge API
106
+
107
+ ### `getScannerHelp(options?)`
108
+
109
+ - `options`: `KnowledgeOptions`
110
+ - `locale?: string`
111
+
112
+ Returns: `ScannerHelp` (`{ locale, version, title, engines, options }`)
113
+
114
+ ### `getPersonaReference(options?)`
115
+
116
+ - `options`: `KnowledgeOptions`
117
+ - `locale?: string`
118
+
119
+ Returns: `PersonaReference` (`{ locale, version, personas }`)
120
+
121
+ ### `getUiHelp(options?)`
122
+
123
+ - `options`: `KnowledgeOptions`
124
+ - `locale?: string`
125
+
126
+ Returns: `UiHelp` (`{ locale, version, tooltips, glossary }`)
127
+
128
+ ### `getKnowledge(options?)`
129
+
130
+ - `options`: `KnowledgeOptions`
131
+ - `locale?: string`
132
+
133
+ Returns: `EngineKnowledge` (`{ locale, version, scanner, personas, tooltips, glossary }`)
134
+
105
135
  ---
106
136
 
107
137
  Canonical type source: `src/index.d.mts`
@@ -1,6 +1,6 @@
1
1
  # Engine Architecture
2
2
 
3
- **Navigation**: [Home](../README.md) • [Architecture](architecture.md) • [API Reference](api-reference.md) • [CLI Handbook](cli-handbook.md) • [Output Artifacts](outputs.md) • [Engine Manifest](engine-manifest.md) • [Testing](testing.md)
3
+ **Navigation**: [Home](../README.md) • [Architecture](architecture.md) • [Intelligence](intelligence.md) • [API Reference](api-reference.md) • [CLI Handbook](cli-handbook.md) • [Output Artifacts](outputs.md) • [Engine Manifest](engine-manifest.md) • [Testing](testing.md)
4
4
 
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  # CLI Handbook
2
2
 
3
- **Navigation**: [Home](../README.md) • [Architecture](architecture.md) • [API Reference](api-reference.md) • [CLI Handbook](cli-handbook.md) • [Output Artifacts](outputs.md) • [Engine Manifest](engine-manifest.md) • [Testing](testing.md)
3
+ **Navigation**: [Home](../README.md) • [Architecture](architecture.md) • [Intelligence](intelligence.md) • [API Reference](api-reference.md) • [CLI Handbook](cli-handbook.md) • [Output Artifacts](outputs.md) • [Engine Manifest](engine-manifest.md) • [Testing](testing.md)
4
4
 
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  # Engine Manifest
2
2
 
3
- **Navigation**: [Home](../README.md) • [Architecture](architecture.md) • [CLI Handbook](cli-handbook.md) • [Output Artifacts](outputs.md) • [Engine Manifest](engine-manifest.md) • [Testing](testing.md)
3
+ **Navigation**: [Home](../README.md) • [Architecture](architecture.md) • [Intelligence](intelligence.md) • [API Reference](api-reference.md) • [CLI Handbook](cli-handbook.md) • [Output Artifacts](outputs.md) • [Engine Manifest](engine-manifest.md) • [Testing](testing.md)
4
4
 
5
5
  ---
6
6
 
@@ -0,0 +1,209 @@
1
+ # Intelligence & Enrichment
2
+
3
+ **Navigation**: [Home](../README.md) • [Architecture](architecture.md) • [API Reference](api-reference.md) • [CLI Handbook](cli-handbook.md) • [Output Artifacts](outputs.md) • [Engine Manifest](engine-manifest.md) • [Intelligence](intelligence.md) • [Testing](testing.md)
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ - [Overview](#overview)
10
+ - [Enrichment Pipeline](#enrichment-pipeline)
11
+ - [Intelligence Database](#intelligence-database)
12
+ - [Finding Enrichment](#finding-enrichment)
13
+ - [False Positive Filtering](#false-positive-filtering)
14
+ - [Cross-Page Deduplication](#cross-page-deduplication)
15
+ - [Ownership Classification](#ownership-classification)
16
+ - [Persona Mapping](#persona-mapping)
17
+ - [Scoring & Compliance](#scoring--compliance)
18
+ - [Source Code Patterns](#source-code-patterns)
19
+ - [Assets Reference](#assets-reference)
20
+
21
+ ---
22
+
23
+ ## Overview
24
+
25
+ After the runtime engines (axe-core, CDP, pa11y) produce raw violations, the analyzer transforms them into enriched findings. This is the step that turns a list of rule IDs and selectors into actionable output with fix code, framework guidance, ownership hints, persona impact, and compliance scoring.
26
+
27
+ The enrichment runs in `src/enrichment/analyzer.mjs` and is invoked by `runAudit` (programmatic) or `audit.mjs` (CLI).
28
+
29
+ ## Enrichment Pipeline
30
+
31
+ ```mermaid
32
+ flowchart TD
33
+ RAW[Raw violations from dom-scanner] --> BUILD[buildFindings]
34
+ BUILD --> IGNORE[Apply ignoreFindings filter]
35
+ IGNORE --> FP[Filter false positives]
36
+ FP --> DEDUP[Cross-page deduplication]
37
+ DEDUP --> META[Compute metadata]
38
+
39
+ subgraph META[Metadata Computation]
40
+ OA[Overall assessment]
41
+ PC[Passed WCAG criteria]
42
+ OOS[Out of scope routes]
43
+ REC[Recommendations]
44
+ TM[Testing methodology]
45
+ INC[Incomplete findings]
46
+ end
47
+
48
+ DEDUP --> OUTPUT[Enriched payload]
49
+ META --> OUTPUT
50
+ ```
51
+
52
+ ## Intelligence Database
53
+
54
+ The engine ships a bundled intelligence database at `assets/remediation/intelligence.mjs`. This contains per-rule entries keyed by axe rule ID (e.g. `color-contrast`, `image-alt`).
55
+
56
+ Each rule entry can include:
57
+
58
+ | Field | Purpose |
59
+ | :--- | :--- |
60
+ | `fix.description` | Plain-language explanation of how to fix |
61
+ | `fix.code` | Ready-to-apply code snippet |
62
+ | `category` | Grouping label (e.g. "Color & Contrast", "Forms") |
63
+ | `related_rules` | Other rule IDs commonly seen together |
64
+ | `managed_by_libraries` | UI libraries that handle this rule (e.g. `["radix-ui"]`) |
65
+ | `false_positive_risk` | Known false positive patterns for this rule |
66
+ | `fix_difficulty_notes` | Edge cases and pitfalls |
67
+ | `framework_notes` | Framework-specific fix guidance (keyed by framework ID) |
68
+ | `cms_notes` | CMS-specific fix guidance |
69
+ | `preferred_relationship_checks` | axe check IDs to prioritize for relationship hints |
70
+ | `guardrails` / `guardrails_overrides` | Must/must-not/verify constraints for automated fixes |
71
+
72
+ When a framework is detected (e.g. `nextjs`), only the relevant framework notes are included in the output. React-based frameworks (`nextjs`, `gatsby`) resolve to `react` notes.
73
+
74
+ ## Finding Enrichment
75
+
76
+ For each raw violation, `buildFindings` produces an enriched finding by:
77
+
78
+ 1. **Selector ranking** — scores all affected selectors by specificity (IDs > data attributes > ARIA > class names), penalizing Tailwind utility classes, and picks the most stable one as `primary_selector`.
79
+
80
+ 2. **WCAG mapping** — maps axe tags to human-readable WCAG labels (e.g. "WCAG 2.1 AA") and resolves the criterion ID (e.g. `1.4.3`) from the WCAG reference database.
81
+
82
+ 3. **Classification** — tags each finding as `A`, `AA`, `AAA`, or `Best Practice` based on its axe tags.
83
+
84
+ 4. **Intelligence lookup** — matches the rule ID against the intelligence database to pull fix description, fix code, category, related rules, guardrails, and difficulty notes.
85
+
86
+ 5. **ARIA role detection** — extracts explicit roles from HTML or infers implicit roles from tag names (e.g. `<nav>` → `navigation`), then links to APG pattern documentation.
87
+
88
+ 6. **Code language detection** — analyzes fix code snippets to determine language (`html`, `jsx`, `css`) for syntax highlighting.
89
+
90
+ 7. **Failure analysis** — extracts the primary failure mode, relationship hints (e.g. "label is not associated with input"), failure check messages, and related DOM context from axe node data.
91
+
92
+ 8. **Component hint** — extracts a semantic component name from the selector (skipping Tailwind utilities) or derives a page hint from the route path.
93
+
94
+ 9. **Impacted users** — looks up which disability groups are affected, first by rule ID, then by axe tag fallback.
95
+
96
+ 10. **File search pattern** — resolves framework-specific glob patterns (from source boundaries) so developers know where to search for the source file.
97
+
98
+ 11. **Stable ID** — generates a deterministic hash (`A11Y-xxxxxx`) from rule ID + URL + selector so findings are stable across scans.
99
+
100
+ ## False Positive Filtering
101
+
102
+ After enrichment, confirmed false positives are removed. Currently covers:
103
+
104
+ - `color-contrast` violations where the evidence HTML contains CSS gradients (`linear-gradient`, `radial-gradient`) — axe cannot compute contrast against gradients.
105
+ - `color-contrast` violations where the element is hidden (`visibility: hidden`, `display: none`).
106
+
107
+ The count of removed false positives is stored in `metadata.fpFiltered`.
108
+
109
+ ## Cross-Page Deduplication
110
+
111
+ When scanning multiple routes, the same component often produces identical violations on every page. The deduplicator groups findings by `rule_id` + normalized `primary_selector` and collapses them into a single representative finding with:
112
+
113
+ - `pages_affected` — number of pages where this violation appears
114
+ - `affected_urls` — list of all affected URLs
115
+ - `total_instances` — sum of DOM element instances across all pages
116
+
117
+ Selectors like `html`, `body`, and `:root` are never grouped (they're too generic).
118
+
119
+ ## Ownership Classification
120
+
121
+ Each finding is classified by whether the violation lives in the project's editable source or outside it:
122
+
123
+ | Status | Meaning |
124
+ | :--- | :--- |
125
+ | `primary` | The finding should be fixed in the project source tree |
126
+ | `outside_primary_source` | The element comes from a WordPress plugin, cross-origin iframe, or external embed |
127
+ | `unknown` | Source scope could not be determined (no `projectDir` or framework boundaries) |
128
+
129
+ This drives the `search_strategy` field: `direct_source_patch` for primary findings, `verify_ownership_before_search` for outside/unknown.
130
+
131
+ ## Persona Mapping
132
+
133
+ The engine maps each finding to disability groups using three layers:
134
+
135
+ 1. **Rule-based** — the WCAG reference database (`assets/reporting/wcag-reference.mjs`) contains a `personaMapping` object that maps persona keys to lists of axe rule IDs. If a finding's `ruleId` matches, it's counted for that persona.
136
+
137
+ 2. **Criterion-based** — if no rule match is found, the engine checks if the finding's WCAG criterion ID is associated with a persona through the rule-to-criterion map.
138
+
139
+ 3. **Keyword fallback** — if neither rule nor criterion matches, the `impactedUsers` text is searched for persona keywords (e.g. "blind", "keyboard", "cognitive").
140
+
141
+ A single finding can match multiple personas. The persona configuration (`personaConfig`) defines the labels and icons:
142
+
143
+ | Key | Label |
144
+ | :--- | :--- |
145
+ | `screenReader` | Screen Readers |
146
+ | `keyboard` | Keyboard Only |
147
+ | `vision` | Color/Low Vision |
148
+ | `cognitive` | Cognitive/Motor |
149
+
150
+ ## Scoring & Compliance
151
+
152
+ The compliance score is computed from severity totals using weights defined in `assets/reporting/compliance-config.mjs`:
153
+
154
+ 1. **Severity totals** — counts findings by `Critical`, `Serious`, `Moderate`, `Minor` (excluding AAA and Best Practice findings).
155
+ 2. **Score** — starts at 100, deducts weighted points per finding.
156
+ 3. **Label** — maps score ranges to grades (`Excellent`, `Good Compliance`, `Needs Improvement`, `Poor`, `Critical`).
157
+ 4. **WCAG status** — `Pass` (no findings), `Conditional Pass` (only Moderate/Minor), or `Fail` (any Critical/Serious).
158
+
159
+ The `overallAssessment` in metadata follows the same logic for the formal compliance verdict.
160
+
161
+ Additionally, `passedCriteria` lists all WCAG criterion IDs that had explicit axe passes and no active violations — used in reports to show what the site does well.
162
+
163
+ ## Source Code Patterns
164
+
165
+ The source scanner (`src/source-patterns/source-scanner.mjs`) detects accessibility issues that runtime engines cannot see because they exist only in source code.
166
+
167
+ ### How it works
168
+
169
+ 1. Loads pattern definitions from `assets/remediation/code-patterns.mjs`. Each pattern has:
170
+ - `id` — unique identifier (e.g. `no-aria-hidden-on-focusable`)
171
+ - `regex` — the pattern to search for
172
+ - `globs` — file extensions to scan (e.g. `**/*.tsx`)
173
+ - `title`, `severity`, `wcag`, `wcag_criterion`, `wcag_level`
174
+ - `fix_description` — how to fix matches
175
+ - `requires_manual_verification` — whether context analysis is needed
176
+ - `context_reject_regex` — regex that, if found near the match, means it's not a violation
177
+ - `context_window` — lines of context to check (default 5)
178
+
179
+ 2. Resolves scan directories using framework source boundaries (`assets/remediation/source-boundaries.mjs`). For example, Next.js projects scope to `src/`, `app/`, `pages/`, `components/`.
180
+
181
+ 3. Walks the file tree, skipping `node_modules`, `.git`, `dist`, build outputs, and minified files.
182
+
183
+ 4. For each file matching the pattern's globs, runs the regex line by line. When a match is found:
184
+ - Captures 3 lines of context above and below
185
+ - Runs context validation if `requires_manual_verification` is set — checks if `context_reject_regex` appears nearby. If it does, the finding is marked `potential` instead of `confirmed`.
186
+ - Generates a stable ID (`PAT-xxxxxx`)
187
+
188
+ 5. Output includes a summary with `total`, `confirmed`, and `potential` counts.
189
+
190
+ ### Integration with the audit pipeline
191
+
192
+ When `runAudit` is called with `projectDir` and without `skipPatterns`:
193
+
194
+ 1. The analyzer runs first to detect the framework.
195
+ 2. Source patterns run after enrichment.
196
+ 3. Pattern findings are attached to the payload as `patternFindings` with their own `generated_at`, `project_dir`, `findings`, and `summary`.
197
+ 4. The remediation guide (`getRemediationGuide`) renders pattern findings in a dedicated section.
198
+
199
+ ## Assets Reference
200
+
201
+ | Asset | Used by | Purpose |
202
+ | :--- | :--- | :--- |
203
+ | `remediation/intelligence.mjs` | analyzer | Per-rule fix descriptions, code, guardrails, framework notes |
204
+ | `remediation/axe-check-maps.mjs` | analyzer | Failure mode and relationship hint mappings from axe check IDs |
205
+ | `remediation/guardrails.mjs` | analyzer | Shared guardrail constraints for safe automated fixes |
206
+ | `remediation/source-boundaries.mjs` | analyzer, source-scanner | Framework-specific source directory scoping |
207
+ | `remediation/code-patterns.mjs` | source-scanner | Regex pattern definitions for source code scanning |
208
+ | `reporting/wcag-reference.mjs` | analyzer, reports | WCAG criterion map, persona mapping, impacted users, APG patterns, MDN links |
209
+ | `reporting/compliance-config.mjs` | analyzer, reports | Severity weights, score thresholds, impact mapping |
package/docs/outputs.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Output Artifacts
2
2
 
3
- **Navigation**: [Home](../README.md) • [Architecture](architecture.md) • [API Reference](api-reference.md) • [CLI Handbook](cli-handbook.md) • [Output Artifacts](outputs.md) • [Engine Manifest](engine-manifest.md) • [Testing](testing.md)
3
+ **Navigation**: [Home](../README.md) • [Architecture](architecture.md) • [Intelligence](intelligence.md) • [API Reference](api-reference.md) • [CLI Handbook](cli-handbook.md) • [Output Artifacts](outputs.md) • [Engine Manifest](engine-manifest.md) • [Testing](testing.md)
4
4
 
5
5
  ---
6
6
 
package/docs/testing.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Testing Strategy
2
2
 
3
- **Navigation**: [Home](../README.md) • [Architecture](architecture.md) • [CLI Handbook](cli-handbook.md) • [Output Artifacts](outputs.md) • [Engine Manifest](engine-manifest.md) • [Testing](testing.md)
3
+ **Navigation**: [Home](../README.md) • [Architecture](architecture.md) • [Intelligence](intelligence.md) • [API Reference](api-reference.md) • [CLI Handbook](cli-handbook.md) • [Output Artifacts](outputs.md) • [Engine Manifest](engine-manifest.md) • [Testing](testing.md)
4
4
 
5
5
  ---
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegovelasquezweb/a11y-engine",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "WCAG 2.2 AA accessibility audit engine — scanner, analyzer, and report builders",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -17,6 +17,7 @@ import sourceBoundaries from "../../assets/remediation/source-boundaries.mjs";
17
17
  import complianceConfig from "../../assets/reporting/compliance-config.mjs";
18
18
  import manualChecks from "../../assets/reporting/manual-checks.mjs";
19
19
  import wcagReference from "../../assets/reporting/wcag-reference.mjs";
20
+ import knowledgeData from "../../assets/knowledge/knowledge.mjs";
20
21
 
21
22
  /**
22
23
  * Pre-loaded asset map. Each value is the parsed JSON object, ready to use.
@@ -46,6 +47,9 @@ export const ASSETS = {
46
47
  wcagReference,
47
48
  manualChecks,
48
49
  },
50
+ knowledge: {
51
+ knowledge: knowledgeData,
52
+ },
49
53
  };
50
54
 
51
55
  /**
@@ -78,6 +82,9 @@ export const ASSET_PATHS = {
78
82
  wcagReference: "reporting.wcagReference",
79
83
  manualChecks: "reporting.manualChecks",
80
84
  },
85
+ knowledge: {
86
+ knowledge: "knowledge.knowledge",
87
+ },
81
88
  };
82
89
 
83
90
  /**
@@ -832,7 +832,7 @@ function buildFindings(inputPayload, cliArgs) {
832
832
 
833
833
  findings.push({
834
834
  id: "",
835
- rule_id: v.id,
835
+ rule_id: v.source === "cdp" ? v.id.replace(/^cdp-/, "") : v.id,
836
836
  source_rule_id: v.source_rule_id || null,
837
837
  source: v.source || "axe",
838
838
  title: v.help,
package/src/index.d.mts CHANGED
@@ -209,6 +209,186 @@ export interface SourcePatternOptions {
209
209
  onlyPattern?: string;
210
210
  }
211
211
 
212
+ // ---------------------------------------------------------------------------
213
+ // Knowledge APIs
214
+ // ---------------------------------------------------------------------------
215
+
216
+ export interface ScannerEngineHelp {
217
+ id: "axe" | "cdp" | "pa11y" | string;
218
+ label: string;
219
+ description: string;
220
+ coverage: string;
221
+ speed: "Fast" | "Medium" | "Slow" | string;
222
+ defaultEnabled: boolean;
223
+ }
224
+
225
+ export interface EnumOptionValue {
226
+ value: string;
227
+ label: string;
228
+ description?: string;
229
+ }
230
+
231
+ export interface ScannerOptionHelp {
232
+ id: string;
233
+ label: string;
234
+ description: string;
235
+ defaultValue: unknown;
236
+ type: string;
237
+ allowedValues?: unknown[] | EnumOptionValue[];
238
+ }
239
+
240
+ export interface ConformanceLevel {
241
+ id: "A" | "AA" | "AAA";
242
+ label: string;
243
+ badge: string;
244
+ description: string;
245
+ shortDescription: string;
246
+ hint: string;
247
+ tags: string[];
248
+ }
249
+
250
+ export interface WcagPrinciple {
251
+ id: string;
252
+ name: string;
253
+ description: string;
254
+ criterionPrefix: string;
255
+ number: number;
256
+ }
257
+
258
+ export interface SeverityLevel {
259
+ id: "Critical" | "Serious" | "Moderate" | "Minor";
260
+ label: string;
261
+ shortDescription: string;
262
+ description: string;
263
+ order: number;
264
+ }
265
+
266
+ export interface OutputFormatInfo {
267
+ title: string;
268
+ description: string;
269
+ detail: string;
270
+ }
271
+
272
+ export interface OutputsInfo {
273
+ pdf: OutputFormatInfo;
274
+ checklist: OutputFormatInfo;
275
+ json: OutputFormatInfo;
276
+ remediation: OutputFormatInfo;
277
+ }
278
+
279
+ export interface ConformanceLevelsResult {
280
+ locale: string;
281
+ version: string;
282
+ conformanceLevels: ConformanceLevel[];
283
+ }
284
+
285
+ export interface WcagPrinciplesResult {
286
+ locale: string;
287
+ version: string;
288
+ wcagPrinciples: WcagPrinciple[];
289
+ }
290
+
291
+ export interface SeverityLevelsResult {
292
+ locale: string;
293
+ version: string;
294
+ severityLevels: SeverityLevel[];
295
+ }
296
+
297
+ export interface OutputsInfoResult {
298
+ locale: string;
299
+ version: string;
300
+ outputs: OutputsInfo;
301
+ }
302
+
303
+ export interface ScannerHelp {
304
+ locale: string;
305
+ version: string;
306
+ title: string;
307
+ engines: ScannerEngineHelp[];
308
+ options: ScannerOptionHelp[];
309
+ }
310
+
311
+ export interface PersonaReferenceItem {
312
+ id: string;
313
+ icon: string;
314
+ label: string;
315
+ description: string;
316
+ keywords: string[];
317
+ mappedRules: string[];
318
+ }
319
+
320
+ export interface PersonaReference {
321
+ locale: string;
322
+ version: string;
323
+ personas: PersonaReferenceItem[];
324
+ }
325
+
326
+ export interface ConceptEntry {
327
+ title: string;
328
+ body: string;
329
+ context?: string;
330
+ }
331
+
332
+ export interface GlossaryEntry {
333
+ term: string;
334
+ definition: string;
335
+ }
336
+
337
+ export interface DocArticle {
338
+ id: string;
339
+ title: string;
340
+ icon?: string;
341
+ badge?: string;
342
+ summary: string;
343
+ body: string;
344
+ }
345
+
346
+ export interface DocGroup {
347
+ id: string;
348
+ label: string;
349
+ articles: DocArticle[];
350
+ }
351
+
352
+ export interface DocSection {
353
+ id: string;
354
+ heading: string;
355
+ articles?: DocArticle[];
356
+ groups?: DocGroup[];
357
+ }
358
+
359
+ export interface KnowledgeDocs {
360
+ sections: DocSection[];
361
+ }
362
+
363
+ export interface UiHelp {
364
+ locale: string;
365
+ version: string;
366
+ concepts: Record<string, ConceptEntry>;
367
+ glossary: GlossaryEntry[];
368
+ }
369
+
370
+ export interface EngineKnowledge {
371
+ locale: string;
372
+ version: string;
373
+ scanner: {
374
+ title: string;
375
+ engines: ScannerEngineHelp[];
376
+ options: ScannerOptionHelp[];
377
+ };
378
+ personas: PersonaReferenceItem[];
379
+ concepts: Record<string, ConceptEntry>;
380
+ glossary: GlossaryEntry[];
381
+ docs: KnowledgeDocs;
382
+ conformanceLevels: ConformanceLevel[];
383
+ wcagPrinciples: WcagPrinciple[];
384
+ severityLevels: SeverityLevel[];
385
+ outputs: OutputsInfo;
386
+ }
387
+
388
+ export interface KnowledgeOptions {
389
+ locale?: string;
390
+ }
391
+
212
392
  // ---------------------------------------------------------------------------
213
393
  // Engine selection
214
394
  // ---------------------------------------------------------------------------
@@ -293,3 +473,19 @@ export function getSourcePatterns(
293
473
  projectDir: string,
294
474
  options?: SourcePatternOptions
295
475
  ): Promise<SourcePatternResult>;
476
+
477
+ export function getScannerHelp(options?: KnowledgeOptions): ScannerHelp;
478
+
479
+ export function getPersonaReference(options?: KnowledgeOptions): PersonaReference;
480
+
481
+ export function getUiHelp(options?: KnowledgeOptions): UiHelp;
482
+
483
+ export function getConformanceLevels(options?: KnowledgeOptions): ConformanceLevelsResult;
484
+
485
+ export function getWcagPrinciples(options?: KnowledgeOptions): WcagPrinciplesResult;
486
+
487
+ export function getSeverityLevels(options?: KnowledgeOptions): SeverityLevelsResult;
488
+
489
+ export function getOutputsInfo(options?: KnowledgeOptions): OutputsInfoResult;
490
+
491
+ export function getKnowledge(options?: KnowledgeOptions): EngineKnowledge;
package/src/index.mjs CHANGED
@@ -14,6 +14,7 @@ let _intelligence = null;
14
14
  let _pa11yConfig = null;
15
15
  let _complianceConfig = null;
16
16
  let _wcagReference = null;
17
+ let _knowledge = null;
17
18
 
18
19
  function getIntelligence() {
19
20
  if (!_intelligence) _intelligence = loadAssetJson(ASSET_PATHS.remediation.intelligence, "intelligence.json");
@@ -41,6 +42,22 @@ function getWcagReference() {
41
42
  return _wcagReference;
42
43
  }
43
44
 
45
+ function getKnowledgeData() {
46
+ if (!_knowledge) _knowledge = loadAssetJson(ASSET_PATHS.knowledge.knowledge, "knowledge.json");
47
+ return _knowledge;
48
+ }
49
+
50
+ function clone(value) {
51
+ return JSON.parse(JSON.stringify(value));
52
+ }
53
+
54
+ function resolveKnowledgeLocale(locale = "en") {
55
+ const payload = getKnowledgeData();
56
+ const locales = payload.locales || {};
57
+ if (locale && locales[locale]) return locale;
58
+ return "en";
59
+ }
60
+
44
61
  // ---------------------------------------------------------------------------
45
62
  // Pa11y rule canonicalization (internal)
46
63
  // ---------------------------------------------------------------------------
@@ -421,6 +438,194 @@ export function getOverview(findings, payload = null) {
421
438
  };
422
439
  }
423
440
 
441
+ // ---------------------------------------------------------------------------
442
+ // Knowledge APIs
443
+ // ---------------------------------------------------------------------------
444
+
445
+ /**
446
+ * Returns scanner-facing help metadata including engine descriptions,
447
+ * advanced option hints, and defaults.
448
+ *
449
+ * @param {{ locale?: string }} [options={}]
450
+ * @returns {{ locale: string, version: string, title: string, engines: object[], options: object[] }}
451
+ */
452
+ export function getScannerHelp(options = {}) {
453
+ const locale = resolveKnowledgeLocale(options.locale || "en");
454
+ const payload = getKnowledgeData();
455
+ const scanner = payload.locales[locale]?.scanner || { title: "Scanner Help", engines: [], options: [] };
456
+
457
+ return {
458
+ locale,
459
+ version: payload.version || "1.0.0",
460
+ title: scanner.title,
461
+ engines: clone(scanner.engines || []),
462
+ options: clone(scanner.options || []),
463
+ };
464
+ }
465
+
466
+ /**
467
+ * Returns persona explanations with labels, descriptions, and the WCAG rule/
468
+ * keyword mappings used for impact grouping.
469
+ *
470
+ * @param {{ locale?: string }} [options={}]
471
+ * @returns {{ locale: string, version: string, personas: object[] }}
472
+ */
473
+ export function getPersonaReference(options = {}) {
474
+ const locale = resolveKnowledgeLocale(options.locale || "en");
475
+ const payload = getKnowledgeData();
476
+ const wcagRef = getWcagReference();
477
+
478
+ const copyMap = payload.locales[locale]?.personas || {};
479
+ const personaConfig = wcagRef.personaConfig || {};
480
+ const personaMapping = wcagRef.personaMapping || {};
481
+
482
+ const personas = Object.keys(copyMap).map((id) => {
483
+ const copy = copyMap[id] || {};
484
+ const config = personaConfig[id] || {};
485
+ const mapping = personaMapping[id] || {};
486
+
487
+ return {
488
+ id,
489
+ icon: id,
490
+ label: copy.label || config.label || id,
491
+ description: copy.description || "",
492
+ keywords: Array.isArray(mapping.keywords) ? clone(mapping.keywords) : [],
493
+ mappedRules: Array.isArray(mapping.rules) ? clone(mapping.rules) : [],
494
+ };
495
+ });
496
+
497
+ return {
498
+ locale,
499
+ version: payload.version || "1.0.0",
500
+ personas,
501
+ };
502
+ }
503
+
504
+ /**
505
+ * Returns UI tooltip copy and glossary terms for scanner cards and labels.
506
+ *
507
+ * @param {{ locale?: string }} [options={}]
508
+ * @returns {{ locale: string, version: string, tooltips: Record<string, object>, glossary: object[] }}
509
+ */
510
+ export function getUiHelp(options = {}) {
511
+ const locale = resolveKnowledgeLocale(options.locale || "en");
512
+ const payload = getKnowledgeData();
513
+ const localePayload = payload.locales[locale] || {};
514
+
515
+ return {
516
+ locale,
517
+ version: payload.version || "1.0.0",
518
+ concepts: clone(localePayload.concepts || {}),
519
+ glossary: clone(localePayload.glossary || []),
520
+ };
521
+ }
522
+
523
+ /**
524
+ * Returns the full documentation package that frontends or agents can render
525
+ * as help content next to findings and scores.
526
+ *
527
+ * @param {{ locale?: string }} [options={}]
528
+ * @returns {{ locale: string, version: string, scanner: object, personas: object[], tooltips: Record<string, object>, glossary: object[] }}
529
+ */
530
+ /**
531
+ * Returns conformance level definitions with WCAG axe-core tag mappings.
532
+ *
533
+ * @param {{ locale?: string }} [options={}]
534
+ * @returns {{ locale: string, version: string, conformanceLevels: object[] }}
535
+ */
536
+ export function getConformanceLevels(options = {}) {
537
+ const locale = resolveKnowledgeLocale(options.locale || "en");
538
+ const payload = getKnowledgeData();
539
+ const levels = payload.locales[locale]?.conformanceLevels || [];
540
+ return {
541
+ locale,
542
+ version: payload.version || "1.0.0",
543
+ conformanceLevels: clone(levels),
544
+ };
545
+ }
546
+
547
+ /**
548
+ * Returns the four WCAG principles with their criterion prefix patterns.
549
+ *
550
+ * @param {{ locale?: string }} [options={}]
551
+ * @returns {{ locale: string, version: string, wcagPrinciples: object[] }}
552
+ */
553
+ export function getWcagPrinciples(options = {}) {
554
+ const locale = resolveKnowledgeLocale(options.locale || "en");
555
+ const payload = getKnowledgeData();
556
+ const principles = payload.locales[locale]?.wcagPrinciples || [];
557
+ return {
558
+ locale,
559
+ version: payload.version || "1.0.0",
560
+ wcagPrinciples: clone(principles),
561
+ };
562
+ }
563
+
564
+ /**
565
+ * Returns severity level definitions with labels, descriptions, and ordering.
566
+ *
567
+ * @param {{ locale?: string }} [options={}]
568
+ * @returns {{ locale: string, version: string, severityLevels: object[] }}
569
+ */
570
+ export function getSeverityLevels(options = {}) {
571
+ const locale = resolveKnowledgeLocale(options.locale || "en");
572
+ const payload = getKnowledgeData();
573
+ const levels = payload.locales[locale]?.severityLevels || [];
574
+ return {
575
+ locale,
576
+ version: payload.version || "1.0.0",
577
+ severityLevels: clone(levels),
578
+ };
579
+ }
580
+
581
+ /**
582
+ * Returns output format descriptions for each report type the engine produces.
583
+ *
584
+ * @param {{ locale?: string }} [options={}]
585
+ * @returns {{ locale: string, version: string, outputs: object }}
586
+ */
587
+ export function getOutputsInfo(options = {}) {
588
+ const locale = resolveKnowledgeLocale(options.locale || "en");
589
+ const payload = getKnowledgeData();
590
+ const outputs = payload.locales[locale]?.outputs || {};
591
+ return {
592
+ locale,
593
+ version: payload.version || "1.0.0",
594
+ outputs: clone(outputs),
595
+ };
596
+ }
597
+
598
+ export function getKnowledge(options = {}) {
599
+ const scanner = getScannerHelp(options);
600
+ const personas = getPersonaReference(options);
601
+ const ui = getUiHelp(options);
602
+ const conformance = getConformanceLevels(options);
603
+ const principles = getWcagPrinciples(options);
604
+ const severity = getSeverityLevels(options);
605
+ const outputsInfo = getOutputsInfo(options);
606
+
607
+ const payload = getKnowledgeData();
608
+ const docs = clone(payload.locales[scanner.locale]?.docs ?? { sections: [] });
609
+
610
+ return {
611
+ locale: scanner.locale,
612
+ version: scanner.version,
613
+ scanner: {
614
+ title: scanner.title,
615
+ engines: scanner.engines,
616
+ options: scanner.options,
617
+ },
618
+ personas: personas.personas,
619
+ concepts: ui.concepts,
620
+ glossary: ui.glossary,
621
+ docs,
622
+ conformanceLevels: conformance.conformanceLevels,
623
+ wcagPrinciples: principles.wcagPrinciples,
624
+ severityLevels: severity.severityLevels,
625
+ outputs: outputsInfo.outputs,
626
+ };
627
+ }
628
+
424
629
  // ---------------------------------------------------------------------------
425
630
  // Full audit pipeline
426
631
  // ---------------------------------------------------------------------------
@@ -285,7 +285,7 @@ function buildHtml(args, findings, metadata = {}) {
285
285
  </div>
286
286
  </div>
287
287
  <h3 class="text-xl font-bold text-slate-900 mb-1">${label} Compliance</h3>
288
- <p class="text-xs font-medium text-slate-500 max-w-[200px] leading-snug">Automated testing coverage based on ${escapeHtml(args.target)} technical checks.</p>
288
+ <p class="text-xs font-medium text-slate-500 max-w-50 leading-snug">Automated testing coverage based on ${escapeHtml(args.target)} technical checks.</p>
289
289
  </div>
290
290
 
291
291
  <div class="md:col-span-7 grid grid-cols-2 gap-4">
@@ -334,13 +334,13 @@ function buildHtml(args, findings, metadata = {}) {
334
334
  <div class="group">
335
335
  <div class="flex items-center justify-between mb-2">
336
336
  <div class="flex items-center gap-3">
337
- <div class="w-8 h-8 rounded-lg bg-slate-50 flex items-center justify-center text-slate-600 group-hover:bg-[var(--primary-light)] group-hover:text-[var(--primary)] transition-colors">${p.icon}</div>
337
+ <div class="w-8 h-8 rounded-lg bg-slate-50 flex items-center justify-center text-slate-600 group-hover:bg-(--primary-light) group-hover:text-(--primary) transition-colors">${p.icon}</div>
338
338
  <span class="text-sm font-bold text-slate-700">${p.label}</span>
339
339
  </div>
340
340
  <span class="text-xs font-black text-slate-900">${p.count} issues</span>
341
341
  </div>
342
342
  <div class="w-full bg-slate-100 rounded-full h-1.5 overflow-hidden">
343
- <div class="bg-[var(--primary)] h-full rounded-full transition-all duration-500" style="width: ${findings.length > 0 ? (p.count / findings.length) * 100 : 0}%"></div>
343
+ <div class="bg-(--primary) h-full rounded-full transition-all duration-500" style="width: ${findings.length > 0 ? (p.count / findings.length) * 100 : 0}%"></div>
344
344
  </div>
345
345
  </div>`,
346
346
  )
@@ -355,14 +355,14 @@ function buildHtml(args, findings, metadata = {}) {
355
355
  ? `
356
356
  <div class="premium-card rounded-2xl bg-slate-900 p-6 mb-12 relative overflow-hidden">
357
357
  <div class="absolute -right-4 -bottom-4 opacity-10">
358
- <svg class="w-32 h-32 text-[var(--primary)]" fill="currentColor" viewBox="0 0 24 24"><path d="M13 10V3L4 14h7v7l9-11h-7z"/></svg>
358
+ <svg class="w-32 h-32 text-(--primary)" fill="currentColor" viewBox="0 0 24 24"><path d="M13 10V3L4 14h7v7l9-11h-7z"/></svg>
359
359
  </div>
360
360
  <div class="relative z-10">
361
361
  <div class="flex items-center gap-3 mb-4">
362
- <span class="px-2 py-0.5 rounded bg-[var(--primary)] text-[10px] font-black text-white uppercase tracking-tighter">AI Analysis</span>
362
+ <span class="px-2 py-0.5 rounded bg-(--primary) text-[10px] font-black text-white uppercase tracking-tighter">AI Analysis</span>
363
363
  <h3 class="text-xl font-bold text-white">Recommended Quick Wins</h3>
364
364
  </div>
365
- <p class="text-xs text-[var(--primary)]/80 mb-6 -mt-2 leading-relaxed italic">High-priority issues with ready-to-use code fixes for immediate remediation.</p>
365
+ <p class="text-xs text-(--primary)/80 mb-6 -mt-2 leading-relaxed italic">High-priority issues with ready-to-use code fixes for immediate remediation.</p>
366
366
  <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
367
367
  ${quickWins
368
368
  .map(
@@ -393,7 +393,7 @@ function buildHtml(args, findings, metadata = {}) {
393
393
  <div class="flex items-center gap-4">
394
394
  <h3 class="text-xl font-extrabold text-slate-900 tracking-tight">Findings <span class="text-slate-600 font-bold ml-1">${findings.length}</span></h3>
395
395
  <div class="flex gap-1 bg-white border border-slate-200 rounded-xl p-1 shadow-sm">
396
- <button onclick="setView('severity')" id="view-severity" class="view-btn px-4 py-1.5 rounded-lg text-xs font-bold uppercase tracking-widest bg-[var(--primary-light)] text-[var(--primary)] transition-all">By Severity</button>
396
+ <button onclick="setView('severity')" id="view-severity" class="view-btn px-4 py-1.5 rounded-lg text-xs font-bold uppercase tracking-widest bg-(--primary-light) text-(--primary) transition-all">By Severity</button>
397
397
  ${
398
398
  Object.keys(_pageCounts).length > 1
399
399
  ? `<button onclick="setView('page')" id="view-page" class="view-btn px-4 py-1.5 rounded-lg text-xs font-bold uppercase tracking-widest text-slate-500 hover:text-slate-700 transition-all">By Page</button>`
@@ -407,7 +407,7 @@ function buildHtml(args, findings, metadata = {}) {
407
407
  <!-- Row 2: Search & Filter Select -->
408
408
  <div class="flex items-center gap-4 w-full">
409
409
  <div class="relative flex-1">
410
- <input type="text" id="search-input" oninput="handleSearch(this.value)" placeholder="Search violations..." class="w-full pl-11 pr-4 py-3 bg-white border border-slate-200 rounded-2xl text-sm font-medium focus:outline-none focus:ring-4 focus:ring-[var(--primary)]/10 focus:border-[var(--primary)] transition-all shadow-sm">
410
+ <input type="text" id="search-input" oninput="handleSearch(this.value)" placeholder="Search violations..." class="w-full pl-11 pr-4 py-3 bg-white border border-slate-200 rounded-2xl text-sm font-medium focus:outline-none focus:ring-4 focus:ring-(--primary)/10 focus:border-(--primary) transition-all shadow-sm">
411
411
  <svg class="absolute left-4 top-3.5 w-5 h-5 text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
412
412
  </div>
413
413
 
@@ -444,7 +444,7 @@ function buildHtml(args, findings, metadata = {}) {
444
444
  </div>
445
445
 
446
446
  <footer class="mt-10 py-6 border-t border-slate-200 text-center">
447
- <p class="text-slate-600 text-sm font-medium">Generated by <a href="https://github.com/diegovelasquezweb/a11y" target="_blank" class="text-slate-700 hover:text-[var(--primary)] font-semibold transition-colors">a11y</a> &bull; <span class="text-slate-700">${escapeHtml(args.target)}</span></p>
447
+ <p class="text-slate-600 text-sm font-medium">Generated by <a href="https://github.com/diegovelasquezweb/a11y" target="_blank" class="text-slate-700 hover:text-(--primary) font-semibold transition-colors">a11y</a> &bull; <span class="text-slate-700">${escapeHtml(args.target)}</span></p>
448
448
  </footer>
449
449
  </main>
450
450