@roopesh.yadava/qa-pack 1.0.3

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.
@@ -0,0 +1,478 @@
1
+ # WCAG 2.1 Check Scripts, Templates & Reference
2
+
3
+ ---
4
+
5
+ ## axe-Core Injection Script
6
+
7
+ Run via `browser_evaluate`. Injects axe-core from CDN. Returns `"loaded"` or `"already loaded"`.
8
+
9
+ ```javascript
10
+ (() => new Promise((resolve, reject) => {
11
+ if (window.axe) return resolve('already loaded');
12
+ const s = document.createElement('script');
13
+ s.src = 'https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.9.1/axe.min.js';
14
+ s.onload = () => resolve('loaded');
15
+ s.onerror = () => reject('CDN blocked — CSP may be preventing external scripts');
16
+ document.head.appendChild(s);
17
+ }))()
18
+ ```
19
+
20
+ ---
21
+
22
+ ## axe-Core Run Script
23
+
24
+ Run via `browser_evaluate` **after** injection succeeds.
25
+ Returns a compact JSON string of violations + incomplete count.
26
+
27
+ ```javascript
28
+ axe.run(document, {
29
+ runOnly: { type: 'tag', values: ['wcag2a', 'wcag2aa'] },
30
+ resultTypes: ['violations', 'incomplete']
31
+ }).then(results => JSON.stringify({
32
+ violations: results.violations.map(v => ({
33
+ id: v.id,
34
+ impact: v.impact,
35
+ description: v.description,
36
+ help: v.help,
37
+ helpUrl: v.helpUrl,
38
+ wcag: v.tags.filter(t => t.startsWith('wcag') || t.startsWith('best')),
39
+ nodeCount: v.nodes.length,
40
+ firstNode: v.nodes[0]?.html?.slice(0, 300) ?? null,
41
+ allNodes: v.nodes.slice(0, 5).map(n => n.html?.slice(0, 150))
42
+ })),
43
+ incomplete: results.incomplete.length,
44
+ passes: results.passes.length
45
+ }))
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Manual Check Scripts
51
+
52
+ ### Check A — Keyboard Traversal (WCAG 2.1.1)
53
+
54
+ ```javascript
55
+ (() => {
56
+ const focusable = [...document.querySelectorAll(
57
+ 'a[href], button:not([disabled]), input:not([disabled]), ' +
58
+ 'select:not([disabled]), textarea:not([disabled]), ' +
59
+ '[tabindex]:not([tabindex="-1"]), details > summary, [contenteditable]'
60
+ )];
61
+ const negativeTab = [...document.querySelectorAll('[tabindex="-1"]')];
62
+ return {
63
+ focusableCount: focusable.length,
64
+ removedFromTab: negativeTab.length,
65
+ firstTen: focusable.slice(0, 10).map(el => ({
66
+ tag: el.tagName.toLowerCase(),
67
+ text: (el.innerText || el.value || el.getAttribute('aria-label') || el.getAttribute('title') || '').trim().slice(0, 60),
68
+ tabindex: el.tabIndex,
69
+ type: el.type || null
70
+ }))
71
+ };
72
+ })()
73
+ ```
74
+
75
+ ---
76
+
77
+ ### Check B — Focus Visibility (WCAG 2.4.7)
78
+
79
+ ```javascript
80
+ (() => {
81
+ const allRules = [];
82
+ try {
83
+ [...document.styleSheets].forEach(sheet => {
84
+ try {
85
+ [...sheet.cssRules].forEach(rule => allRules.push(rule));
86
+ } catch (e) {}
87
+ });
88
+ } catch (e) {}
89
+
90
+ const focusRules = allRules.filter(r => r.selectorText?.includes(':focus'));
91
+ const outlineNoneRules = focusRules.filter(r =>
92
+ r.style?.outline === 'none' || r.style?.outline === '0' ||
93
+ r.style?.outlineWidth === '0px'
94
+ );
95
+
96
+ return {
97
+ totalFocusRules: focusRules.length,
98
+ outlineNoneCount: outlineNoneRules.length,
99
+ hasFocusVisible: allRules.some(r => r.selectorText?.includes(':focus-visible')),
100
+ outlineNoneSelectors: outlineNoneRules.slice(0, 5).map(r => r.selectorText),
101
+ sampleFocusRules: focusRules.slice(0, 5).map(r => ({
102
+ selector: r.selectorText,
103
+ outline: r.style?.outline,
104
+ boxShadow: r.style?.boxShadow
105
+ }))
106
+ };
107
+ })()
108
+ ```
109
+
110
+ ---
111
+
112
+ ### Check C — Skip Navigation Link (WCAG 2.4.1)
113
+
114
+ ```javascript
115
+ (() => {
116
+ const candidates = [...document.querySelectorAll('a')].filter(a =>
117
+ /skip|jump|main|content/i.test(a.textContent + a.getAttribute('href') + (a.className || ''))
118
+ );
119
+ const firstLink = document.querySelector('body > :first-child a, header a:first-of-type');
120
+ return {
121
+ hasSkipLink: candidates.length > 0,
122
+ skipLinks: candidates.slice(0, 3).map(a => ({
123
+ text: a.innerText.trim().slice(0, 60),
124
+ href: a.getAttribute('href')
125
+ })),
126
+ firstBodyLink: firstLink ? {
127
+ text: firstLink.innerText.trim().slice(0, 60),
128
+ href: firstLink.getAttribute('href')
129
+ } : null
130
+ };
131
+ })()
132
+ ```
133
+
134
+ ---
135
+
136
+ ### Check D — Heading Hierarchy (WCAG 1.3.1)
137
+
138
+ ```javascript
139
+ (() => {
140
+ const headings = [...document.querySelectorAll('h1,h2,h3,h4,h5,h6')];
141
+ const levels = headings.map(h => parseInt(h.tagName[1]));
142
+ const skips = [];
143
+ for (let i = 1; i < levels.length; i++) {
144
+ if (levels[i] - levels[i - 1] > 1) {
145
+ skips.push({ from: levels[i - 1], to: levels[i], text: headings[i].innerText.trim().slice(0, 60) });
146
+ }
147
+ }
148
+ return {
149
+ total: headings.length,
150
+ h1Count: levels.filter(l => l === 1).length,
151
+ hierarchy: headings.slice(0, 15).map((h, i) => ({
152
+ level: levels[i],
153
+ text: h.innerText.trim().slice(0, 80)
154
+ })),
155
+ levelSkips: skips,
156
+ issues: [
157
+ ...(levels.filter(l => l === 1).length === 0 ? ['Missing h1'] : []),
158
+ ...(levels.filter(l => l === 1).length > 1 ? ['Multiple h1s'] : []),
159
+ ...(skips.length > 0 ? ['Heading level skips detected'] : [])
160
+ ]
161
+ };
162
+ })()
163
+ ```
164
+
165
+ ---
166
+
167
+ ### Check E — Image Alt Text (WCAG 1.1.1) — cross-check
168
+
169
+ ```javascript
170
+ (() => {
171
+ const imgs = [...document.querySelectorAll('img')];
172
+ const svgs = [...document.querySelectorAll('svg')];
173
+ return {
174
+ imgTotal: imgs.length,
175
+ missingAlt: imgs.filter(i => !i.hasAttribute('alt')).length,
176
+ emptyAlt: imgs.filter(i => i.getAttribute('alt') === '').length,
177
+ genericAlt: imgs.filter(i => /^(image|photo|picture|icon|logo|banner)$/i.test(i.getAttribute('alt') || '')).length,
178
+ svgTotal: svgs.length,
179
+ svgMissingLabel: svgs.filter(s => !s.getAttribute('aria-label') && !s.querySelector('title')).length,
180
+ samples: imgs.filter(i => !i.hasAttribute('alt') || !i.getAttribute('alt')).slice(0, 5)
181
+ .map(i => ({ src: i.src.split('/').pop(), alt: i.getAttribute('alt') }))
182
+ };
183
+ })()
184
+ ```
185
+
186
+ ---
187
+
188
+ ## Severity Mapping
189
+
190
+ | axe-core impact | Jira Priority | WCAG Level | Action |
191
+ |-----------------|---------------|------------|--------|
192
+ | `critical` | P1 — Blocker | A | Create separate Jira Bug |
193
+ | `serious` | P2 — High | AA | Create separate Jira Bug |
194
+ | `moderate` | P3 — Medium | AA | Group into summary comment |
195
+ | `minor` | P4 — Low | AA | Group into summary comment |
196
+
197
+ ---
198
+
199
+ ## WCAG 2.1 Criterion Quick Reference
200
+
201
+ | Rule ID (axe) | WCAG Criterion | Level | Description |
202
+ |---------------|----------------|-------|-------------|
203
+ | `image-alt` | 1.1.1 | A | Images must have alt text |
204
+ | `color-contrast` | 1.4.3 | AA | 4.5:1 normal text / 3:1 large text |
205
+ | `label` | 1.3.1 / 3.3.2 | A | Inputs must have associated labels |
206
+ | `heading-order` | 1.3.1 | A | Headings must not skip levels |
207
+ | `html-has-lang` | 3.1.1 | A | `<html>` must have lang attribute |
208
+ | `link-name` | 2.4.4 | A | Links must have discernible text |
209
+ | `button-name` | 4.1.2 | A | Buttons must have accessible names |
210
+ | `aria-*` | 4.1.2 | A | ARIA attributes must be valid |
211
+ | `keyboard` | 2.1.1 | A | All functionality via keyboard |
212
+ | `focus-visible` | 2.4.7 | AA | Focus indicator must be visible |
213
+ | `bypass` | 2.4.1 | A | Skip link / bypass mechanism |
214
+ | `duplicate-id` | 4.1.1 | A | IDs must be unique |
215
+ | `form-field-multiple-labels` | 1.3.1 | A | One label per form field |
216
+
217
+ ---
218
+
219
+ ## Report Template
220
+
221
+ Use this structure when writing `outputs/a11y-report-[URL_SLUG]-[YYYYMMDD].html`.
222
+
223
+ Write a complete, self-contained HTML file. Replace all `[placeholders]` with real data.
224
+
225
+ ```html
226
+ <!DOCTYPE html>
227
+ <html lang="en">
228
+ <head>
229
+ <meta charset="UTF-8">
230
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
231
+ <title>Accessibility Report — [URL_SLUG] — [YYYYMMDD]</title>
232
+ <style>
233
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
234
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; font-size: 15px; line-height: 1.6; color: #1a1a2e; background: #f4f6fb; padding: 2rem; }
235
+ .container { max-width: 960px; margin: 0 auto; }
236
+ h1 { font-size: 1.8rem; color: #1a1a2e; margin-bottom: 0.25rem; }
237
+ h2 { font-size: 1.2rem; color: #2d3a6e; margin: 2rem 0 0.75rem; border-bottom: 2px solid #e0e4f0; padding-bottom: 0.4rem; }
238
+ h3 { font-size: 1rem; margin: 1.25rem 0 0.5rem; color: #1a1a2e; }
239
+ .meta { font-size: 0.85rem; color: #555; margin-bottom: 1.5rem; }
240
+ .meta span { margin-right: 1.5rem; }
241
+ .badge { display: inline-block; padding: 0.2rem 0.65rem; border-radius: 4px; font-size: 0.78rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; }
242
+ .badge-critical { background: #fde8e8; color: #c0392b; }
243
+ .badge-serious { background: #fef3e2; color: #d35400; }
244
+ .badge-moderate { background: #fffbe6; color: #b8860b; }
245
+ .badge-minor { background: #e8f5e9; color: #2e7d32; }
246
+ .badge-pass { background: #e8f5e9; color: #2e7d32; }
247
+ .badge-fail { background: #fde8e8; color: #c0392b; }
248
+ .badge-review { background: #fffbe6; color: #b8860b; }
249
+ .summary-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1rem; margin: 1rem 0 1.5rem; }
250
+ .summary-card { background: #fff; border-radius: 8px; padding: 1rem; text-align: center; box-shadow: 0 1px 4px rgba(0,0,0,.08); }
251
+ .summary-card .count { font-size: 2rem; font-weight: 700; }
252
+ .summary-card .label { font-size: 0.78rem; color: #777; text-transform: uppercase; }
253
+ .summary-card.critical .count { color: #c0392b; }
254
+ .summary-card.serious .count { color: #d35400; }
255
+ .summary-card.moderate .count { color: #b8860b; }
256
+ .summary-card.minor .count { color: #2e7d32; }
257
+ .status-bar { display: flex; align-items: center; gap: 0.75rem; font-weight: 600; font-size: 1rem; margin-bottom: 1.5rem; }
258
+ .card { background: #fff; border-radius: 8px; padding: 1.25rem 1.5rem; margin-bottom: 1rem; box-shadow: 0 1px 4px rgba(0,0,0,.08); border-left: 4px solid #ccc; }
259
+ .card.critical { border-color: #c0392b; }
260
+ .card.serious { border-color: #d35400; }
261
+ .card.moderate { border-color: #b8860b; }
262
+ .card.minor { border-color: #2e7d32; }
263
+ .card-header { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.75rem; }
264
+ .card-title { font-weight: 600; font-size: 0.95rem; }
265
+ .card dl { display: grid; grid-template-columns: 140px 1fr; gap: 0.3rem 1rem; font-size: 0.875rem; }
266
+ .card dt { font-weight: 600; color: #555; }
267
+ .card dd { color: #1a1a2e; }
268
+ pre { background: #1e1e2e; color: #cdd6f4; padding: 0.85rem 1rem; border-radius: 6px; font-size: 0.8rem; overflow-x: auto; margin: 0.5rem 0; white-space: pre-wrap; word-break: break-all; }
269
+ table { width: 100%; border-collapse: collapse; font-size: 0.875rem; margin: 0.75rem 0; }
270
+ th { background: #eef0f8; text-align: left; padding: 0.55rem 0.75rem; font-weight: 600; color: #333; }
271
+ td { padding: 0.55rem 0.75rem; border-bottom: 1px solid #e8eaf0; vertical-align: top; }
272
+ tr:last-child td { border-bottom: none; }
273
+ .check-pass { color: #2e7d32; font-weight: 600; }
274
+ .check-fail { color: #c0392b; font-weight: 600; }
275
+ .check-warn { color: #b8860b; font-weight: 600; }
276
+ .rec-list { list-style: none; counter-reset: rec; }
277
+ .rec-list li { counter-increment: rec; display: flex; gap: 0.75rem; padding: 0.6rem 0; border-bottom: 1px solid #e8eaf0; font-size: 0.875rem; }
278
+ .rec-list li::before { content: counter(rec); background: #2d3a6e; color: #fff; min-width: 1.5rem; height: 1.5rem; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 0.75rem; font-weight: 700; flex-shrink: 0; }
279
+ .rec-list li:last-child { border-bottom: none; }
280
+ .raw-summary { background: #fff; border-radius: 8px; padding: 1rem 1.5rem; font-size: 0.875rem; box-shadow: 0 1px 4px rgba(0,0,0,.08); display: flex; gap: 2rem; flex-wrap: wrap; }
281
+ .raw-summary span { color: #555; }
282
+ .raw-summary strong { color: #1a1a2e; }
283
+ a { color: #2d3a6e; }
284
+ .screenshot { margin: 1rem 0; }
285
+ .screenshot img { max-width: 100%; border-radius: 6px; border: 1px solid #dde; }
286
+ footer { margin-top: 2rem; font-size: 0.78rem; color: #aaa; text-align: center; }
287
+ </style>
288
+ </head>
289
+ <body>
290
+ <div class="container">
291
+
292
+ <h1>Accessibility Test Report — WCAG 2.1 AA</h1>
293
+ <div class="meta">
294
+ <span><strong>Page:</strong> <a href="[TARGET_URL]">[TARGET_URL]</a></span>
295
+ <span><strong>Tested:</strong> [YYYYMMDD HH:mm]</span>
296
+ <span><strong>Tester:</strong> Claude (accessibility-testing skill)</span>
297
+ <span><strong>WCAG Level:</strong> 2.1 AA</span>
298
+ </div>
299
+
300
+ <!-- ── Executive Summary ── -->
301
+ <h2>Executive Summary</h2>
302
+ <div class="summary-grid">
303
+ <div class="summary-card critical"><div class="count">[CRITICAL_COUNT]</div><div class="label">Critical</div></div>
304
+ <div class="summary-card serious"> <div class="count">[SERIOUS_COUNT]</div> <div class="label">Serious</div></div>
305
+ <div class="summary-card moderate"><div class="count">[MODERATE_COUNT]</div><div class="label">Moderate</div></div>
306
+ <div class="summary-card minor"> <div class="count">[MINOR_COUNT]</div> <div class="label">Minor</div></div>
307
+ </div>
308
+
309
+ <!-- Overall status: replace badge class with badge-pass / badge-fail / badge-review -->
310
+ <div class="status-bar">
311
+ Overall Status: <span class="badge badge-[pass|fail|review]">[PASS / FAIL / NEEDS REVIEW]</span>
312
+ </div>
313
+
314
+ <!-- ── Screenshot ── -->
315
+ <h2>Baseline Screenshot</h2>
316
+ <div class="screenshot">
317
+ <img src="[BASELINE_SCREENSHOT]" alt="Baseline screenshot of [TARGET_URL]">
318
+ </div>
319
+
320
+ <!-- ── Automated Violations ── -->
321
+ <h2>Automated Violations (axe-core)</h2>
322
+
323
+ <!-- Repeat the .card block below for each violation.
324
+ Set class to: card critical | card serious | card moderate | card minor -->
325
+
326
+ <!-- EXAMPLE — Critical violation card -->
327
+ <h3><span class="badge badge-critical">Critical</span></h3>
328
+ <!-- [FOR EACH critical violation:] -->
329
+ <div class="card critical">
330
+ <div class="card-header">
331
+ <span class="badge badge-critical">Critical</span>
332
+ <span class="card-title">[rule-id] — [description]</span>
333
+ </div>
334
+ <dl>
335
+ <dt>WCAG</dt> <dd>[criterion] — Level [A/AA]</dd>
336
+ <dt>Affected</dt> <dd>[N] element(s)</dd>
337
+ <dt>Fix</dt> <dd>[help text from axe]</dd>
338
+ <dt>Reference</dt> <dd><a href="[helpUrl]">[helpUrl]</a></dd>
339
+ </dl>
340
+ <p style="font-size:.8rem;margin-top:.6rem;font-weight:600;color:#555;">Example element:</p>
341
+ <pre>[firstNode HTML — escaped]</pre>
342
+ </div>
343
+ <!-- [END FOR EACH] -->
344
+
345
+ <h3><span class="badge badge-serious">Serious</span></h3>
346
+ <!-- [FOR EACH serious violation — same card structure with class "card serious"] -->
347
+
348
+ <h3><span class="badge badge-moderate">Moderate</span></h3>
349
+ <!-- Moderate + Minor can be a table instead of cards -->
350
+ <table>
351
+ <thead><tr><th>Rule</th><th>Description</th><th>Elements</th><th>WCAG</th></tr></thead>
352
+ <tbody>
353
+ <!-- <tr><td>[id]</td><td>[description]</td><td>[N]</td><td>[criterion]</td></tr> -->
354
+ </tbody>
355
+ </table>
356
+
357
+ <h3><span class="badge badge-minor">Minor</span></h3>
358
+ <table>
359
+ <thead><tr><th>Rule</th><th>Description</th><th>Elements</th><th>WCAG</th></tr></thead>
360
+ <tbody>
361
+ <!-- <tr><td>[id]</td><td>[description]</td><td>[N]</td><td>[criterion]</td></tr> -->
362
+ </tbody>
363
+ </table>
364
+
365
+ <!-- ── Manual Checks ── -->
366
+ <h2>Manual Checks</h2>
367
+ <table>
368
+ <thead><tr><th>Check</th><th>Criterion</th><th>Status</th><th>Detail</th></tr></thead>
369
+ <tbody>
370
+ <tr>
371
+ <td>Keyboard traversal</td><td>2.1.1</td>
372
+ <td class="check-[pass|fail]">[&#10003; Pass / &#10007; Fail]</td>
373
+ <td>[N] focusable elements[, issues if any]</td>
374
+ </tr>
375
+ <tr>
376
+ <td>Focus visibility</td><td>2.4.7</td>
377
+ <td class="check-[pass|fail]">[&#10003; Pass / &#10007; Fail]</td>
378
+ <td>[outline:none on N selectors] or [OK]</td>
379
+ </tr>
380
+ <tr>
381
+ <td>Skip link</td><td>2.4.1</td>
382
+ <td class="check-[pass|fail]">[&#10003; Pass / &#10007; Fail]</td>
383
+ <td>[Present: "Skip to main"] or [Missing]</td>
384
+ </tr>
385
+ <tr>
386
+ <td>Heading hierarchy</td><td>1.3.1</td>
387
+ <td class="check-[pass|fail]">[&#10003; Pass / &#10007; Fail]</td>
388
+ <td>[OK] or [issues list]</td>
389
+ </tr>
390
+ <tr>
391
+ <td>Image alt text</td><td>1.1.1</td>
392
+ <td class="check-[pass|fail|warn]">[&#10003; Pass / &#10007; Fail / &#9888; Review]</td>
393
+ <td>[N] total, [N] missing, [N] empty</td>
394
+ </tr>
395
+ </tbody>
396
+ </table>
397
+
398
+ <!-- ── Recommendations ── -->
399
+ <h2>Recommendations</h2>
400
+ <ol class="rec-list">
401
+ <li><strong>[Priority tag]</strong> [Fix description]</li>
402
+ <!-- add one <li> per recommendation -->
403
+ </ol>
404
+
405
+ <!-- ── axe-core Raw Summary ── -->
406
+ <h2>axe-core Raw Summary</h2>
407
+ <div class="raw-summary">
408
+ <span><strong>Violations:</strong> [N]</span>
409
+ <span><strong>Incomplete:</strong> [N]</span>
410
+ <span><strong>Passes:</strong> [N]</span>
411
+ <span><strong>Engine:</strong> axe-core 4.9.1</span>
412
+ <span><strong>Tags:</strong> wcag2a, wcag2aa</span>
413
+ </div>
414
+
415
+ <footer>Generated by Claude accessibility-testing skill &middot; WCAG 2.1 AA &middot; [YYYYMMDD]</footer>
416
+
417
+ </div>
418
+ </body>
419
+ </html>
420
+ ```
421
+
422
+ ---
423
+
424
+ ## Jira Comment Template (Moderate + Minor Summary)
425
+
426
+ Use with `addCommentToJiraIssue`:
427
+
428
+ ```
429
+ ♿ Accessibility Test — Moderate & Minor Findings
430
+ Page: [TARGET_URL]
431
+ WCAG Level: 2.1 AA | Date: [YYYYMMDD]
432
+
433
+ *Moderate Violations* (fix recommended)
434
+ || Rule || Description || Elements Affected || WCAG ||
435
+ | [id] | [description] | N | [criterion] |
436
+
437
+ *Minor Violations* (low priority)
438
+ || Rule || Description || Elements Affected || WCAG ||
439
+ | [id] | [description] | N | [criterion] |
440
+
441
+ Full report: outputs/a11y-report-[URL_SLUG]-[YYYYMMDD].html
442
+ ```
443
+
444
+ ---
445
+
446
+ ## Jira Bug Template (Critical / Serious)
447
+
448
+ Use with `createJiraIssue` for each critical/serious violation:
449
+
450
+ **Summary:** `[A11Y-CRITICAL] [WCAG 1.1.1] — Images missing alt text on /settings`
451
+
452
+ **Description:**
453
+ ```
454
+ *Accessibility Violation*
455
+ Rule: [axe rule ID]
456
+ WCAG: [criterion] — Level [A/AA]
457
+ Impact: Critical / Serious
458
+ Page: [TARGET_URL]
459
+
460
+ *Steps to Reproduce*
461
+ 1. Navigate to [TARGET_URL]
462
+ 2. Inspect [N] element(s) matching: [firstNode HTML snippet]
463
+
464
+ *Expected*
465
+ [Expected accessible behaviour per WCAG]
466
+
467
+ *Actual*
468
+ [What axe-core / manual check found]
469
+
470
+ *Affected Elements* ([N] total)
471
+ {code}[allNodes list]{code}
472
+
473
+ *Reference*
474
+ [axe helpUrl]
475
+ ```
476
+
477
+ **Issue Type:** Bug
478
+ **Labels:** accessibility, wcag-2.1, [a11y-critical / a11y-serious]