@accesslint/core 0.3.12 → 0.3.13

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/dist/index.js CHANGED
@@ -1,15 +1,15 @@
1
- let B = /* @__PURE__ */ new WeakMap();
2
- function we() {
3
- B = /* @__PURE__ */ new WeakMap();
1
+ let W = /* @__PURE__ */ new WeakMap();
2
+ function Se() {
3
+ W = /* @__PURE__ */ new WeakMap();
4
4
  }
5
- function de(t) {
5
+ function he(e) {
6
6
  var i;
7
- const a = t.tagName.toLowerCase(), e = (i = t.getAttribute("type")) == null ? void 0 : i.toLowerCase();
7
+ const a = e.tagName.toLowerCase(), t = (i = e.getAttribute("type")) == null ? void 0 : i.toLowerCase();
8
8
  switch (a) {
9
9
  case "a":
10
- return t.hasAttribute("href") ? "link" : null;
10
+ return e.hasAttribute("href") ? "link" : null;
11
11
  case "area":
12
- return t.hasAttribute("href") ? "link" : null;
12
+ return e.hasAttribute("href") ? "link" : null;
13
13
  case "article":
14
14
  return "article";
15
15
  case "aside":
@@ -27,7 +27,7 @@ function de(t) {
27
27
  case "figure":
28
28
  return "figure";
29
29
  case "footer":
30
- return t.closest("article, aside, main, nav, section") ? null : "contentinfo";
30
+ return e.closest("article, aside, main, nav, section") ? null : "contentinfo";
31
31
  case "form":
32
32
  return "form";
33
33
  case "h1":
@@ -38,13 +38,13 @@ function de(t) {
38
38
  case "h6":
39
39
  return "heading";
40
40
  case "header":
41
- return t.closest("article, aside, main, nav, section") ? null : "banner";
41
+ return e.closest("article, aside, main, nav, section") ? null : "banner";
42
42
  case "hr":
43
43
  return "separator";
44
44
  case "img":
45
- return t.getAttribute("alt") === "" ? "presentation" : "img";
45
+ return e.getAttribute("alt") === "" ? "presentation" : "img";
46
46
  case "input":
47
- switch (e) {
47
+ switch (t) {
48
48
  case "button":
49
49
  case "image":
50
50
  case "reset":
@@ -71,7 +71,7 @@ function de(t) {
71
71
  return "textbox";
72
72
  }
73
73
  case "li":
74
- return t.closest("ul, ol, menu") ? "listitem" : null;
74
+ return e.closest("ul, ol, menu") ? "listitem" : null;
75
75
  case "main":
76
76
  return "main";
77
77
  case "math":
@@ -94,9 +94,9 @@ function de(t) {
94
94
  case "progress":
95
95
  return "progressbar";
96
96
  case "section":
97
- return t.hasAttribute("aria-label") || t.hasAttribute("aria-labelledby") ? "region" : null;
97
+ return e.hasAttribute("aria-label") || e.hasAttribute("aria-labelledby") ? "region" : null;
98
98
  case "select":
99
- return t.hasAttribute("multiple") || t.size > 1 ? "listbox" : "combobox";
99
+ return e.hasAttribute("multiple") || e.size > 1 ? "listbox" : "combobox";
100
100
  case "summary":
101
101
  return "button";
102
102
  case "table":
@@ -117,71 +117,71 @@ function de(t) {
117
117
  return null;
118
118
  }
119
119
  }
120
- function q(t) {
120
+ function q(e) {
121
121
  var n;
122
- const a = B.get(t);
122
+ const a = W.get(e);
123
123
  if (a !== void 0) return a;
124
- const i = ((n = t.getAttribute("role")) == null ? void 0 : n.trim().toLowerCase()) || null || de(t);
125
- return B.set(t, i), i;
124
+ const i = ((n = e.getAttribute("role")) == null ? void 0 : n.trim().toLowerCase()) || null || he(e);
125
+ return W.set(e, i), i;
126
126
  }
127
- let F = /* @__PURE__ */ new WeakMap();
128
- function Ae() {
129
- F = /* @__PURE__ */ new WeakMap();
127
+ let O = /* @__PURE__ */ new WeakMap();
128
+ function ke() {
129
+ O = /* @__PURE__ */ new WeakMap();
130
130
  }
131
- function v(t) {
132
- const a = F.get(t);
131
+ function v(e) {
132
+ const a = O.get(e);
133
133
  if (a !== void 0) return a;
134
- const e = xe(t);
135
- return F.set(t, e), e;
134
+ const t = Ie(e);
135
+ return O.set(e, t), t;
136
136
  }
137
- function xe(t) {
138
- var r, o, s, l, h;
139
- const a = t.getAttribute("aria-labelledby");
137
+ function Ie(e) {
138
+ var r, o, s, l, p;
139
+ const a = e.getAttribute("aria-labelledby");
140
140
  if (a) {
141
141
  const c = a.split(/\s+/).map((u) => {
142
- const p = t.ownerDocument.getElementById(u);
143
- return p ? A(p).trim() : "";
142
+ const h = e.ownerDocument.getElementById(u);
143
+ return h ? A(h).trim() : "";
144
144
  }).filter(Boolean);
145
145
  if (c.length) return c.join(" ");
146
146
  }
147
- const e = (r = t.getAttribute("aria-label")) == null ? void 0 : r.trim();
148
- if (e) return e;
149
- if (t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement || t instanceof HTMLSelectElement) {
150
- if (t.id) {
151
- const p = t.ownerDocument.querySelector(`label[for="${CSS.escape(t.id)}"]`), b = p ? A(p).trim() : "";
147
+ const t = (r = e.getAttribute("aria-label")) == null ? void 0 : r.trim();
148
+ if (t) return t;
149
+ if (e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement || e instanceof HTMLSelectElement) {
150
+ if (e.id) {
151
+ const h = e.ownerDocument.querySelector(`label[for="${CSS.escape(e.id)}"]`), b = h ? A(h).trim() : "";
152
152
  if (b) return b;
153
153
  }
154
- const c = t.closest("label"), u = c ? A(c).trim() : "";
154
+ const c = e.closest("label"), u = c ? A(c).trim() : "";
155
155
  if (u) return u;
156
156
  }
157
- const i = (o = t.getAttribute("title")) == null ? void 0 : o.trim();
157
+ const i = (o = e.getAttribute("title")) == null ? void 0 : o.trim();
158
158
  if (i) return i;
159
- if (t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement) {
160
- const c = (s = t.getAttribute("placeholder")) == null ? void 0 : s.trim();
159
+ if (e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement) {
160
+ const c = (s = e.getAttribute("placeholder")) == null ? void 0 : s.trim();
161
161
  if (c) return c;
162
162
  }
163
- const n = t.tagName.toLowerCase();
163
+ const n = e.tagName.toLowerCase();
164
164
  if (n === "fieldset") {
165
- const c = t.querySelector(":scope > legend");
165
+ const c = e.querySelector(":scope > legend");
166
166
  if (c) {
167
167
  const u = A(c).trim();
168
168
  if (u) return u;
169
169
  }
170
170
  }
171
171
  if (n === "table") {
172
- const c = t.querySelector(":scope > caption");
172
+ const c = e.querySelector(":scope > caption");
173
173
  if (c) {
174
174
  const u = A(c).trim();
175
175
  if (u) return u;
176
176
  }
177
177
  }
178
- if (!(t instanceof HTMLInputElement)) {
179
- const c = A(t).trim();
178
+ if (!(e instanceof HTMLInputElement)) {
179
+ const c = A(e).trim();
180
180
  if (c) return c;
181
181
  }
182
- return t instanceof HTMLImageElement || t instanceof HTMLAreaElement ? ((l = t.alt) == null ? void 0 : l.trim()) ?? "" : t instanceof HTMLInputElement && t.type === "image" ? ((h = t.alt) == null ? void 0 : h.trim()) ?? "" : "";
182
+ return e instanceof HTMLImageElement || e instanceof HTMLAreaElement ? ((l = e.alt) == null ? void 0 : l.trim()) ?? "" : e instanceof HTMLInputElement && e.type === "image" ? ((p = e.alt) == null ? void 0 : p.trim()) ?? "" : "";
183
183
  }
184
- const Se = /* @__PURE__ */ new Set([
184
+ const Te = /* @__PURE__ */ new Set([
185
185
  "alert",
186
186
  "alertdialog",
187
187
  "application",
@@ -265,51 +265,53 @@ const Se = /* @__PURE__ */ new Set([
265
265
  "treegrid",
266
266
  "treeitem"
267
267
  ]);
268
- function ke(t) {
269
- const a = t.trim().toLowerCase().replace(/[\u201C\u201D\u2018\u2019\u00AB\u00BB]/g, "");
270
- return Se.has(a);
268
+ function Ee(e) {
269
+ const a = e.trim().toLowerCase().replace(/[\u201C\u201D\u2018\u2019\u00AB\u00BB]/g, "");
270
+ return Te.has(a);
271
271
  }
272
- function T(t) {
273
- let a = t;
272
+ function T(e) {
273
+ let a = e;
274
274
  for (; a; ) {
275
- if (a.getAttribute("aria-hidden") === "true" || a instanceof HTMLElement && a.hidden) return !0;
276
- if (typeof getComputedStyle == "function") {
277
- const e = getComputedStyle(a);
278
- if (e.display === "none" || e.visibility === "hidden") return !0;
279
- } else if (a instanceof HTMLElement && a.style.display === "none") return !0;
275
+ if (pe(a)) return !0;
280
276
  a = a.parentElement;
281
277
  }
282
278
  return !1;
283
279
  }
284
- let W = /* @__PURE__ */ new WeakMap();
285
- function Ie() {
286
- W = /* @__PURE__ */ new WeakMap();
280
+ let B = /* @__PURE__ */ new WeakMap();
281
+ function Ce() {
282
+ B = /* @__PURE__ */ new WeakMap();
287
283
  }
288
- function g(t) {
289
- const a = W.get(t);
284
+ function g(e) {
285
+ const a = B.get(e);
290
286
  if (a !== void 0) return a;
291
- let e;
292
- return t.getAttribute("aria-hidden") === "true" || t instanceof HTMLElement && (t.hidden || t.style.display === "none") ? e = !0 : t.parentElement ? e = g(t.parentElement) : e = !1, W.set(t, e), e;
287
+ let t;
288
+ return e.getAttribute("aria-hidden") === "true" || e instanceof HTMLElement && (e.hidden || e.style.display === "none") ? t = !0 : e.parentElement ? t = g(e.parentElement) : t = !1, B.set(e, t), t;
293
289
  }
294
- function Te(t) {
295
- return !!(t.getAttribute("aria-hidden") === "true" || t instanceof HTMLElement && (t.hidden || t.style.display === "none"));
290
+ function pe(e) {
291
+ if (e.getAttribute("aria-hidden") === "true" || e instanceof HTMLElement && e.hidden) return !0;
292
+ if (typeof getComputedStyle == "function") {
293
+ const a = getComputedStyle(e);
294
+ if (a.display === "none" || a.visibility === "hidden") return !0;
295
+ } else if (e instanceof HTMLElement && e.style.display === "none")
296
+ return !0;
297
+ return !1;
296
298
  }
297
- function A(t) {
298
- var e, i, n, r, o;
299
+ function A(e) {
300
+ var t, i, n, r, o;
299
301
  let a = "";
300
- for (const s of t.childNodes)
302
+ for (const s of e.childNodes)
301
303
  if (s.nodeType === 3)
302
304
  a += s.textContent ?? "";
303
305
  else if (s.nodeType === 1) {
304
306
  const l = s;
305
- if (!Te(l)) {
306
- const h = (e = l.tagName) == null ? void 0 : e.toLowerCase();
307
- if (h === "img" || h === "area") {
307
+ if (!pe(l)) {
308
+ const p = (t = l.tagName) == null ? void 0 : t.toLowerCase();
309
+ if (p === "img" || p === "area") {
308
310
  const c = l.getAttribute("aria-labelledby");
309
311
  if (c) {
310
- const u = c.split(/\s+/).map((p) => {
312
+ const u = c.split(/\s+/).map((h) => {
311
313
  var b, f;
312
- return ((f = (b = l.ownerDocument.getElementById(p)) == null ? void 0 : b.textContent) == null ? void 0 : f.trim()) ?? "";
314
+ return ((f = (b = l.ownerDocument.getElementById(h)) == null ? void 0 : b.textContent) == null ? void 0 : f.trim()) ?? "";
313
315
  }).filter(Boolean);
314
316
  if (u.length) {
315
317
  a += u.join(" ");
@@ -317,7 +319,7 @@ function A(t) {
317
319
  }
318
320
  }
319
321
  a += ((i = l.getAttribute("aria-label")) == null ? void 0 : i.trim()) ?? l.getAttribute("alt") ?? ((n = l.getAttribute("title")) == null ? void 0 : n.trim()) ?? "";
320
- } else if (h === "svg") {
322
+ } else if (p === "svg") {
321
323
  const c = (r = l.getAttribute("aria-label")) == null ? void 0 : r.trim();
322
324
  if (c)
323
325
  a += c;
@@ -331,13 +333,13 @@ function A(t) {
331
333
  return a;
332
334
  }
333
335
  let _ = /* @__PURE__ */ new WeakMap();
334
- function Ee() {
336
+ function Le() {
335
337
  _ = /* @__PURE__ */ new WeakMap();
336
338
  }
337
- function Ce(t) {
338
- return t.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
339
+ function qe(e) {
340
+ return e.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
339
341
  }
340
- const Le = [
342
+ const Re = [
341
343
  "data-testid",
342
344
  "data-test-id",
343
345
  "data-cy",
@@ -347,37 +349,37 @@ const Le = [
347
349
  "for",
348
350
  "aria-label"
349
351
  ];
350
- function qe(t) {
351
- const a = t.tagName.toLowerCase();
352
- for (const i of Le) {
353
- const n = t.getAttribute(i);
352
+ function Ne(e) {
353
+ const a = e.tagName.toLowerCase();
354
+ for (const i of Re) {
355
+ const n = e.getAttribute(i);
354
356
  if (n != null && n.length > 0 && n.length < 100)
355
- return `${a}[${i}="${Ce(n)}"]`;
357
+ return `${a}[${i}="${qe(n)}"]`;
356
358
  }
357
- const e = t.parentElement;
358
- if (e) {
359
+ const t = e.parentElement;
360
+ if (t) {
359
361
  let i = 0, n = 0;
360
- for (let r = 0; r < e.children.length; r++)
361
- e.children[r].tagName === t.tagName && (i++, e.children[r] === t && (n = i));
362
+ for (let r = 0; r < t.children.length; r++)
363
+ t.children[r].tagName === e.tagName && (i++, t.children[r] === e && (n = i));
362
364
  if (i > 1)
363
365
  return `${a}:nth-of-type(${n})`;
364
366
  }
365
367
  return a;
366
368
  }
367
- function H(t) {
368
- if (t.id) return `#${CSS.escape(t.id)}`;
369
- const a = t.getRootNode(), e = a instanceof ShadowRoot ? null : a.documentElement, i = [];
370
- let n = t;
371
- for (; n && n !== e; ) {
372
- if (n !== t && n.id) {
369
+ function H(e) {
370
+ if (e.id) return `#${CSS.escape(e.id)}`;
371
+ const a = e.getRootNode(), t = a instanceof ShadowRoot ? null : a.documentElement, i = [];
372
+ let n = e;
373
+ for (; n && n !== t; ) {
374
+ if (n !== e && n.id) {
373
375
  i.unshift(`#${CSS.escape(n.id)}`);
374
376
  break;
375
377
  }
376
- if (i.unshift(qe(n)), i.length >= 2) {
378
+ if (i.unshift(Ne(n)), i.length >= 2) {
377
379
  const r = i.join(" > ");
378
380
  try {
379
381
  const o = a.querySelectorAll(r);
380
- if (o.length === 1 && o[0] === t) return r;
382
+ if (o.length === 1 && o[0] === e) return r;
381
383
  } catch {
382
384
  }
383
385
  }
@@ -385,38 +387,38 @@ function H(t) {
385
387
  }
386
388
  return i.join(" > ");
387
389
  }
388
- function m(t) {
390
+ function m(e) {
389
391
  var r;
390
- const a = _.get(t);
392
+ const a = _.get(e);
391
393
  if (a !== void 0) return a;
392
- const e = [];
393
- let i = t;
394
+ const t = [];
395
+ let i = e;
394
396
  for (; i; ) {
395
397
  const o = i.getRootNode();
396
398
  if (o instanceof ShadowRoot)
397
- e.unshift({ selector: H(i), delimiter: " >>> " }), i = o.host;
399
+ t.unshift({ selector: H(i), delimiter: " >>> " }), i = o.host;
398
400
  else {
399
401
  const s = (r = o.defaultView) == null ? void 0 : r.frameElement;
400
402
  if (s)
401
- e.unshift({ selector: H(i), delimiter: " >>>iframe> " }), i = s;
403
+ t.unshift({ selector: H(i), delimiter: " >>>iframe> " }), i = s;
402
404
  else {
403
- e.unshift({ selector: H(i), delimiter: "" });
405
+ t.unshift({ selector: H(i), delimiter: "" });
404
406
  break;
405
407
  }
406
408
  }
407
409
  }
408
- const n = e.map((o, s) => (s === 0 ? "" : o.delimiter) + o.selector).join("");
409
- return _.set(t, n), n;
410
+ const n = t.map((o, s) => (s === 0 ? "" : o.delimiter) + o.selector).join("");
411
+ return _.set(e, n), n;
410
412
  }
411
- function Ei(t) {
412
- const a = [], e = [];
413
- let i = t;
413
+ function Ri(e) {
414
+ const a = [], t = [];
415
+ let i = e;
414
416
  for (; i; ) {
415
417
  const r = i.indexOf(" >>>iframe> "), o = i.indexOf(" >>> ");
416
418
  if (r !== -1 && (o === -1 || r <= o))
417
- a.push(i.slice(0, r).trim()), e.push("iframe"), i = i.slice(r + 12);
419
+ a.push(i.slice(0, r).trim()), t.push("iframe"), i = i.slice(r + 12);
418
420
  else if (o !== -1)
419
- a.push(i.slice(0, o).trim()), e.push("shadow"), i = i.slice(o + 5);
421
+ a.push(i.slice(0, o).trim()), t.push("shadow"), i = i.slice(o + 5);
420
422
  else {
421
423
  a.push(i.trim());
422
424
  break;
@@ -427,7 +429,7 @@ function Ei(t) {
427
429
  const o = n.querySelector(a[r]);
428
430
  if (!o) return null;
429
431
  if (r < a.length - 1)
430
- if (e[r] === "iframe") {
432
+ if (t[r] === "iframe") {
431
433
  const s = o.contentDocument;
432
434
  if (!s) return null;
433
435
  n = s;
@@ -441,11 +443,11 @@ function Ei(t) {
441
443
  }
442
444
  return null;
443
445
  }
444
- function d(t) {
445
- const a = t.outerHTML;
446
+ function d(e) {
447
+ const a = e.outerHTML;
446
448
  return a.length > 200 ? a.slice(0, 200) + "..." : a;
447
449
  }
448
- const Re = /* @__PURE__ */ new Set([
450
+ const $e = /* @__PURE__ */ new Set([
449
451
  "aria-activedescendant",
450
452
  "aria-atomic",
451
453
  "aria-autocomplete",
@@ -510,7 +512,7 @@ const Re = /* @__PURE__ */ new Set([
510
512
  "aria-multiselectable",
511
513
  "aria-readonly",
512
514
  "aria-required"
513
- ]), X = /* @__PURE__ */ new Set(["aria-checked", "aria-pressed"]), Ne = /* @__PURE__ */ new Set([
515
+ ]), X = /* @__PURE__ */ new Set(["aria-checked", "aria-pressed"]), Me = /* @__PURE__ */ new Set([
514
516
  "aria-colcount",
515
517
  "aria-colindex",
516
518
  "aria-colspan",
@@ -520,7 +522,7 @@ const Re = /* @__PURE__ */ new Set([
520
522
  "aria-rowindex",
521
523
  "aria-rowspan",
522
524
  "aria-setsize"
523
- ]), $e = /* @__PURE__ */ new Set([
525
+ ]), He = /* @__PURE__ */ new Set([
524
526
  "aria-valuemax",
525
527
  "aria-valuemin",
526
528
  "aria-valuenow"
@@ -552,7 +554,7 @@ const Re = /* @__PURE__ */ new Set([
552
554
  "suggestion",
553
555
  "term",
554
556
  "time"
555
- ]), Me = {
557
+ ]), De = {
556
558
  abbr: !0,
557
559
  bdi: !0,
558
560
  bdo: !0,
@@ -580,7 +582,7 @@ const Re = /* @__PURE__ */ new Set([
580
582
  u: !0,
581
583
  var: !0,
582
584
  wbr: !0
583
- }, He = {
585
+ }, Fe = {
584
586
  alert: /* @__PURE__ */ new Set(["aria-disabled", "aria-errormessage", "aria-haspopup", "aria-invalid"]),
585
587
  article: /* @__PURE__ */ new Set(["aria-disabled", "aria-errormessage", "aria-haspopup", "aria-invalid"]),
586
588
  banner: /* @__PURE__ */ new Set(["aria-disabled", "aria-errormessage", "aria-haspopup", "aria-invalid"]),
@@ -614,14 +616,14 @@ const Re = /* @__PURE__ */ new Set([
614
616
  tooltip: /* @__PURE__ */ new Set(["aria-disabled", "aria-errormessage", "aria-haspopup", "aria-invalid"])
615
617
  };
616
618
  let C = null, L = null;
617
- function De() {
619
+ function We() {
618
620
  C = null, L = null;
619
621
  }
620
- function U(t) {
622
+ function U(e) {
621
623
  var n;
622
- if (L && (C == null ? void 0 : C.deref()) === t) return L;
623
- const a = [], e = [], i = [];
624
- for (const r of t.querySelectorAll("*")) {
624
+ if (L && (C == null ? void 0 : C.deref()) === e) return L;
625
+ const a = [], t = [], i = [];
626
+ for (const r of e.querySelectorAll("*")) {
625
627
  let o = !1;
626
628
  for (const c of r.attributes)
627
629
  if (c.name.startsWith("aria-")) {
@@ -630,10 +632,10 @@ function U(t) {
630
632
  }
631
633
  if (!o) continue;
632
634
  let s, l;
633
- const h = () => (s === void 0 && (s = m(r), l = d(r)), { selector: s, html: l });
635
+ const p = () => (s === void 0 && (s = m(r), l = d(r)), { selector: s, html: l });
634
636
  for (const c of r.attributes)
635
- if (c.name.startsWith("aria-") && !Re.has(c.name)) {
636
- const u = h();
637
+ if (c.name.startsWith("aria-") && !$e.has(c.name)) {
638
+ const u = p();
637
639
  a.push({
638
640
  ruleId: "aria-valid-attr",
639
641
  selector: u.selector,
@@ -649,54 +651,54 @@ function U(t) {
649
651
  if (!(u === "" && !Y.has(c.name) && !X.has(c.name))) {
650
652
  if (Y.has(c.name)) {
651
653
  if (u !== "true" && u !== "false") {
652
- const p = h();
653
- e.push({
654
+ const h = p();
655
+ t.push({
654
656
  ruleId: "aria-valid-attr-value",
655
- selector: p.selector,
656
- html: p.html,
657
+ selector: h.selector,
658
+ html: h.html,
657
659
  impact: "critical",
658
660
  message: `${c.name} must be "true" or "false", got "${u}".`
659
661
  });
660
662
  }
661
663
  } else if (X.has(c.name)) {
662
664
  if (u !== "true" && u !== "false" && u !== "mixed") {
663
- const p = h();
664
- e.push({
665
+ const h = p();
666
+ t.push({
665
667
  ruleId: "aria-valid-attr-value",
666
- selector: p.selector,
667
- html: p.html,
668
+ selector: h.selector,
669
+ html: h.html,
668
670
  impact: "critical",
669
671
  message: `${c.name} must be "true", "false", or "mixed", got "${u}".`
670
672
  });
671
673
  }
672
- } else if (Ne.has(c.name)) {
674
+ } else if (Me.has(c.name)) {
673
675
  if (u === "" || !/^-?\d+$/.test(u)) {
674
- const p = h();
675
- e.push({
676
+ const h = p();
677
+ t.push({
676
678
  ruleId: "aria-valid-attr-value",
677
- selector: p.selector,
678
- html: p.html,
679
+ selector: h.selector,
680
+ html: h.html,
679
681
  impact: "critical",
680
682
  message: `${c.name} must be an integer, got "${u}".`
681
683
  });
682
684
  }
683
- } else if ($e.has(c.name)) {
685
+ } else if (He.has(c.name)) {
684
686
  if (u === "" || isNaN(Number(u))) {
685
- const p = h();
686
- e.push({
687
+ const h = p();
688
+ t.push({
687
689
  ruleId: "aria-valid-attr-value",
688
- selector: p.selector,
689
- html: p.html,
690
+ selector: h.selector,
691
+ html: h.html,
690
692
  impact: "critical",
691
693
  message: `${c.name} must be a number, got "${u}".`
692
694
  });
693
695
  }
694
696
  } else if (K[c.name]) {
695
- const p = u.split(/\s+/);
696
- for (const b of p)
697
+ const h = u.split(/\s+/);
698
+ for (const b of h)
697
699
  if (!K[c.name].has(b)) {
698
- const f = h();
699
- e.push({
700
+ const f = p();
701
+ t.push({
700
702
  ruleId: "aria-valid-attr-value",
701
703
  selector: f.selector,
702
704
  html: f.html,
@@ -710,10 +712,10 @@ function U(t) {
710
712
  }
711
713
  if (!g(r)) {
712
714
  const c = (n = r.getAttribute("role")) == null ? void 0 : n.trim().toLowerCase(), u = r.tagName.toLowerCase();
713
- if (!c && Me[u]) {
714
- const p = r.hasAttribute("aria-label"), b = r.hasAttribute("aria-labelledby");
715
- if (p || b) {
716
- const f = h();
715
+ if (!c && De[u]) {
716
+ const h = r.hasAttribute("aria-label"), b = r.hasAttribute("aria-labelledby");
717
+ if (h || b) {
718
+ const f = p();
717
719
  i.push({
718
720
  ruleId: "aria-prohibited-attr",
719
721
  selector: f.selector,
@@ -726,23 +728,23 @@ function U(t) {
726
728
  if (J.has(c)) {
727
729
  const b = r.hasAttribute("aria-label"), f = r.hasAttribute("aria-labelledby");
728
730
  if (b || f) {
729
- const y = h();
731
+ const w = p();
730
732
  i.push({
731
733
  ruleId: "aria-prohibited-attr",
732
- selector: y.selector,
733
- html: y.html,
734
+ selector: w.selector,
735
+ html: w.html,
734
736
  impact: "serious",
735
737
  message: `aria-label and aria-labelledby are prohibited on role "${c}".`
736
738
  });
737
739
  }
738
740
  }
739
- const p = He[c];
740
- if (p) {
741
+ const h = Fe[c];
742
+ if (h) {
741
743
  for (const b of r.attributes)
742
- if (b.name.startsWith("aria-") && p.has(b.name)) {
744
+ if (b.name.startsWith("aria-") && h.has(b.name)) {
743
745
  if ((b.name === "aria-label" || b.name === "aria-labelledby") && J.has(c))
744
746
  continue;
745
- const f = h();
747
+ const f = p();
746
748
  i.push({
747
749
  ruleId: "aria-prohibited-attr",
748
750
  selector: f.selector,
@@ -755,26 +757,26 @@ function U(t) {
755
757
  }
756
758
  }
757
759
  }
758
- return C = new WeakRef(t), L = { validAttr: a, validAttrValue: e, prohibitedAttr: i }, L;
760
+ return C = new WeakRef(e), L = { validAttr: a, validAttrValue: t, prohibitedAttr: i }, L;
759
761
  }
760
762
  let P = /* @__PURE__ */ new WeakMap(), j = /* @__PURE__ */ new WeakMap(), V = /* @__PURE__ */ new WeakMap();
761
763
  function Oe() {
762
764
  P = /* @__PURE__ */ new WeakMap(), j = /* @__PURE__ */ new WeakMap(), V = /* @__PURE__ */ new WeakMap();
763
765
  }
764
- function w(t) {
765
- let a = P.get(t);
766
- return a || (a = getComputedStyle(t), P.set(t, a), a);
766
+ function y(e) {
767
+ let a = P.get(e);
768
+ return a || (a = getComputedStyle(e), P.set(e, a), a);
767
769
  }
768
- function R(t, a, e) {
769
- const [i, n, r] = [t, a, e].map((o) => {
770
+ function R(e, a, t) {
771
+ const [i, n, r] = [e, a, t].map((o) => {
770
772
  const s = o / 255;
771
773
  return s <= 0.04045 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
772
774
  });
773
775
  return 0.2126 * i + 0.7152 * n + 0.0722 * r;
774
776
  }
775
- function me(t, a) {
776
- const e = Math.max(t, a), i = Math.min(t, a);
777
- return (e + 0.05) / (i + 0.05);
777
+ function ge(e, a) {
778
+ const t = Math.max(e, a), i = Math.min(e, a);
779
+ return (t + 0.05) / (i + 0.05);
778
780
  }
779
781
  const Q = {
780
782
  black: [0, 0, 0],
@@ -796,37 +798,37 @@ const Q = {
796
798
  lime: [0, 255, 0],
797
799
  olive: [128, 128, 0]
798
800
  };
799
- function N(t) {
800
- const a = t.trim().toLowerCase();
801
+ function N(e) {
802
+ const a = e.trim().toLowerCase();
801
803
  if (Q[a]) return Q[a];
802
- const e = a.match(/^#([0-9a-f])([0-9a-f])([0-9a-f])$/);
803
- if (e)
804
- return [parseInt(e[1] + e[1], 16), parseInt(e[2] + e[2], 16), parseInt(e[3] + e[3], 16)];
804
+ const t = a.match(/^#([0-9a-f])([0-9a-f])([0-9a-f])$/);
805
+ if (t)
806
+ return [parseInt(t[1] + t[1], 16), parseInt(t[2] + t[2], 16), parseInt(t[3] + t[3], 16)];
805
807
  const i = a.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/);
806
808
  if (i)
807
809
  return [parseInt(i[1], 16), parseInt(i[2], 16), parseInt(i[3], 16)];
808
- const n = t.match(
810
+ const n = e.match(
809
811
  /rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*[\d.]+)?\s*\)/
810
812
  );
811
813
  if (n)
812
814
  return [parseInt(n[1]), parseInt(n[2]), parseInt(n[3])];
813
- const r = t.match(
815
+ const r = e.match(
814
816
  /rgba?\(\s*(\d+)\s+(\d+)\s+(\d+)\s*(?:\/\s*[\d.]+%?)?\s*\)/
815
817
  );
816
818
  return r ? [parseInt(r[1]), parseInt(r[2]), parseInt(r[3])] : null;
817
819
  }
818
- function Be(t) {
819
- const a = j.get(t);
820
+ function Be(e) {
821
+ const a = j.get(e);
820
822
  if (a !== void 0) return a;
821
- const e = Fe(t);
822
- return j.set(t, e), e;
823
+ const t = _e(e);
824
+ return j.set(e, t), t;
823
825
  }
824
- function Fe(t) {
825
- let a = t;
826
+ function _e(e) {
827
+ let a = e;
826
828
  for (; a; ) {
827
- const e = w(a), i = e.backgroundImage;
829
+ const t = y(a), i = t.backgroundImage;
828
830
  if (i && i !== "none" && i !== "initial") return null;
829
- const n = e.backgroundColor;
831
+ const n = t.backgroundColor;
830
832
  if (n === "transparent" || n === "rgba(0, 0, 0, 0)" || n === "rgba(0 0 0 / 0)") {
831
833
  a = a.parentElement;
832
834
  continue;
@@ -840,144 +842,144 @@ function Fe(t) {
840
842
  }
841
843
  return [255, 255, 255];
842
844
  }
843
- const We = /* @__PURE__ */ new Set(["IMG", "PICTURE", "VIDEO", "SVG"]);
844
- function _e(t) {
845
- const a = V.get(t);
845
+ const Pe = /* @__PURE__ */ new Set(["IMG", "PICTURE", "VIDEO", "SVG"]);
846
+ function je(e) {
847
+ const a = V.get(e);
846
848
  if (a !== void 0) return a;
847
- const e = Pe(t);
848
- return V.set(t, e), e;
849
+ const t = Ve(e);
850
+ return V.set(e, t), t;
849
851
  }
850
- function Pe(t) {
851
- let a = t, e = !1;
852
+ function Ve(e) {
853
+ let a = e, t = !1;
852
854
  for (; a; ) {
853
- const i = w(a).position;
854
- if ((i === "absolute" || i === "fixed") && (e = !0), a !== t && i !== "static") {
855
+ const i = y(a).position;
856
+ if ((i === "absolute" || i === "fixed") && (t = !0), a !== e && i !== "static") {
855
857
  for (const n of a.children)
856
- if (!(n === t || n.contains(t)) && We.has(n.tagName)) {
857
- if (e) return !0;
858
- const r = w(n).position;
858
+ if (!(n === e || n.contains(e)) && Pe.has(n.tagName)) {
859
+ if (t) return !0;
860
+ const r = y(n).position;
859
861
  if (r === "absolute" || r === "fixed") return !0;
860
862
  }
861
- if (e) break;
863
+ if (t) break;
862
864
  }
863
865
  a = a.parentElement;
864
866
  }
865
867
  return !1;
866
868
  }
867
- function je(t) {
868
- const a = parseFloat(t);
869
- return t.endsWith("pt") ? a * (4 / 3) : a;
869
+ function ze(e) {
870
+ const a = parseFloat(e);
871
+ return e.endsWith("pt") ? a * (4 / 3) : a;
870
872
  }
871
- function Ve(t) {
872
- const a = w(t), e = je(a.fontSize), i = parseInt(a.fontWeight) || (a.fontWeight === "bold" ? 700 : 400);
873
- return e >= 23.5 || e >= 18.5 && i >= 700;
873
+ function Ue(e) {
874
+ const a = y(e), t = ze(a.fontSize), i = parseInt(a.fontWeight) || (a.fontWeight === "bold" ? 700 : 400);
875
+ return t >= 23.5 || t >= 18.5 && i >= 700;
874
876
  }
875
- function D(t) {
877
+ function D(e) {
876
878
  var r, o;
877
- const a = [], e = t.closest("a");
878
- if (e) {
879
- const s = e.getAttribute("href");
879
+ const a = [], t = e.closest("a");
880
+ if (t) {
881
+ const s = t.getAttribute("href");
880
882
  s && a.push(`Link href: ${s}`);
881
883
  }
882
- const i = t.closest("figure");
884
+ const i = e.closest("figure");
883
885
  if (i) {
884
886
  const s = i.querySelector("figcaption");
885
887
  (r = s == null ? void 0 : s.textContent) != null && r.trim() && a.push(`Figcaption: ${s.textContent.trim().slice(0, 100)}`);
886
888
  }
887
- const n = t.parentElement;
888
- if (n && n !== e) {
889
- const s = t instanceof HTMLImageElement && t.alt || "", l = (o = n.textContent) == null ? void 0 : o.replace(s, "").trim().slice(0, 100);
889
+ const n = e.parentElement;
890
+ if (n && n !== t) {
891
+ const s = e instanceof HTMLImageElement && e.alt || "", l = (o = n.textContent) == null ? void 0 : o.replace(s, "").trim().slice(0, 100);
890
892
  l && a.push(`Adjacent text: ${l}`);
891
893
  }
892
894
  return a.length > 0 ? a.join(`
893
895
  `) : void 0;
894
896
  }
895
- function Z(t) {
896
- let a = t;
897
+ function Z(e) {
898
+ let a = e;
897
899
  for (; a; ) {
898
900
  if (a instanceof HTMLElement && a.style.visibility === "hidden") return !0;
899
901
  a = a.parentElement;
900
902
  }
901
903
  return !1;
902
904
  }
903
- const ze = {
905
+ const Ge = {
904
906
  id: "img-alt",
905
907
  wcag: ["1.1.1"],
906
908
  level: "A",
907
909
  description: `Images must have alternate text. Add an alt attribute to <img> elements. Decorative images may use an empty alt attribute (alt=""), role='none', or role='presentation'.`,
908
910
  guidance: "Every image needs an alt attribute. For informative images, describe the content or function concisely. For decorative images (backgrounds, spacers, purely visual flourishes), use alt='' to hide them from screen readers. Never omit alt entirely—screen readers may read the filename instead.",
909
911
  prompt: "Describe what alt text to add. If the image appears decorative based on context (spacer, background, icon next to text that already describes it), recommend alt=''. Otherwise suggest descriptive alt text based on the src or surrounding context.",
910
- run(t) {
912
+ run(e) {
911
913
  const a = [];
912
- for (const e of t.querySelectorAll("img")) {
913
- if (g(e) || Z(e)) continue;
914
- const i = e.getAttribute("role");
914
+ for (const t of e.querySelectorAll("img")) {
915
+ if (g(t) || Z(t)) continue;
916
+ const i = t.getAttribute("role");
915
917
  if (i === "presentation" || i === "none") {
916
- const r = e.getAttribute("tabindex");
918
+ const r = t.getAttribute("tabindex");
917
919
  if (!r || r === "-1") continue;
918
920
  }
919
- const n = e.getAttribute("alt");
921
+ const n = t.getAttribute("alt");
920
922
  if (n !== null && n.trim() === "" && n !== "") {
921
923
  a.push({
922
924
  ruleId: "img-alt",
923
- selector: m(e),
924
- html: d(e),
925
+ selector: m(t),
926
+ html: d(t),
925
927
  impact: "critical",
926
928
  message: 'Image has whitespace-only alt text. Use alt="" for decorative images or provide descriptive text.',
927
- context: D(e)
929
+ context: D(t)
928
930
  });
929
931
  continue;
930
932
  }
931
- !e.hasAttribute("alt") && !v(e) && a.push({
933
+ !t.hasAttribute("alt") && !v(t) && a.push({
932
934
  ruleId: "img-alt",
933
- selector: m(e),
934
- html: d(e),
935
+ selector: m(t),
936
+ html: d(t),
935
937
  impact: "critical",
936
938
  message: "Image element missing alt attribute.",
937
- context: D(e)
939
+ context: D(t)
938
940
  });
939
941
  }
940
- for (const e of t.querySelectorAll('[role="img"]:not(img):not(svg)'))
941
- g(e) || Z(e) || v(e) || a.push({
942
+ for (const t of e.querySelectorAll('[role="img"]:not(img):not(svg)'))
943
+ g(t) || Z(t) || v(t) || a.push({
942
944
  ruleId: "img-alt",
943
- selector: m(e),
944
- html: d(e),
945
+ selector: m(t),
946
+ html: d(t),
945
947
  impact: "critical",
946
948
  message: 'Element with role="img" has no accessible name. Add aria-label or aria-labelledby.',
947
- context: D(e)
949
+ context: D(t)
948
950
  });
949
951
  return a;
950
952
  }
951
953
  };
952
- function Ue(t) {
954
+ function Ye(e) {
953
955
  var r, o, s;
954
- const a = t.getAttribute("aria-labelledby");
956
+ const a = e.getAttribute("aria-labelledby");
955
957
  if (a) {
956
- const l = a.split(/\s+/).map((h) => {
958
+ const l = a.split(/\s+/).map((p) => {
957
959
  var c, u;
958
- return ((u = (c = t.ownerDocument.getElementById(h)) == null ? void 0 : c.textContent) == null ? void 0 : u.trim()) ?? "";
960
+ return ((u = (c = e.ownerDocument.getElementById(p)) == null ? void 0 : c.textContent) == null ? void 0 : u.trim()) ?? "";
959
961
  }).filter(Boolean);
960
962
  if (l.length) return l.join(" ");
961
963
  }
962
- const e = (r = t.getAttribute("aria-label")) == null ? void 0 : r.trim();
963
- if (e) return e;
964
- const i = t.querySelector("title");
964
+ const t = (r = e.getAttribute("aria-label")) == null ? void 0 : r.trim();
965
+ if (t) return t;
966
+ const i = e.querySelector("title");
965
967
  if ((o = i == null ? void 0 : i.textContent) != null && o.trim()) return i.textContent.trim();
966
- const n = (s = t.getAttribute("title")) == null ? void 0 : s.trim();
968
+ const n = (s = e.getAttribute("title")) == null ? void 0 : s.trim();
967
969
  return n || "";
968
970
  }
969
- const Ge = {
971
+ const Xe = {
970
972
  id: "svg-img-alt",
971
973
  wcag: ["1.1.1"],
972
974
  level: "A",
973
975
  description: "SVG elements with an img, graphics-document, or graphics-symbol role must have an accessible name via a <title> element, aria-label, or aria-labelledby.",
974
976
  guidance: "Inline SVGs with role='img' need accessible names. Add a <title> element as the first child of the SVG (screen readers will announce it), or use aria-label on the SVG element. For complex SVGs, use aria-labelledby referencing both a <title> and <desc> element. Decorative SVGs should use aria-hidden='true' instead.",
975
977
  prompt: "Based on the SVG content or context, suggest either adding aria-label with a description, or if decorative, replacing role='img' with aria-hidden='true'.",
976
- run(t) {
977
- const a = [], e = 'svg[role="img"], [role="graphics-document"], [role="graphics-symbol"]';
978
- for (const i of t.querySelectorAll(e)) {
978
+ run(e) {
979
+ const a = [], t = 'svg[role="img"], [role="graphics-document"], [role="graphics-symbol"]';
980
+ for (const i of e.querySelectorAll(t)) {
979
981
  if (g(i)) continue;
980
- if (!Ue(i)) {
982
+ if (!Ye(i)) {
981
983
  const r = i.getAttribute("role");
982
984
  a.push({
983
985
  ruleId: "svg-img-alt",
@@ -990,26 +992,26 @@ const Ge = {
990
992
  }
991
993
  return a;
992
994
  }
993
- }, Ye = {
995
+ }, Ke = {
994
996
  id: "input-image-alt",
995
997
  wcag: ["1.1.1", "4.1.2"],
996
998
  level: "A",
997
999
  description: 'Image inputs (<input type="image">) must have alternate text via alt, aria-label, or aria-labelledby. The text should describe the button action, not the image.',
998
1000
  guidance: "Image buttons (<input type='image'>) must have alternate text via alt, aria-label, or aria-labelledby. The text should describe the button action, not the image.",
999
1001
  prompt: "Based on the src attribute or form context, suggest alt text describing the button's action (e.g., 'Submit', 'Search', 'Go').",
1000
- run(t) {
1002
+ run(e) {
1001
1003
  const a = [];
1002
- for (const e of t.querySelectorAll('input[type="image"]'))
1003
- g(e) || v(e) || a.push({
1004
+ for (const t of e.querySelectorAll('input[type="image"]'))
1005
+ g(t) || v(t) || a.push({
1004
1006
  ruleId: "input-image-alt",
1005
- selector: m(e),
1006
- html: d(e),
1007
+ selector: m(t),
1008
+ html: d(t),
1007
1009
  impact: "critical",
1008
1010
  message: "Image input missing alt text."
1009
1011
  });
1010
1012
  return a;
1011
1013
  }
1012
- }, Xe = {
1014
+ }, Je = {
1013
1015
  id: "image-redundant-alt",
1014
1016
  wcag: [],
1015
1017
  level: "A",
@@ -1017,15 +1019,15 @@ const Ge = {
1017
1019
  description: "Image alt text should not duplicate adjacent link or button text. When alt text repeats surrounding text, screen reader users hear the same information twice.",
1018
1020
  guidance: "When an image is inside a link or button that also has text, make the alt text complementary rather than identical. If the image is purely decorative in that context, use alt='' to avoid repetition.",
1019
1021
  prompt: "The image alt text is identical to the text already visible in the parent link or button. Screen reader users hear the same words twice. If the image is decorative in this context (e.g. an icon next to a label), recommend alt=''. Otherwise suggest brief complementary alt text that adds information the visible text doesn't convey — for example what the image depicts, not what the link says.",
1020
- run(t) {
1021
- var e;
1022
+ run(e) {
1023
+ var t;
1022
1024
  const a = [];
1023
- for (const i of t.querySelectorAll("img[alt]")) {
1025
+ for (const i of e.querySelectorAll("img[alt]")) {
1024
1026
  const n = i.getAttribute("alt").trim().toLowerCase();
1025
1027
  if (!n) continue;
1026
1028
  const r = i.closest("a, button");
1027
1029
  if (r) {
1028
- const o = ((e = r.textContent) == null ? void 0 : e.trim().toLowerCase()) || "";
1030
+ const o = ((t = r.textContent) == null ? void 0 : t.trim().toLowerCase()) || "";
1029
1031
  if (o && o === n) {
1030
1032
  const s = r.tagName.toLowerCase(), l = r.getAttribute("href");
1031
1033
  a.push({
@@ -1041,7 +1043,7 @@ const Ge = {
1041
1043
  }
1042
1044
  return a;
1043
1045
  }
1044
- }, Ke = ["image", "picture", "photo", "graphic", "icon", "img"], Je = {
1046
+ }, Qe = ["image", "picture", "photo", "graphic", "icon", "img"], Ze = {
1045
1047
  id: "image-alt-redundant-words",
1046
1048
  wcag: [],
1047
1049
  level: "A",
@@ -1049,38 +1051,38 @@ const Ge = {
1049
1051
  description: "Image alt text should not contain words like 'image', 'photo', or 'picture' — screen readers already announce the element type.",
1050
1052
  guidance: "Screen readers already announce 'image' or 'graphic' before reading alt text, so phrases like 'image of', 'photo of', or 'picture of' are redundant. Remove these words and describe what the image shows. For example, change 'image of a dog' to 'golden retriever playing fetch'.",
1051
1053
  prompt: "The alt text contains a word like 'image', 'photo', or 'picture' that is already announced by the screen reader. Rewrite the alt text with the redundant word removed while keeping the description meaningful. For example: 'image of a sunset over the ocean' → 'sunset over the ocean'; 'photo of team members' → 'team members at the 2024 offsite'; 'icon for settings' → 'settings'.",
1052
- run(t) {
1054
+ run(e) {
1053
1055
  const a = [];
1054
- for (const e of t.querySelectorAll("img[alt]")) {
1055
- const i = e.getAttribute("alt").toLowerCase();
1056
+ for (const t of e.querySelectorAll("img[alt]")) {
1057
+ const i = t.getAttribute("alt").toLowerCase();
1056
1058
  if (!i) continue;
1057
- const n = Ke.filter((r) => i.split(/\s+/).includes(r));
1059
+ const n = Qe.filter((r) => i.split(/\s+/).includes(r));
1058
1060
  n.length > 0 && a.push({
1059
1061
  ruleId: "image-alt-redundant-words",
1060
- selector: m(e),
1061
- html: d(e),
1062
+ selector: m(t),
1063
+ html: d(t),
1062
1064
  impact: "minor",
1063
- message: `Alt text "${e.getAttribute("alt")}" contains redundant word(s): ${n.join(", ")}.`,
1064
- context: `Current alt: "${e.getAttribute("alt")}", redundant word(s): ${n.join(", ")}`
1065
+ message: `Alt text "${t.getAttribute("alt")}" contains redundant word(s): ${n.join(", ")}.`,
1066
+ context: `Current alt: "${t.getAttribute("alt")}", redundant word(s): ${n.join(", ")}`
1065
1067
  });
1066
1068
  }
1067
1069
  return a;
1068
1070
  }
1069
- }, Qe = {
1071
+ }, et = {
1070
1072
  id: "area-alt",
1071
1073
  wcag: ["1.1.1", "4.1.2"],
1072
1074
  level: "A",
1073
1075
  description: "Image map <area> elements must have alternative text.",
1074
1076
  guidance: "Each clickable region in an image map needs alternative text so screen reader users know what the region represents. Add an alt attribute to every <area> element describing its purpose. For complex image maps, consider using alternative approaches like SVG with embedded links, or a list of text links.",
1075
1077
  prompt: "Based on the href or shape/coords, suggest alt text describing where this area links or what it represents.",
1076
- run(t) {
1078
+ run(e) {
1077
1079
  const a = [];
1078
- for (const e of t.querySelectorAll("area[href]")) {
1079
- if (g(e)) continue;
1080
- v(e) || a.push({
1080
+ for (const t of e.querySelectorAll("area[href]")) {
1081
+ if (g(t)) continue;
1082
+ v(t) || a.push({
1081
1083
  ruleId: "area-alt",
1082
- selector: m(e),
1083
- html: d(e),
1084
+ selector: m(t),
1085
+ html: d(t),
1084
1086
  impact: "critical",
1085
1087
  message: "Image map <area> element is missing alternative text."
1086
1088
  });
@@ -1088,32 +1090,32 @@ const Ge = {
1088
1090
  return a;
1089
1091
  }
1090
1092
  };
1091
- function Ze(t) {
1093
+ function tt(e) {
1092
1094
  var n, r;
1093
- const a = t.getAttribute("aria-labelledby");
1095
+ const a = e.getAttribute("aria-labelledby");
1094
1096
  if (a) {
1095
1097
  const o = a.split(/\s+/).map((s) => {
1096
- var l, h;
1097
- return ((h = (l = t.ownerDocument.getElementById(s)) == null ? void 0 : l.textContent) == null ? void 0 : h.trim()) ?? "";
1098
+ var l, p;
1099
+ return ((p = (l = e.ownerDocument.getElementById(s)) == null ? void 0 : l.textContent) == null ? void 0 : p.trim()) ?? "";
1098
1100
  }).filter(Boolean);
1099
1101
  if (o.length) return o.join(" ");
1100
1102
  }
1101
- const e = (n = t.getAttribute("aria-label")) == null ? void 0 : n.trim();
1102
- if (e) return e;
1103
- const i = (r = t.getAttribute("title")) == null ? void 0 : r.trim();
1103
+ const t = (n = e.getAttribute("aria-label")) == null ? void 0 : n.trim();
1104
+ if (t) return t;
1105
+ const i = (r = e.getAttribute("title")) == null ? void 0 : r.trim();
1104
1106
  return i || "";
1105
1107
  }
1106
- const et = {
1108
+ const at = {
1107
1109
  id: "object-alt",
1108
1110
  wcag: ["1.1.1"],
1109
1111
  level: "A",
1110
1112
  description: "<object> elements must have alternative text.",
1111
1113
  guidance: "Object elements embed external content that may not be accessible to all users. Provide alternative text via aria-label, aria-labelledby, or a title attribute. The fallback content inside <object> is only shown when the object fails to load and does not serve as an accessible name.",
1112
1114
  prompt: "Based on the data/type attributes, suggest adding aria-label or a title attribute describing what the embedded content represents.",
1113
- run(t) {
1114
- var e;
1115
+ run(e) {
1116
+ var t;
1115
1117
  const a = [];
1116
- for (const i of t.querySelectorAll("object")) {
1118
+ for (const i of e.querySelectorAll("object")) {
1117
1119
  if (g(i) || i instanceof HTMLElement && i.style.visibility === "hidden") continue;
1118
1120
  let n = i.parentElement, r = !1;
1119
1121
  for (; n; ) {
@@ -1123,11 +1125,11 @@ const et = {
1123
1125
  }
1124
1126
  n = n.parentElement;
1125
1127
  }
1126
- if (r || i.getAttribute("role") === "presentation" || i.getAttribute("role") === "none" || Ze(i)) continue;
1128
+ if (r || i.getAttribute("role") === "presentation" || i.getAttribute("role") === "none" || tt(i)) continue;
1127
1129
  const o = i.getAttribute("data") || "";
1128
1130
  if (!((i.getAttribute("type") || "").startsWith("image/") || /\.(png|jpg|jpeg|gif|svg|webp|bmp|ico)$/i.test(o))) {
1129
- const h = i.querySelector("img[alt]");
1130
- if (h && ((e = h.getAttribute("alt")) != null && e.trim())) continue;
1131
+ const p = i.querySelector("img[alt]");
1132
+ if (p && ((t = p.getAttribute("alt")) != null && t.trim())) continue;
1131
1133
  }
1132
1134
  a.push({
1133
1135
  ruleId: "object-alt",
@@ -1139,21 +1141,21 @@ const et = {
1139
1141
  }
1140
1142
  return a;
1141
1143
  }
1142
- }, tt = {
1144
+ }, it = {
1143
1145
  id: "role-img-alt",
1144
1146
  wcag: ["1.1.1"],
1145
1147
  level: "A",
1146
1148
  description: "Elements with role='img' must have an accessible name.",
1147
1149
  guidance: "When you assign role='img' to an element (like a div containing icon fonts or CSS backgrounds), you must provide an accessible name via aria-label or aria-labelledby. Without this, screen reader users have no way to understand what the image represents. If the image is decorative, use role='presentation' or role='none' instead.",
1148
1150
  prompt: "Based on the element's content or class names, suggest either an aria-label describing the image, or if decorative, recommend removing role='img' or adding aria-hidden='true'.",
1149
- run(t) {
1151
+ run(e) {
1150
1152
  const a = [];
1151
- for (const e of t.querySelectorAll('[role="img"]')) {
1152
- if (g(e) || e.tagName.toLowerCase() === "svg" || e.tagName.toLowerCase() === "img") continue;
1153
- v(e) || a.push({
1153
+ for (const t of e.querySelectorAll('[role="img"]')) {
1154
+ if (g(t) || t.tagName.toLowerCase() === "svg" || t.tagName.toLowerCase() === "img") continue;
1155
+ v(t) || a.push({
1154
1156
  ruleId: "role-img-alt",
1155
- selector: m(e),
1156
- html: d(e),
1157
+ selector: m(t),
1158
+ html: d(t),
1157
1159
  impact: "serious",
1158
1160
  message: "Element with role='img' has no accessible name. Add aria-label or aria-labelledby."
1159
1161
  });
@@ -1161,17 +1163,17 @@ const et = {
1161
1163
  return a;
1162
1164
  }
1163
1165
  };
1164
- function Ci(t) {
1165
- if (typeof t != "object" || t === null)
1166
+ function Ni(e) {
1167
+ if (typeof e != "object" || e === null)
1166
1168
  return "Rule spec must be an object";
1167
- const a = t;
1169
+ const a = e;
1168
1170
  if (typeof a.id != "string" || a.id.length === 0)
1169
1171
  return "Rule must have a non-empty string id";
1170
1172
  if (typeof a.selector != "string" || a.selector.length === 0)
1171
1173
  return "Rule must have a non-empty string selector";
1172
1174
  if (typeof a.check != "object" || a.check === null)
1173
1175
  return "Rule must have a check object";
1174
- const e = a.check;
1176
+ const t = a.check;
1175
1177
  if (![
1176
1178
  "selector-exists",
1177
1179
  "attribute-value",
@@ -1179,8 +1181,8 @@ function Ci(t) {
1179
1181
  "attribute-regex",
1180
1182
  "child-required",
1181
1183
  "child-invalid"
1182
- ].includes(e.type))
1183
- return `Invalid check type: ${String(e.type)}`;
1184
+ ].includes(t.type))
1185
+ return `Invalid check type: ${String(t.type)}`;
1184
1186
  if (typeof a.impact != "string" || !["critical", "serious", "moderate", "minor"].includes(a.impact))
1185
1187
  return "Rule must have a valid impact (critical|serious|moderate|minor)";
1186
1188
  if (typeof a.message != "string" || a.message.length === 0)
@@ -1191,153 +1193,171 @@ function Ci(t) {
1191
1193
  return "Rule must have a wcag array";
1192
1194
  if (typeof a.level != "string" || !["A", "AA"].includes(a.level))
1193
1195
  return "Rule must have level A or AA";
1194
- const n = at(e);
1196
+ const n = nt(t);
1195
1197
  return n || null;
1196
1198
  }
1197
- function at(t) {
1198
- switch (t.type) {
1199
+ function nt(e) {
1200
+ switch (e.type) {
1199
1201
  case "selector-exists":
1200
1202
  return null;
1201
1203
  case "attribute-value":
1202
- return typeof t.attribute != "string" ? "attribute-value check requires attribute string" : [">", "<", "=", "!=", "in", "not-in"].includes(t.operator) ? t.value === void 0 ? "attribute-value check requires value" : null : "attribute-value check requires valid operator";
1204
+ return typeof e.attribute != "string" ? "attribute-value check requires attribute string" : [">", "<", "=", "!=", "in", "not-in"].includes(e.operator) ? e.value === void 0 ? "attribute-value check requires value" : null : "attribute-value check requires valid operator";
1203
1205
  case "attribute-missing":
1204
- return typeof t.attribute != "string" ? "attribute-missing check requires attribute string" : null;
1206
+ return typeof e.attribute != "string" ? "attribute-missing check requires attribute string" : null;
1205
1207
  case "attribute-regex":
1206
- return typeof t.attribute != "string" ? "attribute-regex check requires attribute string" : typeof t.pattern != "string" ? "attribute-regex check requires pattern string" : typeof t.shouldMatch != "boolean" ? "attribute-regex check requires shouldMatch boolean" : null;
1208
+ return typeof e.attribute != "string" ? "attribute-regex check requires attribute string" : typeof e.pattern != "string" ? "attribute-regex check requires pattern string" : typeof e.shouldMatch != "boolean" ? "attribute-regex check requires shouldMatch boolean" : null;
1207
1209
  case "child-required":
1208
- return typeof t.childSelector != "string" ? "child-required check requires childSelector string" : null;
1210
+ return typeof e.childSelector != "string" ? "child-required check requires childSelector string" : null;
1209
1211
  case "child-invalid":
1210
- return Array.isArray(t.allowedChildren) ? null : "child-invalid check requires allowedChildren array";
1212
+ return Array.isArray(e.allowedChildren) ? null : "child-invalid check requires allowedChildren array";
1211
1213
  default:
1212
- return `Unknown check type: ${String(t.type)}`;
1214
+ return `Unknown check type: ${String(e.type)}`;
1213
1215
  }
1214
1216
  }
1215
- function S(t, a, e) {
1216
- let i = t;
1217
+ function S(e, a, t) {
1218
+ let i = e;
1217
1219
  if (i.includes("{{tag}}") && (i = i.replace(/\{\{tag\}\}/g, a.tagName.toLowerCase())), i.includes("{{value}}")) {
1218
1220
  let n = "";
1219
- "attribute" in e && e.attribute && (n = a.getAttribute(e.attribute) ?? ""), i = i.replace(/\{\{value\}\}/g, n);
1221
+ "attribute" in t && t.attribute && (n = a.getAttribute(t.attribute) ?? ""), i = i.replace(/\{\{value\}\}/g, n);
1220
1222
  }
1221
1223
  return i;
1222
1224
  }
1223
- function k(t) {
1224
- const a = t.skipAriaHidden !== !1;
1225
+ function k(e) {
1226
+ const a = e.skipAriaHidden !== !1;
1225
1227
  return {
1226
- id: t.id,
1227
- wcag: t.wcag,
1228
- level: t.level,
1229
- tags: t.tags,
1230
- description: t.description,
1231
- guidance: t.guidance,
1232
- prompt: t.prompt,
1233
- run(e) {
1228
+ id: e.id,
1229
+ wcag: e.wcag,
1230
+ level: e.level,
1231
+ tags: e.tags,
1232
+ description: e.description,
1233
+ guidance: e.guidance,
1234
+ prompt: e.prompt,
1235
+ run(t) {
1234
1236
  var n, r;
1235
1237
  const i = [];
1236
- switch (t.check.type) {
1238
+ switch (e.check.type) {
1237
1239
  case "selector-exists": {
1238
- for (const o of e.querySelectorAll(t.selector))
1240
+ for (const o of t.querySelectorAll(e.selector))
1239
1241
  a && g(o) || i.push({
1240
- ruleId: t.id,
1242
+ ruleId: e.id,
1241
1243
  selector: m(o),
1242
1244
  html: d(o),
1243
- impact: t.impact,
1244
- message: S(t.message, o, t.check),
1245
+ impact: e.impact,
1246
+ message: S(e.message, o, e.check),
1245
1247
  element: o
1246
1248
  });
1247
1249
  break;
1248
1250
  }
1249
1251
  case "attribute-value": {
1250
- const { attribute: o, operator: s, value: l } = t.check;
1251
- for (const h of e.querySelectorAll(t.selector)) {
1252
- if (a && g(h)) continue;
1253
- const c = h.getAttribute(o);
1254
- c !== null && it(c, s, l) && i.push({
1255
- ruleId: t.id,
1256
- selector: m(h),
1257
- html: d(h),
1258
- impact: t.impact,
1259
- message: S(t.message, h, t.check),
1260
- element: h
1252
+ const { attribute: o, operator: s, value: l } = e.check;
1253
+ for (const p of t.querySelectorAll(e.selector)) {
1254
+ if (a && g(p)) continue;
1255
+ const c = p.getAttribute(o);
1256
+ c !== null && rt(c, s, l) && i.push({
1257
+ ruleId: e.id,
1258
+ selector: m(p),
1259
+ html: d(p),
1260
+ impact: e.impact,
1261
+ message: S(e.message, p, e.check),
1262
+ element: p
1261
1263
  });
1262
1264
  }
1263
1265
  break;
1264
1266
  }
1265
1267
  case "attribute-missing": {
1266
- const { attribute: o } = t.check;
1267
- for (const s of e.querySelectorAll(t.selector))
1268
+ const { attribute: o } = e.check;
1269
+ for (const s of t.querySelectorAll(e.selector))
1268
1270
  a && g(s) || s.hasAttribute(o) || i.push({
1269
- ruleId: t.id,
1271
+ ruleId: e.id,
1270
1272
  selector: m(s),
1271
1273
  html: d(s),
1272
- impact: t.impact,
1273
- message: S(t.message, s, t.check),
1274
+ impact: e.impact,
1275
+ message: S(e.message, s, e.check),
1274
1276
  element: s
1275
1277
  });
1276
1278
  break;
1277
1279
  }
1278
1280
  case "attribute-regex": {
1279
- const { attribute: o, pattern: s, flags: l, shouldMatch: h } = t.check;
1281
+ const { attribute: o, pattern: s, flags: l, shouldMatch: p } = e.check;
1280
1282
  let c;
1281
1283
  try {
1282
1284
  c = new RegExp(s, l);
1283
1285
  } catch {
1284
1286
  break;
1285
1287
  }
1286
- for (const u of e.querySelectorAll(t.selector)) {
1288
+ for (const u of t.querySelectorAll(e.selector)) {
1287
1289
  if (a && g(u)) continue;
1288
- const p = u.getAttribute(o);
1289
- if (p === null) continue;
1290
- const b = c.test(p);
1291
- h && !b ? i.push({
1292
- ruleId: t.id,
1290
+ const h = u.getAttribute(o);
1291
+ if (h === null) continue;
1292
+ const b = c.test(h);
1293
+ p && !b ? i.push({
1294
+ ruleId: e.id,
1293
1295
  selector: m(u),
1294
1296
  html: d(u),
1295
- impact: t.impact,
1296
- message: S(t.message, u, t.check),
1297
+ impact: e.impact,
1298
+ message: S(e.message, u, e.check),
1297
1299
  element: u
1298
- }) : !h && b && i.push({
1299
- ruleId: t.id,
1300
+ }) : !p && b && i.push({
1301
+ ruleId: e.id,
1300
1302
  selector: m(u),
1301
1303
  html: d(u),
1302
- impact: t.impact,
1303
- message: S(t.message, u, t.check),
1304
+ impact: e.impact,
1305
+ message: S(e.message, u, e.check),
1304
1306
  element: u
1305
1307
  });
1306
1308
  }
1307
1309
  break;
1308
1310
  }
1309
1311
  case "child-required": {
1310
- const { childSelector: o } = t.check;
1311
- for (const s of e.querySelectorAll(t.selector))
1312
+ const { childSelector: o } = e.check;
1313
+ for (const s of t.querySelectorAll(e.selector))
1312
1314
  a && g(s) || s.querySelector(o) || i.push({
1313
- ruleId: t.id,
1315
+ ruleId: e.id,
1314
1316
  selector: m(s),
1315
1317
  html: d(s),
1316
- impact: t.impact,
1317
- message: S(t.message, s, t.check),
1318
+ impact: e.impact,
1319
+ message: S(e.message, s, e.check),
1318
1320
  element: s
1319
1321
  });
1320
1322
  break;
1321
1323
  }
1322
1324
  case "child-invalid": {
1323
1325
  const o = new Set(
1324
- t.check.allowedChildren.map((l) => l.toLowerCase())
1325
- ), s = t.check.allowedChildRoles ? new Set(t.check.allowedChildRoles.map((l) => l.toLowerCase())) : null;
1326
- for (const l of e.querySelectorAll(t.selector)) {
1326
+ e.check.allowedChildren.map((l) => l.toLowerCase())
1327
+ ), s = e.check.allowedChildRoles ? new Set(e.check.allowedChildRoles.map((l) => l.toLowerCase())) : null;
1328
+ for (const l of t.querySelectorAll(e.selector)) {
1327
1329
  if (a && g(l)) continue;
1328
- const h = (n = l.getAttribute("role")) == null ? void 0 : n.trim().toLowerCase();
1329
- if (!(h === "presentation" || h === "none"))
1330
- for (const c of l.children) {
1331
- if (o.has(c.tagName.toLowerCase())) continue;
1332
- const u = (r = c.getAttribute("role")) == null ? void 0 : r.trim().toLowerCase();
1333
- if (!(u && (s != null && s.has(u))) && !(u === "presentation" || u === "none")) {
1330
+ const p = (n = l.getAttribute("role")) == null ? void 0 : n.trim().toLowerCase();
1331
+ if (p === "presentation" || p === "none") continue;
1332
+ let c = !1;
1333
+ const u = e.check.allowedChildren.filter(
1334
+ (h) => h !== "script" && h !== "template"
1335
+ );
1336
+ for (const h of l.childNodes)
1337
+ if (h.nodeType === 3 && h.textContent && h.textContent.trim()) {
1338
+ const b = u.map((f) => `<${f}>`).join(" or ");
1339
+ i.push({
1340
+ ruleId: e.id,
1341
+ selector: m(l),
1342
+ html: d(l),
1343
+ impact: e.impact,
1344
+ message: `<${l.tagName.toLowerCase()}> contains direct text content. Wrap in ${b}.`,
1345
+ element: l
1346
+ }), c = !0;
1347
+ break;
1348
+ }
1349
+ if (!c)
1350
+ for (const h of l.children) {
1351
+ if (o.has(h.tagName.toLowerCase())) continue;
1352
+ const b = (r = h.getAttribute("role")) == null ? void 0 : r.trim().toLowerCase();
1353
+ if (!(b && (s != null && s.has(b))) && !(b === "presentation" || b === "none")) {
1334
1354
  i.push({
1335
- ruleId: t.id,
1336
- selector: m(c),
1337
- html: d(c),
1338
- impact: t.impact,
1339
- message: S(t.message, c, t.check),
1340
- element: c
1355
+ ruleId: e.id,
1356
+ selector: m(h),
1357
+ html: d(h),
1358
+ impact: e.impact,
1359
+ message: S(e.message, h, e.check),
1360
+ element: h
1341
1361
  });
1342
1362
  break;
1343
1363
  }
@@ -1350,25 +1370,25 @@ function k(t) {
1350
1370
  }
1351
1371
  };
1352
1372
  }
1353
- function it(t, a, e) {
1373
+ function rt(e, a, t) {
1354
1374
  switch (a) {
1355
1375
  case ">":
1356
- return parseFloat(t) > e;
1376
+ return parseFloat(e) > t;
1357
1377
  case "<":
1358
- return parseFloat(t) < e;
1378
+ return parseFloat(e) < t;
1359
1379
  case "=":
1360
- return t === String(e);
1380
+ return e === String(t);
1361
1381
  case "!=":
1362
- return t !== String(e);
1382
+ return e !== String(t);
1363
1383
  case "in":
1364
- return Array.isArray(e) && e.includes(t);
1384
+ return Array.isArray(t) && t.includes(e);
1365
1385
  case "not-in":
1366
- return Array.isArray(e) && !e.includes(t);
1386
+ return Array.isArray(t) && !t.includes(e);
1367
1387
  default:
1368
1388
  return !1;
1369
1389
  }
1370
1390
  }
1371
- const nt = {
1391
+ const ot = {
1372
1392
  id: "server-side-image-map",
1373
1393
  selector: "img[ismap], input[type='image'][ismap]",
1374
1394
  check: { type: "selector-exists" },
@@ -1379,7 +1399,7 @@ const nt = {
1379
1399
  level: "A",
1380
1400
  guidance: "Server-side image maps (using ismap attribute) send click coordinates to the server, which is inaccessible to keyboard users and screen readers who can't precisely click specific regions. Replace with client-side image maps (<map> with <area> elements) that provide keyboard access and accessible names, or use linked images/buttons instead.",
1381
1401
  prompt: "Explain that the ismap attribute should be removed and the functionality replaced with a client-side <map> element with <area> children, or separate linked images/buttons."
1382
- }, rt = k(nt), ot = [
1402
+ }, st = k(ot), lt = [
1383
1403
  '[role="checkbox"]',
1384
1404
  '[role="combobox"]',
1385
1405
  '[role="listbox"]',
@@ -1391,13 +1411,13 @@ const nt = {
1391
1411
  '[role="spinbutton"]',
1392
1412
  '[role="switch"]',
1393
1413
  '[role="textbox"]'
1394
- ].join(", "), st = /* @__PURE__ */ new Set([
1414
+ ].join(", "), ct = /* @__PURE__ */ new Set([
1395
1415
  "checkbox",
1396
1416
  "menuitemcheckbox",
1397
1417
  "menuitemradio",
1398
1418
  "radio",
1399
1419
  "switch"
1400
- ]), lt = /* @__PURE__ */ new Set([
1420
+ ]), ut = /* @__PURE__ */ new Set([
1401
1421
  "combobox",
1402
1422
  "listbox",
1403
1423
  "searchbox",
@@ -1405,64 +1425,64 @@ const nt = {
1405
1425
  "spinbutton",
1406
1426
  "textbox"
1407
1427
  ]);
1408
- function ct(t) {
1409
- var o, s, l, h;
1410
- const a = (o = t.getAttribute("role")) == null ? void 0 : o.trim().toLowerCase();
1411
- if (a && st.has(a) || (t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement) && !(a && lt.has(a)))
1412
- return v(t);
1413
- const i = t.getAttribute("aria-labelledby");
1428
+ function dt(e) {
1429
+ var o, s, l, p;
1430
+ const a = (o = e.getAttribute("role")) == null ? void 0 : o.trim().toLowerCase();
1431
+ if (a && ct.has(a) || (e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement) && !(a && ut.has(a)))
1432
+ return v(e);
1433
+ const i = e.getAttribute("aria-labelledby");
1414
1434
  if (i) {
1415
1435
  const c = i.split(/\s+/).map((u) => {
1416
- const p = t.ownerDocument.getElementById(u);
1417
- return p ? A(p).trim() : "";
1436
+ const h = e.ownerDocument.getElementById(u);
1437
+ return h ? A(h).trim() : "";
1418
1438
  }).filter(Boolean);
1419
1439
  if (c.length) return c.join(" ");
1420
1440
  }
1421
- const n = (s = t.getAttribute("aria-label")) == null ? void 0 : s.trim();
1441
+ const n = (s = e.getAttribute("aria-label")) == null ? void 0 : s.trim();
1422
1442
  if (n) return n;
1423
- if (t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement || t instanceof HTMLSelectElement) {
1424
- if (t.id) {
1425
- const u = t.ownerDocument.querySelector(`label[for="${CSS.escape(t.id)}"]`);
1443
+ if (e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement || e instanceof HTMLSelectElement) {
1444
+ if (e.id) {
1445
+ const u = e.ownerDocument.querySelector(`label[for="${CSS.escape(e.id)}"]`);
1426
1446
  if (u) {
1427
- const p = A(u).trim();
1428
- if (p) return p;
1447
+ const h = A(u).trim();
1448
+ if (h) return h;
1429
1449
  }
1430
1450
  }
1431
- const c = t.closest("label");
1451
+ const c = e.closest("label");
1432
1452
  if (c) {
1433
1453
  const u = A(c).trim();
1434
1454
  if (u) return u;
1435
1455
  }
1436
1456
  }
1437
- const r = (l = t.getAttribute("title")) == null ? void 0 : l.trim();
1457
+ const r = (l = e.getAttribute("title")) == null ? void 0 : l.trim();
1438
1458
  if (r) return r;
1439
- if (t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement) {
1440
- const c = (h = t.getAttribute("placeholder")) == null ? void 0 : h.trim();
1459
+ if (e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement) {
1460
+ const c = (p = e.getAttribute("placeholder")) == null ? void 0 : p.trim();
1441
1461
  if (c) return c;
1442
1462
  }
1443
1463
  return "";
1444
1464
  }
1445
- const ut = {
1465
+ const mt = {
1446
1466
  id: "label",
1447
1467
  wcag: ["4.1.2"],
1448
1468
  level: "A",
1449
1469
  description: "Form elements must have labels. Use <label>, aria-label, or aria-labelledby.",
1450
1470
  guidance: "Every form input needs an accessible label so users understand what information to enter. Use a <label> element with a for attribute matching the input's id, wrap the input in a <label>, or use aria-label/aria-labelledby for custom components. Placeholders are not sufficient as labels since they disappear when typing.",
1451
1471
  prompt: `This form field has no accessible label. Based on the context (input type, name attribute, placeholder, or surrounding elements), suggest adding a <label for="id"> element with descriptive text, or an aria-label attribute. The label should describe what information the user should enter, not the field type. For example: 'Email address', 'Search', 'Phone number'.`,
1452
- run(t) {
1472
+ run(e) {
1453
1473
  var n;
1454
- const a = [], i = t.querySelectorAll(`input:not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]), textarea, select, ${ot}`);
1474
+ const a = [], i = e.querySelectorAll(`input:not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]), textarea, select, ${lt}`);
1455
1475
  for (const r of i) {
1456
1476
  if (g(r) || T(r) || (r instanceof HTMLInputElement || r instanceof HTMLTextAreaElement || r instanceof HTMLSelectElement || r instanceof HTMLButtonElement) && r.disabled || r.closest("fieldset[disabled]") || r.getAttribute("aria-disabled") === "true") continue;
1457
1477
  const o = (n = r.getAttribute("role")) == null ? void 0 : n.trim().toLowerCase();
1458
1478
  if (o === "presentation" || o === "none") continue;
1459
- if (!ct(r)) {
1460
- const l = [], h = r.tagName.toLowerCase(), c = r.getAttribute("type");
1461
- c && h === "input" && l.push(`type: ${c}`);
1479
+ if (!dt(r)) {
1480
+ const l = [], p = r.tagName.toLowerCase(), c = r.getAttribute("type");
1481
+ c && p === "input" && l.push(`type: ${c}`);
1462
1482
  const u = r.getAttribute("name");
1463
1483
  u && l.push(`name: "${u}"`);
1464
- const p = r.getAttribute("placeholder");
1465
- p && l.push(`placeholder: "${p}"`), o && l.push(`role: ${o}`);
1484
+ const h = r.getAttribute("placeholder");
1485
+ h && l.push(`placeholder: "${h}"`), o && l.push(`role: ${o}`);
1466
1486
  const b = r.getAttribute("id");
1467
1487
  b && l.push(`id: "${b}"`), a.push({
1468
1488
  ruleId: "label",
@@ -1476,7 +1496,7 @@ const ut = {
1476
1496
  }
1477
1497
  return a;
1478
1498
  }
1479
- }, dt = {
1499
+ }, ht = {
1480
1500
  id: "form-field-multiple-labels",
1481
1501
  wcag: [],
1482
1502
  level: "A",
@@ -1484,11 +1504,11 @@ const ut = {
1484
1504
  description: "Form fields should not have multiple label elements.",
1485
1505
  guidance: "When a form field has multiple <label> elements pointing to it, assistive technologies may announce only one label or behave inconsistently. Use a single <label> and combine any additional text into it, or use aria-describedby for supplementary information.",
1486
1506
  prompt: "Identify the multiple labels and recommend consolidating them into a single <label> element or using aria-describedby for supplementary text.",
1487
- run(t) {
1488
- const a = [], e = t.querySelectorAll('input:not([type="hidden"]), textarea, select');
1489
- for (const i of e) {
1507
+ run(e) {
1508
+ const a = [], t = e.querySelectorAll('input:not([type="hidden"]), textarea, select');
1509
+ for (const i of t) {
1490
1510
  if (g(i) || !i.id) continue;
1491
- const n = t.querySelectorAll(`label[for="${CSS.escape(i.id)}"]`);
1511
+ const n = e.querySelectorAll(`label[for="${CSS.escape(i.id)}"]`);
1492
1512
  let r = 0, o = i.parentElement;
1493
1513
  for (; o; ) {
1494
1514
  if (o.tagName.toLowerCase() === "label" && !o.hasAttribute("for")) {
@@ -1508,40 +1528,40 @@ const ut = {
1508
1528
  }
1509
1529
  return a;
1510
1530
  }
1511
- }, mt = {
1531
+ }, pt = {
1512
1532
  id: "select-name",
1513
1533
  wcag: ["4.1.2"],
1514
1534
  level: "A",
1515
1535
  description: "Select elements must have a programmatically associated label via <label>, aria-label, or aria-labelledby.",
1516
1536
  guidance: "Select dropdowns need labels so users understand what choice they're making. Use a <label> element with a for attribute matching the select's id, or wrap the select in a <label>. For selects without visible labels, use aria-label. The first <option> is not a substitute for a proper label.",
1517
1537
  prompt: "Based on the options or context, suggest a label element or aria-label describing what this select controls.",
1518
- run(t) {
1538
+ run(e) {
1519
1539
  const a = [];
1520
- for (const e of t.querySelectorAll("select"))
1521
- g(e) || v(e) || a.push({
1540
+ for (const t of e.querySelectorAll("select"))
1541
+ g(t) || v(t) || a.push({
1522
1542
  ruleId: "select-name",
1523
- selector: m(e),
1524
- html: d(e),
1543
+ selector: m(t),
1544
+ html: d(t),
1525
1545
  impact: "critical",
1526
1546
  message: "Select element has no accessible name."
1527
1547
  });
1528
1548
  return a;
1529
1549
  }
1530
- }, ht = {
1550
+ }, gt = {
1531
1551
  id: "input-button-name",
1532
1552
  wcag: ["4.1.2"],
1533
1553
  level: "A",
1534
1554
  description: "Input buttons must have discernible text via value, aria-label, or aria-labelledby.",
1535
1555
  guidance: "Input buttons (<input type='submit'>, type='button', type='reset'>) need accessible names so users know what action the button performs. Add a value attribute with descriptive text (e.g., value='Submit Form'), or use aria-label if the value must differ from the accessible name.",
1536
1556
  prompt: "Based on the input type and form context, suggest a value attribute describing the button's action.",
1537
- run(t) {
1538
- var e, i;
1557
+ run(e) {
1558
+ var t, i;
1539
1559
  const a = [];
1540
- for (const n of t.querySelectorAll(
1560
+ for (const n of e.querySelectorAll(
1541
1561
  'input[type="submit"], input[type="button"], input[type="reset"]'
1542
1562
  )) {
1543
1563
  if (g(n)) continue;
1544
- const r = (e = n.getAttribute("value")) == null ? void 0 : e.trim(), o = (i = n.getAttribute("type")) == null ? void 0 : i.toLowerCase(), s = (o === "submit" || o === "reset") && !n.hasAttribute("value");
1564
+ const r = (t = n.getAttribute("value")) == null ? void 0 : t.trim(), o = (i = n.getAttribute("type")) == null ? void 0 : i.toLowerCase(), s = (o === "submit" || o === "reset") && !n.hasAttribute("value");
1545
1565
  !r && !s && !v(n) && a.push({
1546
1566
  ruleId: "input-button-name",
1547
1567
  selector: m(n),
@@ -1552,7 +1572,7 @@ const ut = {
1552
1572
  }
1553
1573
  return a;
1554
1574
  }
1555
- }, pt = /* @__PURE__ */ new Set([
1575
+ }, bt = /* @__PURE__ */ new Set([
1556
1576
  "off",
1557
1577
  "on",
1558
1578
  "name",
@@ -1607,7 +1627,7 @@ const ut = {
1607
1627
  "impp",
1608
1628
  "url",
1609
1629
  "photo"
1610
- ]), gt = /* @__PURE__ */ new Set([
1630
+ ]), ft = /* @__PURE__ */ new Set([
1611
1631
  "tel",
1612
1632
  "tel-country-code",
1613
1633
  "tel-national",
@@ -1616,33 +1636,33 @@ const ut = {
1616
1636
  "tel-extension",
1617
1637
  "email",
1618
1638
  "impp"
1619
- ]), bt = /* @__PURE__ */ new Set(["home", "work", "mobile", "fax", "pager"]), ft = /* @__PURE__ */ new Set(["shipping", "billing"]), vt = /* @__PURE__ */ new Set(["webauthn"]);
1620
- function yt(t) {
1621
- const a = t.toLowerCase().split(/\s+/).filter(Boolean);
1639
+ ]), vt = /* @__PURE__ */ new Set(["home", "work", "mobile", "fax", "pager"]), wt = /* @__PURE__ */ new Set(["shipping", "billing"]), yt = /* @__PURE__ */ new Set(["webauthn"]);
1640
+ function At(e) {
1641
+ const a = e.toLowerCase().split(/\s+/).filter(Boolean);
1622
1642
  if (a.length === 0) return !0;
1623
- let e = 0;
1624
- a[e].startsWith("section-") && e++, e < a.length && ft.has(a[e]) && e++;
1643
+ let t = 0;
1644
+ a[t].startsWith("section-") && t++, t < a.length && wt.has(a[t]) && t++;
1625
1645
  let i = !1;
1626
- if (e < a.length && bt.has(a[e]) && (i = !0, e++), e >= a.length) return !1;
1627
- const n = a[e];
1628
- return !pt.has(n) || i && !gt.has(n) ? !1 : (e++, e < a.length && vt.has(a[e]) && e++, e === a.length);
1646
+ if (t < a.length && vt.has(a[t]) && (i = !0, t++), t >= a.length) return !1;
1647
+ const n = a[t];
1648
+ return !bt.has(n) || i && !ft.has(n) ? !1 : (t++, t < a.length && yt.has(a[t]) && t++, t === a.length);
1629
1649
  }
1630
- const wt = {
1650
+ const xt = {
1631
1651
  id: "autocomplete-valid",
1632
1652
  wcag: ["1.3.5"],
1633
1653
  level: "AA",
1634
1654
  description: "Autocomplete attribute must use valid values from the HTML specification.",
1635
1655
  guidance: "The autocomplete attribute helps users fill forms by identifying input purposes. Use standard values like 'name', 'email', 'tel', 'street-address', 'postal-code', 'cc-number'. This benefits users with cognitive disabilities, motor impairments, and anyone using password managers or autofill. Check the HTML specification for the complete list of valid tokens.",
1636
1656
  prompt: "Show the invalid autocomplete value and suggest the correct standard value based on the input's apparent purpose.",
1637
- run(t) {
1657
+ run(e) {
1638
1658
  const a = [];
1639
- for (const e of t.querySelectorAll("[autocomplete]")) {
1640
- if (g(e) || e instanceof HTMLElement && e.style.display === "none" || e.disabled || e.getAttribute("aria-disabled") === "true") continue;
1641
- const i = e.getAttribute("autocomplete").trim();
1642
- i && (yt(i) || a.push({
1659
+ for (const t of e.querySelectorAll("[autocomplete]")) {
1660
+ if (g(t) || t instanceof HTMLElement && t.style.display === "none" || t.disabled || t.getAttribute("aria-disabled") === "true") continue;
1661
+ const i = t.getAttribute("autocomplete").trim();
1662
+ i && (At(i) || a.push({
1643
1663
  ruleId: "autocomplete-valid",
1644
- selector: m(e),
1645
- html: d(e),
1664
+ selector: m(t),
1665
+ html: d(t),
1646
1666
  impact: "serious",
1647
1667
  message: `Invalid autocomplete value "${i}".`
1648
1668
  }));
@@ -1650,22 +1670,22 @@ const wt = {
1650
1670
  return a;
1651
1671
  }
1652
1672
  };
1653
- function ee(t) {
1654
- return t.toLowerCase().replace(/\s+/g, " ").trim();
1673
+ function ee(e) {
1674
+ return e.toLowerCase().replace(/\s+/g, " ").trim();
1655
1675
  }
1656
- function te(t, a) {
1657
- const e = ee(t), i = ee(a);
1658
- if (!e || !i || e.includes(i) || i.includes(e)) return !0;
1676
+ function te(e, a) {
1677
+ const t = ee(e), i = ee(a);
1678
+ if (!t || !i || t.includes(i) || i.includes(t)) return !0;
1659
1679
  const n = i.split(/\s+/).map((r) => r.replace(/[.,;:!?\u2026]+$/g, "")).filter((r) => r.length > 2);
1660
- return n.length >= 2 && n.filter((o) => e.includes(o)).length / n.length > 0.5;
1680
+ return n.length >= 2 && n.filter((o) => t.includes(o)).length / n.length > 0.5;
1661
1681
  }
1662
- function z(t) {
1682
+ function z(e) {
1663
1683
  let a = "";
1664
- for (const e of t.childNodes)
1665
- if (e.nodeType === 3)
1666
- a += e.textContent ?? "";
1667
- else if (e.nodeType === 1) {
1668
- const i = e, n = i.tagName.toLowerCase();
1684
+ for (const t of e.childNodes)
1685
+ if (t.nodeType === 3)
1686
+ a += t.textContent ?? "";
1687
+ else if (t.nodeType === 1) {
1688
+ const i = t, n = i.tagName.toLowerCase();
1669
1689
  if (n === "style" || n === "script" || n === "svg" || i.getAttribute("aria-hidden") === "true" || i instanceof HTMLElement && i.style.display === "none") continue;
1670
1690
  const r = i.getAttribute("role");
1671
1691
  if (r === "img" || r === "presentation" || r === "none") continue;
@@ -1673,7 +1693,7 @@ function z(t) {
1673
1693
  }
1674
1694
  return a;
1675
1695
  }
1676
- const At = {
1696
+ const St = {
1677
1697
  id: "label-content-name-mismatch",
1678
1698
  wcag: [],
1679
1699
  level: "A",
@@ -1681,46 +1701,46 @@ const At = {
1681
1701
  description: "Interactive elements with visible text must have accessible names that contain that text.",
1682
1702
  guidance: "For voice control users who activate controls by speaking their visible label, the accessible name must include the visible text. If aria-label is 'Submit form' but the button shows 'Send', voice users saying 'click Send' won't activate it. Ensure aria-label/aria-labelledby contains or matches the visible text.",
1683
1703
  prompt: "Show the mismatch between the visible text and accessible name, and suggest updating aria-label to include the visible text.",
1684
- run(t) {
1704
+ run(e) {
1685
1705
  const a = [];
1686
- for (const e of t.querySelectorAll('button, [role="button"], a[href], input[type="submit"], input[type="button"]')) {
1687
- if (g(e)) continue;
1688
- const i = v(e);
1706
+ for (const t of e.querySelectorAll('button, [role="button"], a[href], input[type="submit"], input[type="button"]')) {
1707
+ if (g(t)) continue;
1708
+ const i = v(t);
1689
1709
  if (!i) continue;
1690
1710
  let n = "";
1691
- e instanceof HTMLInputElement ? n = e.value || "" : n = z(e);
1711
+ t instanceof HTMLInputElement ? n = t.value || "" : n = z(t);
1692
1712
  const r = n.trim();
1693
1713
  if (!r || r.length <= 2) continue;
1694
- const o = e.hasAttribute("aria-label"), s = e.hasAttribute("aria-labelledby");
1714
+ const o = t.hasAttribute("aria-label"), s = t.hasAttribute("aria-labelledby");
1695
1715
  !o && !s || te(i, n) || a.push({
1696
1716
  ruleId: "label-content-name-mismatch",
1697
- selector: m(e),
1698
- html: d(e),
1717
+ selector: m(t),
1718
+ html: d(t),
1699
1719
  impact: "serious",
1700
1720
  message: `Accessible name "${i}" does not contain visible text "${n.trim()}".`
1701
1721
  });
1702
1722
  }
1703
- for (const e of t.querySelectorAll("input, select, textarea")) {
1704
- if (g(e) || e instanceof HTMLInputElement && ["hidden", "submit", "button", "image"].includes(e.type)) continue;
1705
- const i = v(e);
1706
- if (!i || !e.hasAttribute("aria-label")) continue;
1707
- const r = e.id;
1723
+ for (const t of e.querySelectorAll("input, select, textarea")) {
1724
+ if (g(t) || t instanceof HTMLInputElement && ["hidden", "submit", "button", "image"].includes(t.type)) continue;
1725
+ const i = v(t);
1726
+ if (!i || !t.hasAttribute("aria-label")) continue;
1727
+ const r = t.id;
1708
1728
  let o = "";
1709
1729
  if (r) {
1710
- const s = t.querySelector(`label[for="${CSS.escape(r)}"]`);
1730
+ const s = e.querySelector(`label[for="${CSS.escape(r)}"]`);
1711
1731
  s && (o = z(s));
1712
1732
  }
1713
1733
  o.trim() && (te(i, o) || a.push({
1714
1734
  ruleId: "label-content-name-mismatch",
1715
- selector: m(e),
1716
- html: d(e),
1735
+ selector: m(t),
1736
+ html: d(t),
1717
1737
  impact: "serious",
1718
1738
  message: `Accessible name "${i}" does not contain visible label "${o.trim()}".`
1719
1739
  }));
1720
1740
  }
1721
1741
  return a;
1722
1742
  }
1723
- }, xt = {
1743
+ }, kt = {
1724
1744
  id: "label-title-only",
1725
1745
  wcag: [],
1726
1746
  level: "A",
@@ -1728,22 +1748,22 @@ const At = {
1728
1748
  description: "Form elements should not use title attribute as the only accessible name.",
1729
1749
  guidance: "The title attribute is unreliable as a label because it only appears on hover/focus (not visible to touch users) and is often ignored by assistive technologies. Use a visible <label> element, aria-label, or aria-labelledby instead. Title can supplement a label but should not replace it.",
1730
1750
  prompt: "The title attribute text should be moved to a visible <label> element or aria-label. Show what text to use based on the current title.",
1731
- run(t) {
1751
+ run(e) {
1732
1752
  var i, n, r, o;
1733
- const a = [], e = t.querySelectorAll(
1753
+ const a = [], t = e.querySelectorAll(
1734
1754
  'input:not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]), textarea, select'
1735
1755
  );
1736
- for (const s of e) {
1756
+ for (const s of t) {
1737
1757
  if (g(s)) continue;
1738
- const l = s.hasAttribute("title") && ((i = s.getAttribute("title")) == null ? void 0 : i.trim()), h = s.hasAttribute("aria-label") && ((n = s.getAttribute("aria-label")) == null ? void 0 : n.trim()), c = s.hasAttribute("aria-labelledby");
1758
+ const l = s.hasAttribute("title") && ((i = s.getAttribute("title")) == null ? void 0 : i.trim()), p = s.hasAttribute("aria-label") && ((n = s.getAttribute("aria-label")) == null ? void 0 : n.trim()), c = s.hasAttribute("aria-labelledby");
1739
1759
  let u = !1;
1740
- const p = s.id;
1741
- if (p) {
1742
- const f = s.ownerDocument.querySelector(`label[for="${CSS.escape(p)}"]`);
1760
+ const h = s.id;
1761
+ if (h) {
1762
+ const f = s.ownerDocument.querySelector(`label[for="${CSS.escape(h)}"]`);
1743
1763
  (r = f == null ? void 0 : f.textContent) != null && r.trim() && (u = !0);
1744
1764
  }
1745
1765
  const b = s.closest("label");
1746
- (o = b == null ? void 0 : b.textContent) != null && o.trim() && (u = !0), l && !h && !c && !u && a.push({
1766
+ (o = b == null ? void 0 : b.textContent) != null && o.trim() && (u = !0), l && !p && !c && !u && a.push({
1747
1767
  ruleId: "label-title-only",
1748
1768
  selector: m(s),
1749
1769
  html: d(s),
@@ -1753,7 +1773,7 @@ const At = {
1753
1773
  }
1754
1774
  return a;
1755
1775
  }
1756
- }, St = {
1776
+ }, It = {
1757
1777
  id: "tabindex",
1758
1778
  selector: "[tabindex]",
1759
1779
  check: { type: "attribute-value", attribute: "tabindex", operator: ">", value: 0 },
@@ -1765,7 +1785,7 @@ const At = {
1765
1785
  tags: ["best-practice"],
1766
1786
  guidance: "Positive tabindex values force elements to the front of the tab order regardless of DOM position, creating unpredictable navigation for keyboard users. Use tabindex='0' to add elements to the natural tab order, or tabindex='-1' to make elements programmatically focusable but not in tab order. Rely on DOM order for tab sequence.",
1767
1787
  prompt: "Change the positive tabindex value to tabindex='0' and rely on DOM order for tab sequence instead."
1768
- }, kt = k(St), It = /* @__PURE__ */ new Set([
1788
+ }, Tt = k(It), Et = /* @__PURE__ */ new Set([
1769
1789
  "div",
1770
1790
  "span",
1771
1791
  "p",
@@ -1792,7 +1812,7 @@ const At = {
1792
1812
  "tr",
1793
1813
  "td",
1794
1814
  "th"
1795
- ]), Tt = {
1815
+ ]), Ct = {
1796
1816
  id: "focus-order-semantics",
1797
1817
  wcag: [],
1798
1818
  tags: ["best-practice"],
@@ -1800,22 +1820,22 @@ const At = {
1800
1820
  description: "Elements that receive keyboard focus must have an appropriate role so assistive technologies can convey their purpose. Non-interactive elements with tabindex='0' need a valid interactive ARIA role.",
1801
1821
  guidance: "When adding tabindex='0' to non-interactive elements like <div> or <span>, screen readers announce them generically. Add an appropriate role (button, link, tab, etc.) so users understand the element's purpose. Also add keyboard event handlers (Enter/Space for buttons, Enter for links). Consider using native interactive elements instead.",
1802
1822
  prompt: "Based on the element's apparent purpose, suggest adding an appropriate role attribute (button, link, etc.) or converting to a native interactive element.",
1803
- run(t) {
1823
+ run(e) {
1804
1824
  const a = [];
1805
- for (const e of t.querySelectorAll('[tabindex="0"]')) {
1806
- const i = e.tagName.toLowerCase();
1807
- if (!It.has(i)) continue;
1808
- e.getAttribute("role") || a.push({
1825
+ for (const t of e.querySelectorAll('[tabindex="0"]')) {
1826
+ const i = t.tagName.toLowerCase();
1827
+ if (!Et.has(i)) continue;
1828
+ t.getAttribute("role") || a.push({
1809
1829
  ruleId: "focus-order-semantics",
1810
- selector: m(e),
1811
- html: d(e),
1830
+ selector: m(t),
1831
+ html: d(t),
1812
1832
  impact: "moderate",
1813
1833
  message: `Non-interactive <${i}> with tabindex="0" has no interactive role.`
1814
1834
  });
1815
1835
  }
1816
1836
  return a;
1817
1837
  }
1818
- }, Et = /* @__PURE__ */ new Set([
1838
+ }, Lt = /* @__PURE__ */ new Set([
1819
1839
  "a",
1820
1840
  "audio",
1821
1841
  "button",
@@ -1824,7 +1844,7 @@ const At = {
1824
1844
  "select",
1825
1845
  "textarea",
1826
1846
  "video"
1827
- ]), Ct = /* @__PURE__ */ new Set([
1847
+ ]), qt = /* @__PURE__ */ new Set([
1828
1848
  "button",
1829
1849
  "checkbox",
1830
1850
  "combobox",
@@ -1848,7 +1868,7 @@ const At = {
1848
1868
  "tabpanel",
1849
1869
  "textbox",
1850
1870
  "treeitem"
1851
- ]), Lt = {
1871
+ ]), Rt = {
1852
1872
  grid: /* @__PURE__ */ new Set(["gridcell", "row", "columnheader", "rowheader"]),
1853
1873
  listbox: /* @__PURE__ */ new Set(["option"]),
1854
1874
  menu: /* @__PURE__ */ new Set(["menuitem", "menuitemcheckbox", "menuitemradio"]),
@@ -1858,44 +1878,44 @@ const At = {
1858
1878
  tree: /* @__PURE__ */ new Set(["treeitem"]),
1859
1879
  treegrid: /* @__PURE__ */ new Set(["gridcell", "row", "columnheader", "rowheader", "treeitem"])
1860
1880
  };
1861
- function qt(t, a) {
1881
+ function Nt(e, a) {
1862
1882
  var n, r, o;
1863
- const e = (n = t.getAttribute("role")) == null ? void 0 : n.toLowerCase(), i = (r = a.getAttribute("role")) == null ? void 0 : r.toLowerCase();
1864
- return !e || !i ? !1 : ((o = Lt[e]) == null ? void 0 : o.has(i)) ?? !1;
1883
+ const t = (n = e.getAttribute("role")) == null ? void 0 : n.toLowerCase(), i = (r = a.getAttribute("role")) == null ? void 0 : r.toLowerCase();
1884
+ return !t || !i ? !1 : ((o = Rt[t]) == null ? void 0 : o.has(i)) ?? !1;
1865
1885
  }
1866
- function Rt(t) {
1886
+ function $t(e) {
1867
1887
  var n;
1868
- const a = t.tagName.toLowerCase();
1869
- if (Et.has(a))
1870
- return a === "a" && !t.hasAttribute("href") ? !1 : a === "audio" || a === "video" ? t.hasAttribute("controls") : !(a === "img" && !t.hasAttribute("usemap") || a === "input" && t.type === "hidden" || t.disabled);
1871
- const e = (n = t.getAttribute("role")) == null ? void 0 : n.toLowerCase();
1872
- if (e && Ct.has(e)) return !0;
1873
- const i = t.getAttribute("tabindex");
1874
- return i !== null && i !== "-1" || t.getAttribute("contenteditable") === "true";
1888
+ const a = e.tagName.toLowerCase();
1889
+ if (Lt.has(a))
1890
+ return a === "a" && !e.hasAttribute("href") ? !1 : a === "audio" || a === "video" ? e.hasAttribute("controls") : !(a === "img" && !e.hasAttribute("usemap") || a === "input" && e.type === "hidden" || e.disabled);
1891
+ const t = (n = e.getAttribute("role")) == null ? void 0 : n.toLowerCase();
1892
+ if (t && qt.has(t)) return !0;
1893
+ const i = e.getAttribute("tabindex");
1894
+ return i !== null && i !== "-1" || e.getAttribute("contenteditable") === "true";
1875
1895
  }
1876
- function Nt(t) {
1877
- const a = t.tagName.toLowerCase();
1878
- return !!(a === "a" && t.hasAttribute("href") || a === "button" && !t.disabled);
1896
+ function Mt(e) {
1897
+ const a = e.tagName.toLowerCase();
1898
+ return !!(a === "a" && e.hasAttribute("href") || a === "button" && !e.disabled);
1879
1899
  }
1880
- const $t = {
1900
+ const Ht = {
1881
1901
  id: "nested-interactive",
1882
1902
  wcag: ["4.1.2"],
1883
1903
  level: "A",
1884
1904
  description: "Interactive controls must not be nested inside each other.",
1885
1905
  guidance: "Nesting interactive elements (like a button inside a link, or a link inside a button) creates unpredictable behavior and confuses assistive technologies. The browser may remove the inner element from the accessibility tree. Restructure the HTML so interactive elements are siblings, not nested. If you need a clickable card, use CSS and JavaScript rather than nesting.",
1886
1906
  prompt: "Identify which elements are nested and suggest restructuring them as siblings instead.",
1887
- run(t) {
1888
- const a = [], e = t.body ?? t;
1889
- if (!e) return a;
1890
- const n = (t.body ? t : t.ownerDocument).createTreeWalker(e, NodeFilter.SHOW_ELEMENT), r = [];
1907
+ run(e) {
1908
+ const a = [], t = e.body ?? e;
1909
+ if (!t) return a;
1910
+ const n = (e.body ? e : e.ownerDocument).createTreeWalker(t, NodeFilter.SHOW_ELEMENT), r = [];
1891
1911
  let o = n.currentNode;
1892
1912
  for (; o; ) {
1893
1913
  for (; r.length > 0 && !r[r.length - 1].contains(o); )
1894
1914
  r.pop();
1895
- if (!g(o) && Rt(o)) {
1915
+ if (!g(o) && $t(o)) {
1896
1916
  if (r.length > 0) {
1897
1917
  const s = r[r.length - 1];
1898
- qt(s, o) || a.push({
1918
+ Nt(s, o) || a.push({
1899
1919
  ruleId: "nested-interactive",
1900
1920
  selector: m(o),
1901
1921
  html: d(o),
@@ -1903,35 +1923,35 @@ const $t = {
1903
1923
  message: `Interactive element <${o.tagName.toLowerCase()}> is nested inside <${s.tagName.toLowerCase()}>.`
1904
1924
  });
1905
1925
  }
1906
- Nt(o) && r.push(o);
1926
+ Mt(o) && r.push(o);
1907
1927
  }
1908
1928
  o = n.nextNode();
1909
1929
  }
1910
1930
  return a;
1911
1931
  }
1912
- }, Mt = {
1932
+ }, Dt = {
1913
1933
  id: "scrollable-region-focusable",
1914
1934
  wcag: ["2.1.1"],
1915
1935
  level: "A",
1916
1936
  description: "Scrollable regions must be keyboard accessible.",
1917
1937
  guidance: "Content that scrolls must be accessible to keyboard users. If a region has overflow:scroll or overflow:auto and contains scrollable content, it needs either tabindex='0' to be focusable, or it must contain focusable elements. Without this, keyboard users cannot scroll the content.",
1918
1938
  prompt: "Explain how to make this scrollable region keyboard accessible.",
1919
- run(t) {
1920
- var e;
1939
+ run(e) {
1940
+ var t;
1921
1941
  const a = [];
1922
- for (const i of t.querySelectorAll("*")) {
1942
+ for (const i of e.querySelectorAll("*")) {
1923
1943
  if (g(i) || !(i instanceof HTMLElement)) continue;
1924
1944
  const n = i.tagName.toLowerCase();
1925
1945
  if (n === "body" || n === "html") continue;
1926
1946
  const r = i.getAttribute("role");
1927
1947
  if (r === "presentation" || r === "none") continue;
1928
- const o = w(i), s = o.overflowX, l = o.overflowY;
1948
+ const o = y(i), s = o.overflowX, l = o.overflowY;
1929
1949
  if (!(s === "scroll" || s === "auto" || l === "scroll" || l === "auto")) continue;
1930
1950
  if (i.scrollHeight > 0 || i.clientHeight > 0) {
1931
1951
  const b = i.scrollHeight - i.clientHeight, f = i.scrollWidth - i.clientWidth;
1932
- if (b <= 0 && f <= 0 || b < 36 && f < 36 || i.clientWidth < 64 && i.clientHeight < 64) continue;
1952
+ if (b <= 0 && f <= 0 || b < 14 && f < 14 || i.clientWidth < 64 && i.clientHeight < 64) continue;
1933
1953
  } else {
1934
- const b = o.height !== "" || o.maxHeight !== "", f = ((e = i.textContent) == null ? void 0 : e.trim().length) ?? 0;
1954
+ const b = o.height !== "" || o.maxHeight !== "", f = ((t = i.textContent) == null ? void 0 : t.trim().length) ?? 0;
1935
1955
  if (!b || f <= 50) continue;
1936
1956
  }
1937
1957
  const u = i.getAttribute("tabindex");
@@ -1947,7 +1967,7 @@ const $t = {
1947
1967
  }
1948
1968
  return a;
1949
1969
  }
1950
- }, Ht = {
1970
+ }, Ft = {
1951
1971
  id: "accesskeys",
1952
1972
  wcag: [],
1953
1973
  level: "A",
@@ -1955,17 +1975,17 @@ const $t = {
1955
1975
  description: "Accesskey attribute values must be unique.",
1956
1976
  guidance: "When multiple elements share the same accesskey, browser behavior becomes unpredictable - usually only the first element is activated. Ensure each accesskey value is unique within the page. Also consider that accesskeys can conflict with browser and screen reader shortcuts, so use them sparingly.",
1957
1977
  prompt: "Suggest removing or changing this duplicate accesskey to a unique value.",
1958
- run(t) {
1978
+ run(e) {
1959
1979
  var i;
1960
- const a = [], e = /* @__PURE__ */ new Map();
1961
- for (const n of t.querySelectorAll("[accesskey]")) {
1980
+ const a = [], t = /* @__PURE__ */ new Map();
1981
+ for (const n of e.querySelectorAll("[accesskey]")) {
1962
1982
  if (g(n)) continue;
1963
1983
  const r = (i = n.getAttribute("accesskey")) == null ? void 0 : i.trim().toLowerCase();
1964
1984
  if (!r) continue;
1965
- const o = e.get(r) || [];
1966
- o.push(n), e.set(r, o);
1985
+ const o = t.get(r) || [];
1986
+ o.push(n), t.set(r, o);
1967
1987
  }
1968
- for (const [n, r] of e)
1988
+ for (const [n, r] of t)
1969
1989
  if (r.length > 1)
1970
1990
  for (const o of r.slice(1))
1971
1991
  a.push({
@@ -1977,7 +1997,7 @@ const $t = {
1977
1997
  });
1978
1998
  return a;
1979
1999
  }
1980
- }, Dt = {
2000
+ }, Wt = {
1981
2001
  id: "heading-order",
1982
2002
  wcag: [],
1983
2003
  level: "A",
@@ -1985,10 +2005,10 @@ const $t = {
1985
2005
  description: "Heading levels should increase by one; skipping levels (e.g. h2 to h4) makes navigation harder.",
1986
2006
  guidance: "Screen reader users navigate by headings to understand page structure. Skipping levels (h2 to h4) suggests missing content and creates confusion. Start with h1 for the page title, then use h2 for main sections, h3 for subsections, etc. You can go back up (h3 to h2) when starting a new section.",
1987
2007
  prompt: "State which heading level was expected and suggest changing this heading to the appropriate level.",
1988
- run(t) {
1989
- const a = [], e = t.querySelectorAll("h1, h2, h3, h4, h5, h6, [role='heading']");
2008
+ run(e) {
2009
+ const a = [], t = e.querySelectorAll("h1, h2, h3, h4, h5, h6, [role='heading']");
1990
2010
  let i = 0, n = null;
1991
- for (const r of e) {
2011
+ for (const r of t) {
1992
2012
  if (g(r)) continue;
1993
2013
  let o;
1994
2014
  r.hasAttribute("aria-level") ? o = parseInt(r.getAttribute("aria-level"), 10) : o = parseInt(r.tagName[1], 10), i > 0 && o > i + 1 && a.push({
@@ -2010,18 +2030,18 @@ const $t = {
2010
2030
  description: "Page should have exactly one main landmark.",
2011
2031
  guidance: "The main landmark contains the primary content of the page. Screen readers allow users to jump directly to main content. Use a single <main> element (or role='main') to wrap the central content, excluding headers, footers, and navigation.",
2012
2032
  prompt: "Identify the primary content area and explain how to wrap it in a <main> element.",
2013
- run(t) {
2014
- const a = t.querySelectorAll('main, [role="main"]');
2033
+ run(e) {
2034
+ const a = e.querySelectorAll('main, [role="main"]');
2015
2035
  return a.length === 0 ? [{
2016
2036
  ruleId: "landmark-one-main",
2017
2037
  selector: "html",
2018
2038
  html: "<html>",
2019
2039
  impact: "moderate",
2020
2040
  message: "Page has no main landmark."
2021
- }] : a.length > 1 ? Array.from(a).slice(1).map((e) => ({
2041
+ }] : a.length > 1 ? Array.from(a).slice(1).map((t) => ({
2022
2042
  ruleId: "landmark-one-main",
2023
- selector: m(e),
2024
- html: d(e),
2043
+ selector: m(t),
2044
+ html: d(t),
2025
2045
  impact: "moderate",
2026
2046
  message: "Page has multiple main landmarks."
2027
2047
  })) : [];
@@ -2034,8 +2054,8 @@ const $t = {
2034
2054
  description: "Page should not have more than one banner landmark.",
2035
2055
  guidance: "The banner landmark (typically <header>) identifies site-oriented content like logos and search. Only one top-level banner is allowed per page. If you need multiple headers, nest them inside sectioning elements (article, section, aside) where they become scoped headers rather than page-level banners.",
2036
2056
  prompt: "Explain whether to remove this duplicate banner or nest it inside a sectioning element.",
2037
- run(t) {
2038
- const a = [], e = t.querySelectorAll('header, [role="banner"]'), i = Array.from(e).filter((n) => !n.closest($));
2057
+ run(e) {
2058
+ const a = [], t = e.querySelectorAll('header, [role="banner"]'), i = Array.from(t).filter((n) => !n.closest($));
2039
2059
  return i.length > 1 && i.slice(1).forEach(
2040
2060
  (n) => a.push({
2041
2061
  ruleId: "landmark-no-duplicate-banner",
@@ -2046,7 +2066,7 @@ const $t = {
2046
2066
  })
2047
2067
  ), a;
2048
2068
  }
2049
- }, Ft = {
2069
+ }, _t = {
2050
2070
  id: "landmark-no-duplicate-contentinfo",
2051
2071
  wcag: [],
2052
2072
  level: "A",
@@ -2054,8 +2074,8 @@ const $t = {
2054
2074
  description: "Page should not have more than one contentinfo landmark.",
2055
2075
  guidance: "The contentinfo landmark (typically <footer>) contains information about the page like copyright and contact info. Only one top-level contentinfo is allowed per page. Nest additional footers inside sectioning elements to scope them.",
2056
2076
  prompt: "Explain whether to remove this duplicate footer or nest it inside a sectioning element.",
2057
- run(t) {
2058
- const a = [], e = t.querySelectorAll('footer, [role="contentinfo"]'), i = Array.from(e).filter((n) => !n.closest($));
2077
+ run(e) {
2078
+ const a = [], t = e.querySelectorAll('footer, [role="contentinfo"]'), i = Array.from(t).filter((n) => !n.closest($));
2059
2079
  return i.length > 1 && i.slice(1).forEach(
2060
2080
  (n) => a.push({
2061
2081
  ruleId: "landmark-no-duplicate-contentinfo",
@@ -2066,7 +2086,7 @@ const $t = {
2066
2086
  })
2067
2087
  ), a;
2068
2088
  }
2069
- }, Wt = {
2089
+ }, Pt = {
2070
2090
  id: "landmark-no-duplicate-main",
2071
2091
  wcag: [],
2072
2092
  level: "A",
@@ -2074,9 +2094,9 @@ const $t = {
2074
2094
  description: "Page should not have more than one main landmark.",
2075
2095
  guidance: "Only one main landmark should exist per page. The main landmark identifies the primary content area. If you have multiple content sections, use <section> with appropriate headings instead of multiple main elements.",
2076
2096
  prompt: "Explain which main landmark to keep and how to restructure the duplicate.",
2077
- run(t) {
2078
- const a = [], e = t.querySelectorAll('main, [role="main"]');
2079
- return e.length > 1 && Array.from(e).slice(1).forEach(
2097
+ run(e) {
2098
+ const a = [], t = e.querySelectorAll('main, [role="main"]');
2099
+ return t.length > 1 && Array.from(t).slice(1).forEach(
2080
2100
  (i) => a.push({
2081
2101
  ruleId: "landmark-no-duplicate-main",
2082
2102
  selector: m(i),
@@ -2086,7 +2106,7 @@ const $t = {
2086
2106
  })
2087
2107
  ), a;
2088
2108
  }
2089
- }, _t = {
2109
+ }, jt = {
2090
2110
  id: "landmark-banner-is-top-level",
2091
2111
  wcag: [],
2092
2112
  level: "A",
@@ -2094,9 +2114,9 @@ const $t = {
2094
2114
  description: "Banner landmark should not be nested within another landmark.",
2095
2115
  guidance: "The banner landmark should be a top-level landmark, not nested inside article, aside, main, nav, or section. If a header is inside these elements, it automatically becomes a generic header rather than a banner. Remove explicit role='banner' from nested headers or restructure the page.",
2096
2116
  prompt: "Explain why this banner is incorrectly nested and how to fix it.",
2097
- run(t) {
2098
- const a = [], e = t.querySelectorAll('[role="banner"]');
2099
- for (const i of e)
2117
+ run(e) {
2118
+ const a = [], t = e.querySelectorAll('[role="banner"]');
2119
+ for (const i of t)
2100
2120
  i.closest($) && a.push({
2101
2121
  ruleId: "landmark-banner-is-top-level",
2102
2122
  selector: m(i),
@@ -2106,7 +2126,7 @@ const $t = {
2106
2126
  });
2107
2127
  return a;
2108
2128
  }
2109
- }, Pt = {
2129
+ }, Vt = {
2110
2130
  id: "landmark-contentinfo-is-top-level",
2111
2131
  wcag: [],
2112
2132
  level: "A",
@@ -2114,9 +2134,9 @@ const $t = {
2114
2134
  description: "Contentinfo landmark should not be nested within another landmark.",
2115
2135
  guidance: "The contentinfo landmark should be a top-level landmark. A footer inside article, aside, main, nav, or section becomes a scoped footer, not a contentinfo landmark. Remove explicit role='contentinfo' from nested footers or move the footer outside sectioning elements.",
2116
2136
  prompt: "Explain why this contentinfo is incorrectly nested and how to fix it.",
2117
- run(t) {
2118
- const a = [], e = t.querySelectorAll('[role="contentinfo"]');
2119
- for (const i of e)
2137
+ run(e) {
2138
+ const a = [], t = e.querySelectorAll('[role="contentinfo"]');
2139
+ for (const i of t)
2120
2140
  i.closest($) && a.push({
2121
2141
  ruleId: "landmark-contentinfo-is-top-level",
2122
2142
  selector: m(i),
@@ -2126,7 +2146,7 @@ const $t = {
2126
2146
  });
2127
2147
  return a;
2128
2148
  }
2129
- }, jt = {
2149
+ }, zt = {
2130
2150
  id: "landmark-main-is-top-level",
2131
2151
  wcag: [],
2132
2152
  level: "A",
@@ -2134,9 +2154,9 @@ const $t = {
2134
2154
  description: "Main landmark should not be nested within another landmark.",
2135
2155
  guidance: "The main landmark must be a top-level landmark since it represents the primary content of the page. Do not nest <main> or role='main' inside article, aside, nav, or section elements.",
2136
2156
  prompt: "Explain why the main landmark must be top-level and where to move it.",
2137
- run(t) {
2138
- const a = [], e = t.querySelectorAll('main, [role="main"]');
2139
- for (const i of e) {
2157
+ run(e) {
2158
+ const a = [], t = e.querySelectorAll('main, [role="main"]');
2159
+ for (const i of t) {
2140
2160
  const n = i.parentElement;
2141
2161
  n != null && n.closest('article, aside, nav, section, [role="article"], [role="complementary"], [role="navigation"], [role="region"]') && a.push({
2142
2162
  ruleId: "landmark-main-is-top-level",
@@ -2148,7 +2168,7 @@ const $t = {
2148
2168
  }
2149
2169
  return a;
2150
2170
  }
2151
- }, Vt = {
2171
+ }, Ut = {
2152
2172
  id: "landmark-complementary-is-top-level",
2153
2173
  wcag: [],
2154
2174
  level: "A",
@@ -2156,9 +2176,9 @@ const $t = {
2156
2176
  description: "Aside (complementary) landmark should be top-level or directly inside main.",
2157
2177
  guidance: "The complementary landmark (aside) should be top-level or a direct child of main. Nesting aside deep within other landmarks reduces its discoverability for screen reader users navigating by landmarks.",
2158
2178
  prompt: "Explain why this aside should be repositioned and suggest where to move it.",
2159
- run(t) {
2160
- const a = [], e = t.querySelectorAll('aside, [role="complementary"]');
2161
- for (const i of e) {
2179
+ run(e) {
2180
+ const a = [], t = e.querySelectorAll('aside, [role="complementary"]');
2181
+ for (const i of t) {
2162
2182
  const n = i.parentElement;
2163
2183
  n && !n.matches('body, main, [role="main"]') && i.closest('article, nav, section, [role="article"], [role="navigation"], [role="region"]') && a.push({
2164
2184
  ruleId: "landmark-complementary-is-top-level",
@@ -2170,7 +2190,7 @@ const $t = {
2170
2190
  }
2171
2191
  return a;
2172
2192
  }
2173
- }, zt = {
2193
+ }, Gt = {
2174
2194
  id: "landmark-unique",
2175
2195
  wcag: [],
2176
2196
  level: "A",
@@ -2178,35 +2198,35 @@ const $t = {
2178
2198
  description: "Landmarks should have unique labels when there are multiple of the same type.",
2179
2199
  guidance: "When a page has multiple landmarks of the same type (e.g., multiple nav elements), each should have a unique accessible name via aria-label or aria-labelledby. This helps screen reader users distinguish between them (e.g., 'Main navigation' vs 'Footer navigation').",
2180
2200
  prompt: "Suggest a unique aria-label that distinguishes this landmark based on its purpose.",
2181
- run(t) {
2182
- const a = [], e = [
2201
+ run(e) {
2202
+ const a = [], t = [
2183
2203
  { selector: 'nav, [role="navigation"]', type: "navigation" },
2184
2204
  { selector: 'aside, [role="complementary"]', type: "complementary" },
2185
2205
  { selector: 'section[aria-label], section[aria-labelledby], [role="region"]', type: "region" },
2186
2206
  { selector: 'form[aria-label], form[aria-labelledby], [role="form"], [role="search"]', type: "form" }
2187
2207
  ];
2188
- for (const { selector: i, type: n } of e) {
2189
- const r = Array.from(t.querySelectorAll(i)).filter((s) => !g(s));
2208
+ for (const { selector: i, type: n } of t) {
2209
+ const r = Array.from(e.querySelectorAll(i)).filter((s) => !g(s));
2190
2210
  if (r.length <= 1) continue;
2191
2211
  const o = /* @__PURE__ */ new Map();
2192
2212
  for (const s of r) {
2193
- const l = v(s).toLowerCase() || "", h = o.get(l) || [];
2194
- h.push(s), o.set(l, h);
2213
+ const l = v(s).toLowerCase() || "", p = o.get(l) || [];
2214
+ p.push(s), o.set(l, p);
2195
2215
  }
2196
2216
  for (const [s, l] of o)
2197
2217
  if (l.length > 1)
2198
- for (const h of l.slice(1))
2218
+ for (const p of l.slice(1))
2199
2219
  a.push({
2200
2220
  ruleId: "landmark-unique",
2201
- selector: m(h),
2202
- html: d(h),
2221
+ selector: m(p),
2222
+ html: d(p),
2203
2223
  impact: "moderate",
2204
2224
  message: s ? `Multiple ${n} landmarks have the same label "${s}".` : `Multiple ${n} landmarks have no label. Add unique aria-label attributes.`
2205
2225
  });
2206
2226
  }
2207
2227
  return a;
2208
2228
  }
2209
- }, Ut = {
2229
+ }, Yt = {
2210
2230
  id: "region",
2211
2231
  wcag: [],
2212
2232
  level: "A",
@@ -2214,11 +2234,11 @@ const $t = {
2214
2234
  description: "All page content should be contained within landmarks.",
2215
2235
  guidance: "Screen reader users navigate pages by landmarks. Content outside landmarks is harder to find and understand. Wrap all visible content in appropriate landmarks: <header>, <nav>, <main>, <aside>, <footer>, or <section> with a label. Skip links may exist outside landmarks.",
2216
2236
  prompt: "Based on the content, suggest which landmark element would be most appropriate.",
2217
- run(t) {
2237
+ run(e) {
2218
2238
  var i;
2219
- const a = [], e = t.body;
2220
- if (!e) return [];
2221
- for (const n of e.children) {
2239
+ const a = [], t = e.body;
2240
+ if (!t) return [];
2241
+ for (const n of t.children) {
2222
2242
  if (g(n) || n instanceof HTMLScriptElement || n instanceof HTMLStyleElement || n.tagName === "NOSCRIPT" || n instanceof HTMLElement && n.hidden || n.matches('a[href^="#"]')) continue;
2223
2243
  const r = n.matches(ae), o = (i = n.textContent) == null ? void 0 : i.trim();
2224
2244
  !r && o && (n.querySelector(ae) || a.push({
@@ -2231,7 +2251,7 @@ const $t = {
2231
2251
  }
2232
2252
  return a;
2233
2253
  }
2234
- }, Gt = {
2254
+ }, Xt = {
2235
2255
  id: "list",
2236
2256
  selector: "ul, ol",
2237
2257
  check: { type: "child-invalid", allowedChildren: ["li", "script", "template"], allowedChildRoles: ["listitem"] },
@@ -2242,26 +2262,26 @@ const $t = {
2242
2262
  level: "A",
2243
2263
  guidance: "Screen readers announce list structure ('list with 5 items') based on proper markup. Placing non-<li> elements directly inside <ul> or <ol> breaks this structure. Wrap content in <li> elements, or if you need wrapper divs for styling, restructure your CSS to style the <li> elements directly.",
2244
2264
  prompt: "Explain how to restructure this element within the list properly."
2245
- }, Yt = k(Gt), Xt = {
2265
+ }, Kt = k(Xt), Jt = {
2246
2266
  id: "dlitem",
2247
2267
  wcag: ["1.3.1"],
2248
2268
  level: "A",
2249
2269
  description: "<dt> and <dd> elements must be contained in a <dl>.",
2250
2270
  guidance: "Definition terms (<dt>) and definitions (<dd>) only have semantic meaning inside a definition list (<dl>). Outside of <dl>, they're treated as generic text. Wrap related <dt> and <dd> pairs in a <dl> element to convey the term/definition relationship to assistive technologies.",
2251
2271
  prompt: "Explain how to properly structure this term/definition content.",
2252
- run(t) {
2272
+ run(e) {
2253
2273
  const a = [];
2254
- for (const e of t.querySelectorAll("dt, dd"))
2255
- (!e.parentElement || e.parentElement.tagName.toLowerCase() !== "dl") && a.push({
2274
+ for (const t of e.querySelectorAll("dt, dd"))
2275
+ (!t.parentElement || t.parentElement.tagName.toLowerCase() !== "dl") && a.push({
2256
2276
  ruleId: "dlitem",
2257
- selector: m(e),
2258
- html: d(e),
2277
+ selector: m(t),
2278
+ html: d(t),
2259
2279
  impact: "serious",
2260
- message: `<${e.tagName.toLowerCase()}> is not contained in a <dl>.`
2280
+ message: `<${t.tagName.toLowerCase()}> is not contained in a <dl>.`
2261
2281
  });
2262
2282
  return a;
2263
2283
  }
2264
- }, Kt = {
2284
+ }, Qt = {
2265
2285
  id: "definition-list",
2266
2286
  selector: "dl",
2267
2287
  check: { type: "child-invalid", allowedChildren: ["dt", "dd", "div", "script", "template"] },
@@ -2272,22 +2292,22 @@ const $t = {
2272
2292
  level: "A",
2273
2293
  guidance: "Definition lists have strict content requirements. Only <dt> (terms), <dd> (definitions), and <div> (for grouping dt/dd pairs) are valid children. Other elements break the list structure for screen readers. Move invalid elements outside the <dl>, or restructure using proper definition list markup.",
2274
2294
  prompt: "Explain whether to move this element outside the <dl> or convert it to dt/dd."
2275
- }, Jt = k(Kt), Qt = {
2295
+ }, Zt = k(Qt), ea = {
2276
2296
  id: "listitem",
2277
2297
  wcag: ["1.3.1"],
2278
2298
  level: "A",
2279
2299
  description: "<li> elements must be contained in a <ul>, <ol>, or <menu>.",
2280
2300
  guidance: "List items (<li>) only have semantic meaning inside a list container (<ul>, <ol>, or <menu>). Outside of these containers, assistive technologies cannot convey the list relationship. Wrap <li> elements in the appropriate list container.",
2281
2301
  prompt: "Explain that this <li> must be placed inside a <ul>, <ol>, or <menu> element.",
2282
- run(t) {
2283
- var e;
2302
+ run(e) {
2303
+ var t;
2284
2304
  const a = [];
2285
- for (const i of t.querySelectorAll("li")) {
2305
+ for (const i of e.querySelectorAll("li")) {
2286
2306
  if (g(i)) continue;
2287
2307
  const n = i.parentElement;
2288
2308
  if (!n) continue;
2289
2309
  const r = n.tagName.toLowerCase();
2290
- r === "ul" || r === "ol" || r === "menu" || ((e = n.getAttribute("role")) == null ? void 0 : e.trim().toLowerCase()) === "list" || a.push({
2310
+ r === "ul" || r === "ol" || r === "menu" || ((t = n.getAttribute("role")) == null ? void 0 : t.trim().toLowerCase()) === "list" || a.push({
2291
2311
  ruleId: "listitem",
2292
2312
  selector: m(i),
2293
2313
  html: d(i),
@@ -2297,23 +2317,23 @@ const $t = {
2297
2317
  }
2298
2318
  return a;
2299
2319
  }
2300
- }, Zt = {
2320
+ }, ta = {
2301
2321
  id: "document-title",
2302
2322
  wcag: ["2.4.2"],
2303
2323
  level: "A",
2304
2324
  description: "Documents must have a <title> element to provide users with an overview of content.",
2305
2325
  guidance: "Screen reader users rely on page titles to identify and navigate between tabs/windows. Add a descriptive <title> element in <head> that summarizes the page purpose. Keep titles unique across the site, placing specific content before the site name (e.g., 'Contact Us - Acme Corp').",
2306
2326
  prompt: "The page has no title or an empty title. Suggest a concise, descriptive <title> based on the page content sample in context. Good titles are specific and front-load the unique part: 'Product Details - Store Name' rather than 'Store Name - Product Details'.",
2307
- run(t) {
2308
- var e, i, n;
2309
- const a = t.querySelector("title");
2310
- if (!a || !((e = a.textContent) != null && e.trim())) {
2327
+ run(e) {
2328
+ var t, i, n;
2329
+ const a = e.querySelector("title");
2330
+ if (!a || !((t = a.textContent) != null && t.trim())) {
2311
2331
  let r;
2312
- const o = t.querySelector("h1");
2332
+ const o = e.querySelector("h1");
2313
2333
  if ((i = o == null ? void 0 : o.textContent) != null && i.trim())
2314
2334
  r = `h1: "${o.textContent.trim().slice(0, 100)}"`;
2315
- else if (t.body) {
2316
- const s = ((n = t.body.textContent) == null ? void 0 : n.trim().replace(/\s+/g, " ")) || "";
2335
+ else if (e.body) {
2336
+ const s = ((n = e.body.textContent) == null ? void 0 : n.trim().replace(/\s+/g, " ")) || "";
2317
2337
  s && (r = `Page text: "${s.slice(0, 150)}"`);
2318
2338
  }
2319
2339
  return [{
@@ -2327,7 +2347,7 @@ const $t = {
2327
2347
  }
2328
2348
  return [];
2329
2349
  }
2330
- }, ea = {
2350
+ }, aa = {
2331
2351
  id: "bypass",
2332
2352
  wcag: [],
2333
2353
  level: "A",
@@ -2335,19 +2355,19 @@ const $t = {
2335
2355
  description: "Page must have a mechanism to bypass repeated blocks of content.",
2336
2356
  guidance: 'Keyboard users must be able to skip repetitive content like navigation. Provide a skip link at the top of the page that links to the main content (e.g., <a href="#main">Skip to main content</a>), or use a <main> landmark. Screen readers can jump directly to landmarks, so a properly marked-up <main> element satisfies this requirement.',
2337
2357
  prompt: 'The page has no mechanism for keyboard users to skip repeated content. The simplest fix is to wrap the primary content area in a <main> element — screen readers can jump directly to it. Alternatively, add a skip link as the first element in <body>: <a href="#main" class="skip-link">Skip to main content</a>, with a matching id on the target element. Use the context to understand what the page is missing.',
2338
- run(t) {
2339
- if (t.querySelector(
2358
+ run(e) {
2359
+ if (e.querySelector(
2340
2360
  'main, [role="main"], nav, [role="navigation"], aside, [role="complementary"], header, [role="banner"], footer, [role="contentinfo"], [role="search"], [role="region"]'
2341
2361
  )) return [];
2342
- const e = t.querySelector('a[href^="#"]');
2343
- if (e) {
2344
- const r = e.getAttribute("href");
2362
+ const t = e.querySelector('a[href^="#"]');
2363
+ if (t) {
2364
+ const r = t.getAttribute("href");
2345
2365
  if (r && r.length > 1) {
2346
2366
  const o = r.slice(1);
2347
- if (t.getElementById(o)) return [];
2367
+ if (e.getElementById(o)) return [];
2348
2368
  }
2349
2369
  }
2350
- if (t.querySelector("h1, h2, h3, [role='heading']")) return [];
2370
+ if (e.querySelector("h1, h2, h3, [role='heading']")) return [];
2351
2371
  const n = [];
2352
2372
  return n.push("no landmarks (<main>, <nav>, <header>, <footer>)"), n.push("no skip link"), n.push("no headings"), [{
2353
2373
  ruleId: "bypass",
@@ -2358,7 +2378,7 @@ const $t = {
2358
2378
  context: `Missing: ${n.join(", ")}`
2359
2379
  }];
2360
2380
  }
2361
- }, ta = {
2381
+ }, ia = {
2362
2382
  id: "page-has-heading-one",
2363
2383
  wcag: [],
2364
2384
  level: "A",
@@ -2366,19 +2386,19 @@ const $t = {
2366
2386
  description: "Page should contain a level-one heading.",
2367
2387
  guidance: "A level-one heading (<h1> or role='heading' with aria-level='1') helps users understand the page topic and provides a landmark for screen reader navigation. Each page should have exactly one h1 that describes the main content, typically matching or similar to the page title.",
2368
2388
  prompt: "The page has no <h1> heading. Suggest appropriate h1 text based on the page title or content sample in context. The h1 should describe the page's main topic and typically be placed at the start of the main content area.",
2369
- run(t) {
2389
+ run(e) {
2370
2390
  var o, s, l;
2371
- const a = t.querySelector("h1");
2391
+ const a = e.querySelector("h1");
2372
2392
  if (a && v(a)) return [];
2373
- const e = t.querySelectorAll('[role="heading"][aria-level="1"]');
2374
- for (const h of e)
2375
- if (v(h)) return [];
2376
- const i = [], n = (s = (o = t.querySelector("title")) == null ? void 0 : o.textContent) == null ? void 0 : s.trim();
2393
+ const t = e.querySelectorAll('[role="heading"][aria-level="1"]');
2394
+ for (const p of t)
2395
+ if (v(p)) return [];
2396
+ const i = [], n = (s = (o = e.querySelector("title")) == null ? void 0 : o.textContent) == null ? void 0 : s.trim();
2377
2397
  n && i.push(`Page title: "${n}"`);
2378
- const r = t.querySelector("main");
2398
+ const r = e.querySelector("main");
2379
2399
  if (r) {
2380
- const h = ((l = r.textContent) == null ? void 0 : l.trim().replace(/\s+/g, " ")) || "";
2381
- h && i.push(`Main content: "${h.slice(0, 100)}"`);
2400
+ const p = ((l = r.textContent) == null ? void 0 : l.trim().replace(/\s+/g, " ")) || "";
2401
+ p && i.push(`Main content: "${p.slice(0, 100)}"`);
2382
2402
  }
2383
2403
  return [{
2384
2404
  ruleId: "page-has-heading-one",
@@ -2390,29 +2410,29 @@ const $t = {
2390
2410
  }];
2391
2411
  }
2392
2412
  };
2393
- function he(t) {
2394
- if (!(t instanceof HTMLElement)) return !1;
2395
- if (t.style.display === "none" || t.style.visibility === "hidden") return !0;
2396
- const a = t.getAttribute("width"), e = t.getAttribute("height");
2397
- return (a === "0" || a === "1") && (e === "0" || e === "1");
2413
+ function be(e) {
2414
+ if (!(e instanceof HTMLElement)) return !1;
2415
+ if (e.style.display === "none" || e.style.visibility === "hidden") return !0;
2416
+ const a = e.getAttribute("width"), t = e.getAttribute("height");
2417
+ return (a === "0" || a === "1") && (t === "0" || t === "1");
2398
2418
  }
2399
- const aa = {
2419
+ const na = {
2400
2420
  id: "frame-title",
2401
2421
  wcag: ["4.1.2"],
2402
2422
  level: "A",
2403
2423
  description: "Frames must have an accessible name.",
2404
2424
  guidance: "Screen readers announce frame titles when users navigate frames. Add a title attribute to <iframe> and <frame> elements that describes the frame's purpose (e.g., <iframe title='Video player'>). Avoid generic titles like 'frame' or 'iframe'. If the frame is decorative, use aria-hidden='true'.",
2405
2425
  prompt: "This iframe has no accessible name. Based on the src URL in context, suggest a descriptive title attribute that tells screen reader users what the frame contains. For example: 'YouTube video player', 'Google Map', 'Payment form', 'Chat widget'. If the frame appears decorative or non-essential, recommend adding aria-hidden='true' instead.",
2406
- run(t) {
2426
+ run(e) {
2407
2427
  const a = [];
2408
- for (const e of t.querySelectorAll("iframe, frame")) {
2409
- if (g(e) || he(e)) continue;
2410
- if (!v(e)) {
2411
- const n = e.getAttribute("src");
2428
+ for (const t of e.querySelectorAll("iframe, frame")) {
2429
+ if (g(t) || be(t)) continue;
2430
+ if (!v(t)) {
2431
+ const n = t.getAttribute("src");
2412
2432
  a.push({
2413
2433
  ruleId: "frame-title",
2414
- selector: m(e),
2415
- html: d(e),
2434
+ selector: m(t),
2435
+ html: d(t),
2416
2436
  impact: "serious",
2417
2437
  message: "Frame is missing an accessible name. Add a title attribute.",
2418
2438
  context: n ? `src: "${n}"` : void 0
@@ -2421,7 +2441,7 @@ const aa = {
2421
2441
  }
2422
2442
  return a;
2423
2443
  }
2424
- }, ia = {
2444
+ }, ra = {
2425
2445
  id: "frame-title-unique",
2426
2446
  wcag: ["4.1.2"],
2427
2447
  level: "A",
@@ -2429,11 +2449,11 @@ const aa = {
2429
2449
  description: "Frame titles should be unique.",
2430
2450
  guidance: "When multiple frames have identical titles, screen reader users cannot distinguish between them. Give each frame a unique, descriptive title that explains its specific purpose or content.",
2431
2451
  prompt: "Suggest a more specific title to distinguish this frame from others.",
2432
- run(t) {
2452
+ run(e) {
2433
2453
  var n;
2434
- const a = [], e = Array.from(t.querySelectorAll("iframe[title], frame[title]")), i = /* @__PURE__ */ new Map();
2435
- for (const r of e) {
2436
- if (g(r) || he(r)) continue;
2454
+ const a = [], t = Array.from(e.querySelectorAll("iframe[title], frame[title]")), i = /* @__PURE__ */ new Map();
2455
+ for (const r of t) {
2456
+ if (g(r) || be(r)) continue;
2437
2457
  const o = (n = r.getAttribute("title")) == null ? void 0 : n.trim().toLowerCase();
2438
2458
  if (o) {
2439
2459
  const s = i.get(o) || [];
@@ -2452,7 +2472,7 @@ const aa = {
2452
2472
  });
2453
2473
  return a;
2454
2474
  }
2455
- }, na = {
2475
+ }, oa = {
2456
2476
  id: "empty-heading",
2457
2477
  wcag: [],
2458
2478
  level: "A",
@@ -2460,10 +2480,10 @@ const aa = {
2460
2480
  description: "Headings must have discernible text.",
2461
2481
  guidance: "Screen reader users navigate pages by headings, so empty headings create confusing navigation points. Ensure all headings contain visible text or accessible names. If a heading is used purely for visual styling, use CSS instead of heading elements.",
2462
2482
  prompt: "This heading element has no text content, so screen reader users encounter a blank heading when navigating. Either add descriptive text that summarizes the following section, or if this element is used only for visual styling, replace it with a styled <p> or <div> and use CSS for appearance. The context includes nearby content to help suggest appropriate heading text.",
2463
- run(t) {
2483
+ run(e) {
2464
2484
  var i;
2465
- const a = [], e = t.querySelectorAll('h1, h2, h3, h4, h5, h6, [role="heading"]');
2466
- for (const n of e)
2485
+ const a = [], t = e.querySelectorAll('h1, h2, h3, h4, h5, h6, [role="heading"]');
2486
+ for (const n of t)
2467
2487
  if (!g(n) && !v(n)) {
2468
2488
  let r;
2469
2489
  const o = n.nextElementSibling;
@@ -2482,52 +2502,55 @@ const aa = {
2482
2502
  }
2483
2503
  return a;
2484
2504
  }
2485
- }, ra = {
2505
+ }, sa = {
2486
2506
  id: "meta-viewport",
2487
2507
  wcag: ["1.4.4"],
2488
2508
  level: "AA",
2489
2509
  description: "Viewport meta tag must not disable user scaling.",
2490
2510
  guidance: "Users with low vision need to zoom content up to 200% or more. Setting user-scalable=no or maximum-scale=1 prevents zooming and fails WCAG. Remove these restrictions. If your layout breaks at high zoom, fix the responsive design rather than preventing zoom.",
2491
2511
  prompt: "The viewport meta tag restricts zooming, which prevents low-vision users from enlarging content. Show the current content attribute and a corrected version with the problematic properties removed. Keep other viewport properties (like width=device-width, initial-scale=1) intact — only remove user-scalable=no and maximum-scale restrictions.",
2492
- run(t) {
2493
- const a = [], e = t.querySelector('meta[name="viewport"]');
2494
- if (!e) return [];
2495
- const i = e.getAttribute("content") || "", n = i.toLowerCase();
2496
- (/user-scalable\s*=\s*no/i.test(n) || /user-scalable\s*=\s*0/i.test(n)) && a.push({
2497
- ruleId: "meta-viewport",
2498
- selector: m(e),
2499
- html: d(e),
2500
- impact: "critical",
2501
- message: "Viewport disables user scaling. Remove user-scalable=no.",
2502
- context: `content: "${i}"`
2503
- });
2504
- const r = n.match(/maximum-scale\s*=\s*([\d.]+|yes)/i);
2512
+ run(e) {
2513
+ const a = [], t = e.querySelector('meta[name="viewport"]');
2514
+ if (!t) return [];
2515
+ const i = t.getAttribute("content") || "", n = i.toLowerCase(), r = n.match(/user-scalable\s*=\s*([^\s,;]+)/i);
2505
2516
  if (r) {
2506
- const o = r[1], s = o.toLowerCase() === "yes" ? 1 : parseFloat(o);
2507
- s < 2 && a.push({
2517
+ const s = r[1], l = parseFloat(s);
2518
+ (s === "no" || !isNaN(l) && l > -1 && l < 1) && a.push({
2519
+ ruleId: "meta-viewport",
2520
+ selector: m(t),
2521
+ html: d(t),
2522
+ impact: "critical",
2523
+ message: `Viewport disables user scaling (user-scalable=${s}). Remove this restriction.`,
2524
+ context: `content: "${i}"`
2525
+ });
2526
+ }
2527
+ const o = n.match(/maximum-scale\s*=\s*([\d.]+|yes)/i);
2528
+ if (o) {
2529
+ const s = o[1], l = s.toLowerCase() === "yes" ? 1 : parseFloat(s);
2530
+ l < 2 && a.push({
2508
2531
  ruleId: "meta-viewport",
2509
- selector: m(e),
2510
- html: d(e),
2532
+ selector: m(t),
2533
+ html: d(t),
2511
2534
  impact: "critical",
2512
- message: `Viewport maximum-scale=${s} restricts zooming. Set to at least 2 or remove.`,
2535
+ message: `Viewport maximum-scale=${l} restricts zooming. Set to at least 2 or remove.`,
2513
2536
  context: `content: "${i}"`
2514
2537
  });
2515
2538
  }
2516
2539
  return a;
2517
2540
  }
2518
- }, oa = {
2541
+ }, la = {
2519
2542
  id: "meta-refresh",
2520
2543
  wcag: ["2.2.1", "2.2.4", "3.2.5"],
2521
2544
  level: "A",
2522
2545
  description: "Meta refresh must not redirect or refresh automatically.",
2523
2546
  guidance: "Automatic page refreshes or redirects can disorient users, especially those using screen readers or with cognitive disabilities. They may lose their place or not have time to read content. If a redirect is needed, use a server-side redirect (HTTP 301/302) instead. For timed refreshes, provide user controls.",
2524
2547
  prompt: "Explain why meta refresh is problematic and suggest server-side alternatives.",
2525
- run(t) {
2526
- for (const a of t.querySelectorAll('meta[http-equiv="refresh"]')) {
2527
- const e = a.getAttribute("content") || "", i = e.match(/^(\d+)/);
2548
+ run(e) {
2549
+ for (const a of e.querySelectorAll('meta[http-equiv="refresh"]')) {
2550
+ const t = a.getAttribute("content") || "", i = t.match(/^(\d+)/);
2528
2551
  if (!i) continue;
2529
2552
  const n = parseInt(i[1], 10);
2530
- if (/^\d+\s*[;,]\s*url\s*=/i.test(e) || /^\d+\s*[;,]\s*['"]?\s*https?:/i.test(e))
2553
+ if (/^\d+\s*[;,]\s*url\s*=/i.test(t) || /^\d+\s*[;,]\s*['"]?\s*https?:/i.test(t))
2531
2554
  return n > 0 && n <= 72e3 ? [{
2532
2555
  ruleId: "meta-refresh",
2533
2556
  selector: m(a),
@@ -2546,7 +2569,7 @@ const aa = {
2546
2569
  }
2547
2570
  return [];
2548
2571
  }
2549
- }, sa = {
2572
+ }, ca = {
2550
2573
  id: "blink",
2551
2574
  selector: "blink",
2552
2575
  check: { type: "selector-exists" },
@@ -2557,7 +2580,7 @@ const aa = {
2557
2580
  level: "A",
2558
2581
  guidance: "Blinking content can cause seizures in users with photosensitive epilepsy and is distracting for users with attention disorders. The <blink> element is deprecated and should never be used. If you need to draw attention to content, use less intrusive methods like color, borders, or icons.",
2559
2582
  prompt: "Suggest static alternatives to the blinking effect."
2560
- }, la = k(sa), ca = {
2583
+ }, ua = k(ca), da = {
2561
2584
  id: "marquee",
2562
2585
  selector: "marquee",
2563
2586
  check: { type: "selector-exists" },
@@ -2568,7 +2591,7 @@ const aa = {
2568
2591
  level: "A",
2569
2592
  guidance: "Scrolling or moving content is difficult for many users to read, especially those with cognitive or visual disabilities. The <marquee> element is deprecated. Replace scrolling text with static content. If content must scroll, provide pause/stop controls and ensure it stops after 5 seconds.",
2570
2593
  prompt: "Suggest static alternatives or accessible carousel patterns."
2571
- }, ua = k(ca), da = {
2594
+ }, ma = k(da), ha = {
2572
2595
  id: "p-as-heading",
2573
2596
  wcag: [],
2574
2597
  level: "A",
@@ -2576,15 +2599,15 @@ const aa = {
2576
2599
  description: "Paragraphs should not be styled to look like headings.",
2577
2600
  guidance: "When paragraphs are styled with bold, large fonts to look like headings, screen reader users miss the semantic structure. Use proper heading elements (h1-h6) instead of styled paragraphs. If you need specific styling, apply CSS to the heading elements while maintaining proper heading hierarchy.",
2578
2601
  prompt: "Suggest the appropriate heading level based on the document structure.",
2579
- run(t) {
2580
- var e, i;
2602
+ run(e) {
2603
+ var t, i;
2581
2604
  const a = [];
2582
- for (const n of t.querySelectorAll("p")) {
2605
+ for (const n of e.querySelectorAll("p")) {
2583
2606
  if (g(n)) continue;
2584
- const r = n.getAttribute("style") || "", o = /font-weight\s*:\s*(bold|[6-9]00)/i.test(r), s = /font-size\s*:\s*(\d+)\s*(px|em|rem)/i.test(r), l = ((e = n.className) == null ? void 0 : e.toLowerCase()) || "", h = /\bh[1-6]\b|\bheading\b/.test(l), c = ((i = n.textContent) == null ? void 0 : i.trim()) || "", u = c.length > 0 && c.length < 50, p = !c.match(/[.!?,;:]$/);
2585
- if ((o && s || o && h) && u && p) {
2586
- const y = n.nextElementSibling;
2587
- y && (y.tagName === "P" || y.tagName === "DIV" || y.tagName === "UL") && a.push({
2607
+ const r = n.getAttribute("style") || "", o = /font-weight\s*:\s*(bold|[6-9]00)/i.test(r), s = /font-size\s*:\s*(\d+)\s*(px|em|rem)/i.test(r), l = ((t = n.className) == null ? void 0 : t.toLowerCase()) || "", p = /\bh[1-6]\b|\bheading\b/.test(l), c = ((i = n.textContent) == null ? void 0 : i.trim()) || "", u = c.length > 0 && c.length < 50, h = !c.match(/[.!?,;:]$/);
2608
+ if ((o && s || o && p) && u && h) {
2609
+ const w = n.nextElementSibling;
2610
+ w && (w.tagName === "P" || w.tagName === "DIV" || w.tagName === "UL") && a.push({
2588
2611
  ruleId: "p-as-heading",
2589
2612
  selector: m(n),
2590
2613
  html: d(n),
@@ -2595,48 +2618,48 @@ const aa = {
2595
2618
  }
2596
2619
  return a;
2597
2620
  }
2598
- }, ma = {
2621
+ }, pa = {
2599
2622
  id: "aria-roles",
2600
2623
  wcag: ["4.1.2"],
2601
2624
  level: "A",
2602
2625
  description: "ARIA role values must be valid.",
2603
2626
  guidance: "Invalid role values are ignored by assistive technologies, meaning the element will not have the intended semantics. Check the spelling and use only roles defined in the WAI-ARIA specification. Common roles include: button, link, navigation, main, dialog, alert, tab, tabpanel, menu, menuitem.",
2604
2627
  prompt: "Identify the invalid role and suggest the correct spelling or a valid alternative role that matches the intended purpose.",
2605
- run(t) {
2628
+ run(e) {
2606
2629
  const a = [];
2607
- for (const e of t.querySelectorAll("[role]")) {
2608
- const r = e.getAttribute("role").replace(/[\u201C\u201D\u2018\u2019\u00AB\u00BB]/g, "").split(/\s+/).filter(Boolean);
2609
- !r.some((s) => ke(s)) && r.length > 0 && a.push({
2630
+ for (const t of e.querySelectorAll("[role]")) {
2631
+ const r = t.getAttribute("role").replace(/[\u201C\u201D\u2018\u2019\u00AB\u00BB]/g, "").split(/\s+/).filter(Boolean);
2632
+ !r.some((s) => Ee(s)) && r.length > 0 && a.push({
2610
2633
  ruleId: "aria-roles",
2611
- selector: m(e),
2612
- html: d(e),
2634
+ selector: m(t),
2635
+ html: d(t),
2613
2636
  impact: "critical",
2614
2637
  message: `Invalid ARIA role "${r[0]}".`
2615
2638
  });
2616
2639
  }
2617
2640
  return a;
2618
2641
  }
2619
- }, ha = {
2642
+ }, ga = {
2620
2643
  id: "aria-valid-attr",
2621
2644
  wcag: ["4.1.2"],
2622
2645
  level: "A",
2623
2646
  description: "ARIA attributes must be valid (correctly spelled).",
2624
2647
  guidance: "Misspelled ARIA attributes are ignored by assistive technologies. Check the spelling against the WAI-ARIA specification. Common mistakes: aria-labeledby (should be aria-labelledby), aria-role (should be role), aria-description (valid in ARIA 1.3+).",
2625
2648
  prompt: "Identify the misspelled attribute and provide the correct spelling.",
2626
- run(t) {
2627
- return U(t).validAttr;
2649
+ run(e) {
2650
+ return U(e).validAttr;
2628
2651
  }
2629
- }, pa = {
2652
+ }, ba = {
2630
2653
  id: "aria-valid-attr-value",
2631
2654
  wcag: ["4.1.2"],
2632
2655
  level: "A",
2633
2656
  description: "ARIA attributes must have valid values.",
2634
2657
  guidance: "Each ARIA attribute accepts specific value types. Boolean attributes (aria-hidden, aria-disabled) accept only 'true' or 'false'. Tristate attributes (aria-checked, aria-pressed) also accept 'mixed'. Token attributes (aria-live, aria-autocomplete) accept predefined values. ID reference attributes (aria-labelledby, aria-describedby) must reference existing element IDs.",
2635
2658
  prompt: "Show the invalid value and list the valid values for this specific attribute.",
2636
- run(t) {
2637
- return U(t).validAttrValue;
2659
+ run(e) {
2660
+ return U(e).validAttrValue;
2638
2661
  }
2639
- }, ga = {
2662
+ }, fa = {
2640
2663
  checkbox: ["aria-checked"],
2641
2664
  combobox: ["aria-expanded"],
2642
2665
  heading: ["aria-level"],
@@ -2650,30 +2673,30 @@ const aa = {
2650
2673
  slider: ["aria-valuenow"],
2651
2674
  spinbutton: ["aria-valuenow"],
2652
2675
  switch: ["aria-checked"]
2653
- }, ba = {
2676
+ }, va = {
2654
2677
  id: "aria-required-attr",
2655
2678
  wcag: ["4.1.2"],
2656
2679
  level: "A",
2657
2680
  description: "Elements with ARIA roles must have all required ARIA attributes.",
2658
2681
  guidance: "Some ARIA roles require specific attributes to function correctly. For example, checkbox requires aria-checked, slider requires aria-valuenow, heading requires aria-level. Without these attributes, assistive technologies cannot convey the element's state or value to users. Add the missing required attribute with an appropriate value.",
2659
2682
  prompt: "State which attribute is required for this role and suggest an appropriate value based on the element's apparent state.",
2660
- run(t) {
2683
+ run(e) {
2661
2684
  const a = [];
2662
- for (const e of t.querySelectorAll("[role]")) {
2663
- if (g(e) || e instanceof HTMLElement && e.style.display === "none") continue;
2664
- const i = e.getAttribute("role").trim().toLowerCase(), n = ga[i];
2665
- if (n && !(i === "checkbox" && e instanceof HTMLInputElement && e.type === "checkbox") && !(i === "radio" && e instanceof HTMLInputElement && e.type === "radio") && !(i === "option" && e instanceof HTMLOptionElement) && !(i === "heading" && /^h[1-6]$/i.test(e.tagName))) {
2685
+ for (const t of e.querySelectorAll("[role]")) {
2686
+ if (g(t) || t instanceof HTMLElement && t.style.display === "none") continue;
2687
+ const i = t.getAttribute("role").trim().toLowerCase(), n = fa[i];
2688
+ if (n && !(i === "checkbox" && t instanceof HTMLInputElement && t.type === "checkbox") && !(i === "radio" && t instanceof HTMLInputElement && t.type === "radio") && !(i === "option" && t instanceof HTMLOptionElement) && !(i === "heading" && /^h[1-6]$/i.test(t.tagName))) {
2666
2689
  if (i === "separator") {
2667
- const r = e.getAttribute("tabindex");
2690
+ const r = t.getAttribute("tabindex");
2668
2691
  if (!r || r === "-1") continue;
2669
2692
  }
2670
- if (!(e.tagName.toLowerCase() === "hr" && !e.hasAttribute("role"))) {
2693
+ if (!(t.tagName.toLowerCase() === "hr" && !t.hasAttribute("role"))) {
2671
2694
  for (const r of n)
2672
- if (!e.hasAttribute(r)) {
2695
+ if (!t.hasAttribute(r)) {
2673
2696
  a.push({
2674
2697
  ruleId: "aria-required-attr",
2675
- selector: m(e),
2676
- html: d(e),
2698
+ selector: m(t),
2699
+ html: d(t),
2677
2700
  impact: "critical",
2678
2701
  message: `Role "${i}" requires attribute "${r}".`
2679
2702
  });
@@ -2685,16 +2708,16 @@ const aa = {
2685
2708
  return a;
2686
2709
  }
2687
2710
  };
2688
- function fa(t) {
2711
+ function wa(e) {
2689
2712
  var r, o, s;
2690
- const a = [], e = t.className;
2691
- e && typeof e == "string" && e.trim() && a.push(`Classes: ${e.trim().slice(0, 100)}`);
2692
- const i = t.closest("form");
2713
+ const a = [], t = e.className;
2714
+ t && typeof t == "string" && t.trim() && a.push(`Classes: ${t.trim().slice(0, 100)}`);
2715
+ const i = e.closest("form");
2693
2716
  if (i) {
2694
2717
  const l = i.getAttribute("aria-label") || ((o = (r = i.querySelector("legend")) == null ? void 0 : r.textContent) == null ? void 0 : o.trim());
2695
2718
  l && a.push(`Form: ${l.slice(0, 60)}`);
2696
2719
  }
2697
- const n = t.parentElement;
2720
+ const n = e.parentElement;
2698
2721
  if (n) {
2699
2722
  const l = n.closest("h1, h2, h3, h4, h5, h6") || n.querySelector("h1, h2, h3, h4, h5, h6");
2700
2723
  (s = l == null ? void 0 : l.textContent) != null && s.trim() && a.push(`Nearby heading: ${l.textContent.trim().slice(0, 60)}`);
@@ -2702,31 +2725,31 @@ function fa(t) {
2702
2725
  return a.length > 0 ? a.join(`
2703
2726
  `) : void 0;
2704
2727
  }
2705
- const va = {
2728
+ const ya = {
2706
2729
  id: "button-name",
2707
2730
  wcag: ["4.1.2"],
2708
2731
  level: "A",
2709
2732
  description: "Buttons must have discernible text.",
2710
2733
  guidance: "Screen reader users need to know what a button does. Add visible text content, aria-label, or aria-labelledby. For icon buttons, use aria-label describing the action (e.g., aria-label='Close'). If the button contains an image, ensure the image has alt text describing the button's action.",
2711
2734
  prompt: "Based on the button's content, class, or context, suggest an appropriate aria-label describing the action it performs.",
2712
- run(t) {
2735
+ run(e) {
2713
2736
  const a = [];
2714
- for (const e of t.querySelectorAll('button, [role="button"]')) {
2715
- if (g(e) || T(e)) continue;
2716
- const i = e.getAttribute("role");
2717
- if ((i === "none" || i === "presentation") && !(e.matches('button:not([disabled]), [tabindex]:not([tabindex="-1"])') || e.tagName.toLowerCase() === "button" && !e.disabled) || e.getRootNode() instanceof ShadowRoot) continue;
2718
- v(e) || a.push({
2737
+ for (const t of e.querySelectorAll('button, [role="button"]')) {
2738
+ if (g(t) || T(t)) continue;
2739
+ const i = t.getAttribute("role");
2740
+ if ((i === "none" || i === "presentation") && !(t.matches('button:not([disabled]), [tabindex]:not([tabindex="-1"])') || t.tagName.toLowerCase() === "button" && !t.disabled) || t.getRootNode() instanceof ShadowRoot) continue;
2741
+ v(t) || a.push({
2719
2742
  ruleId: "button-name",
2720
- selector: m(e),
2721
- html: d(e),
2743
+ selector: m(t),
2744
+ html: d(t),
2722
2745
  impact: "critical",
2723
2746
  message: "Button has no discernible text.",
2724
- context: fa(e)
2747
+ context: wa(t)
2725
2748
  });
2726
2749
  }
2727
2750
  return a;
2728
2751
  }
2729
- }, ya = {
2752
+ }, Aa = {
2730
2753
  alert: /* @__PURE__ */ new Set(["aria-atomic", "aria-busy", "aria-live", "aria-relevant"]),
2731
2754
  alertdialog: /* @__PURE__ */ new Set(["aria-describedby", "aria-modal"]),
2732
2755
  application: /* @__PURE__ */ new Set(["aria-activedescendant", "aria-disabled", "aria-errormessage", "aria-expanded", "aria-haspopup", "aria-invalid"]),
@@ -2798,7 +2821,7 @@ const va = {
2798
2821
  tree: /* @__PURE__ */ new Set(["aria-activedescendant", "aria-disabled", "aria-errormessage", "aria-invalid", "aria-multiselectable", "aria-orientation", "aria-required"]),
2799
2822
  treegrid: /* @__PURE__ */ new Set(["aria-activedescendant", "aria-colcount", "aria-disabled", "aria-errormessage", "aria-invalid", "aria-multiselectable", "aria-orientation", "aria-readonly", "aria-required", "aria-rowcount"]),
2800
2823
  treeitem: /* @__PURE__ */ new Set(["aria-checked", "aria-disabled", "aria-expanded", "aria-haspopup", "aria-level", "aria-posinset", "aria-selected", "aria-setsize"])
2801
- }, wa = /* @__PURE__ */ new Set([
2824
+ }, xa = /* @__PURE__ */ new Set([
2802
2825
  "aria-atomic",
2803
2826
  "aria-busy",
2804
2827
  "aria-controls",
@@ -2822,28 +2845,28 @@ const va = {
2822
2845
  "aria-roledescription",
2823
2846
  "aria-braillelabel",
2824
2847
  "aria-brailleroledescription"
2825
- ]), Aa = {
2848
+ ]), Sa = {
2826
2849
  id: "aria-allowed-attr",
2827
2850
  wcag: ["4.1.2"],
2828
2851
  level: "A",
2829
2852
  description: "ARIA attributes must be allowed for the element's role.",
2830
2853
  guidance: "Each ARIA role supports specific attributes. Using unsupported attributes creates confusion for assistive technologies. Check the ARIA specification for which attributes are valid for each role, or remove the attribute if it's not needed.",
2831
2854
  prompt: "The ARIA attribute listed in context is not supported on this element's role. Either remove the attribute (if the behavior it describes isn't needed), or change the element's role to one that supports it. The context lists which attributes ARE allowed on this role — use that to suggest alternatives if applicable.",
2832
- run(t) {
2855
+ run(e) {
2833
2856
  const a = [];
2834
- for (const e of t.querySelectorAll("[role], [aria-*]")) {
2835
- if (g(e)) continue;
2836
- const i = q(e);
2857
+ for (const t of e.querySelectorAll("[role], [aria-*]")) {
2858
+ if (g(t)) continue;
2859
+ const i = q(t);
2837
2860
  if (!i) continue;
2838
- const n = ya[i];
2861
+ const n = Aa[i];
2839
2862
  if (n)
2840
- for (const r of e.attributes) {
2841
- if (!r.name.startsWith("aria-") || wa.has(r.name) || n.has(r.name)) continue;
2863
+ for (const r of t.attributes) {
2864
+ if (!r.name.startsWith("aria-") || xa.has(r.name) || n.has(r.name)) continue;
2842
2865
  const o = n.size > 0 ? [...n].join(", ") : "none (only global ARIA attributes)";
2843
2866
  a.push({
2844
2867
  ruleId: "aria-allowed-attr",
2845
- selector: m(e),
2846
- html: d(e),
2868
+ selector: m(t),
2869
+ html: d(t),
2847
2870
  impact: "critical",
2848
2871
  message: `ARIA attribute "${r.name}" is not allowed on role "${i}".`,
2849
2872
  context: `Attribute: ${r.name}="${r.value}", role: ${i}, allowed role-specific attributes: ${o}`
@@ -2852,7 +2875,7 @@ const va = {
2852
2875
  }
2853
2876
  return a;
2854
2877
  }
2855
- }, xa = /* @__PURE__ */ new Set([
2878
+ }, ka = /* @__PURE__ */ new Set([
2856
2879
  "base",
2857
2880
  "col",
2858
2881
  "colgroup",
@@ -2974,38 +2997,38 @@ const va = {
2974
2997
  video: /* @__PURE__ */ new Set(["application"]),
2975
2998
  wbr: /* @__PURE__ */ new Set(["none", "presentation"])
2976
2999
  };
2977
- function Sa(t) {
2978
- var e;
2979
- const a = t.tagName.toLowerCase();
2980
- if (xa.has(a))
3000
+ function Ia(e) {
3001
+ var t;
3002
+ const a = e.tagName.toLowerCase();
3003
+ if (ka.has(a))
2981
3004
  return "none";
2982
- if (a === "a" && t.hasAttribute("href"))
3005
+ if (a === "a" && e.hasAttribute("href"))
2983
3006
  return E["a[href]"];
2984
- if (a === "img" && t.getAttribute("alt") === "")
3007
+ if (a === "img" && e.getAttribute("alt") === "")
2985
3008
  return E["img[alt='']"];
2986
3009
  if (a === "input") {
2987
- const n = `input[type=${((e = t.getAttribute("type")) == null ? void 0 : e.toLowerCase()) || "text"}]`;
3010
+ const n = `input[type=${((t = e.getAttribute("type")) == null ? void 0 : t.toLowerCase()) || "text"}]`;
2988
3011
  return n in E ? E[n] : "none";
2989
3012
  }
2990
3013
  return E[a] || "any";
2991
3014
  }
2992
- const ka = {
3015
+ const Ta = {
2993
3016
  id: "aria-allowed-role",
2994
3017
  wcag: ["4.1.2"],
2995
3018
  level: "A",
2996
3019
  description: "ARIA role must be appropriate for the element.",
2997
3020
  guidance: "Not all ARIA roles can be applied to all HTML elements. Many elements have implicit roles (e.g., <header> is implicitly banner, <nav> is navigation, <main> is main). Adding an explicit role that matches the implicit role is redundant. Adding a conflicting role breaks semantics. Either remove the role attribute or use a different element.",
2998
3021
  prompt: "Consider implicit roles: header=banner, nav=navigation, main=main, footer=contentinfo, aside=complementary, article=article, section=region (when labeled). Explain if this role is redundant (matches implicit) or invalid (conflicts). Suggest removing it or restructuring.",
2999
- run(t) {
3000
- var e;
3022
+ run(e) {
3023
+ var t;
3001
3024
  const a = [];
3002
- for (const i of t.querySelectorAll("[role]")) {
3025
+ for (const i of e.querySelectorAll("[role]")) {
3003
3026
  if (g(i)) continue;
3004
- const n = (e = i.getAttribute("role")) == null ? void 0 : e.trim().toLowerCase();
3027
+ const n = (t = i.getAttribute("role")) == null ? void 0 : t.trim().toLowerCase();
3005
3028
  if (!n) continue;
3006
- const r = de(i);
3029
+ const r = he(i);
3007
3030
  if (r && n === r) continue;
3008
- const o = Sa(i);
3031
+ const o = Ia(i);
3009
3032
  o === "none" ? a.push({
3010
3033
  ruleId: "aria-allowed-role",
3011
3034
  selector: m(i),
@@ -3030,8 +3053,8 @@ const ka = {
3030
3053
  grid: [["row", "rowgroup"]],
3031
3054
  list: [["listitem", "group"]],
3032
3055
  listbox: [["option", "group"]],
3033
- menu: [["menuitem", "menuitemcheckbox", "menuitemradio", "group"]],
3034
- menubar: [["menuitem", "menuitemcheckbox", "menuitemradio", "group"]],
3056
+ menu: [["menuitem", "menuitemcheckbox", "menuitemradio", "group", "menu", "separator"]],
3057
+ menubar: [["menuitem", "menuitemcheckbox", "menuitemradio", "group", "menu", "separator"]],
3035
3058
  radiogroup: [["radio"]],
3036
3059
  row: [["cell", "columnheader", "gridcell", "rowheader"]],
3037
3060
  rowgroup: [["row"]],
@@ -3039,7 +3062,7 @@ const ka = {
3039
3062
  tablist: [["tab"]],
3040
3063
  tree: [["treeitem", "group"]],
3041
3064
  treegrid: [["row", "rowgroup"]]
3042
- }, Ia = /* @__PURE__ */ new Set([
3065
+ }, Ea = /* @__PURE__ */ new Set([
3043
3066
  "doc-bibliography",
3044
3067
  "doc-endnotes",
3045
3068
  "grid",
@@ -3053,8 +3076,9 @@ const ka = {
3053
3076
  "treegrid"
3054
3077
  ]), ne = {
3055
3078
  caption: ["figure", "table", "grid", "treegrid"],
3056
- // cell/gridcell/columnheader/rowheader must be in a row
3057
- // but we skip checking native td/th since they're handled by HTML semantics
3079
+ cell: ["row"],
3080
+ columnheader: ["row"],
3081
+ gridcell: ["row"],
3058
3082
  listitem: ["list", "group"],
3059
3083
  menuitem: ["menu", "menubar", "group"],
3060
3084
  menuitemcheckbox: ["menu", "menubar", "group"],
@@ -3062,25 +3086,26 @@ const ka = {
3062
3086
  option: ["listbox", "group"],
3063
3087
  row: ["table", "grid", "treegrid", "rowgroup"],
3064
3088
  rowgroup: ["table", "grid", "treegrid"],
3089
+ rowheader: ["row"],
3065
3090
  tab: ["tablist"],
3066
3091
  treeitem: ["tree", "group"]
3067
3092
  };
3068
- function Ta(t, a) {
3093
+ function Ca(e, a) {
3069
3094
  var o;
3070
- const e = ((o = t.getAttribute("aria-owns")) == null ? void 0 : o.split(/\s+/)) || [], i = t.ownerDocument, n = /* @__PURE__ */ new Set();
3095
+ const t = ((o = e.getAttribute("aria-owns")) == null ? void 0 : o.split(/\s+/)) || [], i = e.ownerDocument, n = /* @__PURE__ */ new Set();
3071
3096
  let r = !1;
3072
- for (const s of t.querySelectorAll("*")) {
3097
+ for (const s of e.querySelectorAll("*")) {
3073
3098
  if (g(s)) continue;
3074
3099
  r = !0;
3075
3100
  const l = q(s);
3076
3101
  l && n.add(l);
3077
3102
  }
3078
- for (const s of e) {
3103
+ for (const s of t) {
3079
3104
  const l = i.getElementById(s);
3080
3105
  if (l && !g(l)) {
3081
3106
  r = !0;
3082
- const h = q(l);
3083
- h && n.add(h);
3107
+ const p = q(l);
3108
+ p && n.add(p);
3084
3109
  }
3085
3110
  }
3086
3111
  if (!r) return "empty";
@@ -3088,27 +3113,27 @@ function Ta(t, a) {
3088
3113
  if (!s.some((l) => n.has(l))) return "fail";
3089
3114
  return "pass";
3090
3115
  }
3091
- const Ea = {
3116
+ const La = {
3092
3117
  id: "aria-required-children",
3093
3118
  wcag: ["1.3.1"],
3094
3119
  level: "A",
3095
3120
  description: "Certain ARIA roles require specific child roles to be present.",
3096
3121
  guidance: "Some ARIA roles represent containers that must contain specific child roles for proper semantics. For example, a list must contain listitems, a menu must contain menuitems. Add the required child elements with appropriate roles, or use native HTML elements that provide these semantics implicitly (e.g., <ul> with <li>).",
3097
3122
  prompt: "State which child role(s) are required and suggest adding elements with those roles, or using equivalent native HTML elements.",
3098
- run(t) {
3099
- var e;
3123
+ run(e) {
3124
+ var t;
3100
3125
  const a = [];
3101
- for (const i of t.querySelectorAll("[role]")) {
3126
+ for (const i of e.querySelectorAll("[role]")) {
3102
3127
  if (g(i)) continue;
3103
- const n = (e = i.getAttribute("role")) == null ? void 0 : e.trim().toLowerCase();
3128
+ const n = (t = i.getAttribute("role")) == null ? void 0 : t.trim().toLowerCase();
3104
3129
  if (!n || !(n in ie) || i.getAttribute("aria-busy") === "true") continue;
3105
3130
  if (n === "combobox") {
3106
3131
  if (i.getAttribute("aria-expanded") !== "true") continue;
3107
3132
  const l = i.tagName.toLowerCase();
3108
3133
  if (l === "input" || l === "textarea") continue;
3109
3134
  }
3110
- const r = ie[n], o = Ta(i, r);
3111
- if (o === "pass" || o === "empty" && Ia.has(n)) continue;
3135
+ const r = ie[n], o = Ca(i, r);
3136
+ if (o === "pass" || o === "empty" && Ea.has(n)) continue;
3112
3137
  const s = r.map((l) => l.join(" or ")).join(", ");
3113
3138
  a.push({
3114
3139
  ruleId: "aria-required-children",
@@ -3120,23 +3145,23 @@ const Ea = {
3120
3145
  }
3121
3146
  return a;
3122
3147
  }
3123
- }, Ca = {
3148
+ }, qa = {
3124
3149
  id: "aria-required-parent",
3125
3150
  wcag: ["1.3.1"],
3126
3151
  level: "A",
3127
3152
  description: "Certain ARIA roles must be contained within specific parent roles.",
3128
3153
  guidance: "Some ARIA roles represent items that must exist within specific container roles. For example, a listitem must be within a list, a tab must be within a tablist. Wrap the element in the appropriate parent, or use native HTML elements that provide this structure (e.g., <li> inside <ul>).",
3129
3154
  prompt: "State which parent role is required and suggest wrapping in an element with that role, or using equivalent native HTML structure.",
3130
- run(t) {
3131
- var e;
3155
+ run(e) {
3156
+ var t;
3132
3157
  const a = [];
3133
- for (const i of t.querySelectorAll("[role]")) {
3158
+ for (const i of e.querySelectorAll("[role]")) {
3134
3159
  if (g(i)) continue;
3135
- const n = (e = i.getAttribute("role")) == null ? void 0 : e.trim().toLowerCase();
3160
+ const n = (t = i.getAttribute("role")) == null ? void 0 : t.trim().toLowerCase();
3136
3161
  if (!n || !(n in ne)) continue;
3137
3162
  const r = ne[n];
3138
3163
  let o = i.parentElement, s = !1;
3139
- for (; o && o !== t.documentElement; ) {
3164
+ for (; o && o !== e.documentElement; ) {
3140
3165
  const l = q(o);
3141
3166
  if (l && r.includes(l)) {
3142
3167
  s = !0;
@@ -3170,10 +3195,10 @@ const Ea = {
3170
3195
  "embed",
3171
3196
  "area[href]"
3172
3197
  ].join(", ");
3173
- function La(t) {
3174
- let a = t;
3175
- const e = t.ownerDocument, i = e.defaultView;
3176
- for (; a && a !== e.body; ) {
3198
+ function Ra(e) {
3199
+ let a = e;
3200
+ const t = e.ownerDocument, i = t.defaultView;
3201
+ for (; a && a !== t.body; ) {
3177
3202
  if (a.style.display === "none" || a.style.visibility === "hidden") return !1;
3178
3203
  if (i) {
3179
3204
  const n = i.getComputedStyle(a);
@@ -3183,7 +3208,7 @@ function La(t) {
3183
3208
  }
3184
3209
  return !0;
3185
3210
  }
3186
- const qa = {
3211
+ const Na = {
3187
3212
  id: "aria-hidden-body",
3188
3213
  selector: 'body[aria-hidden="true"]',
3189
3214
  check: { type: "selector-exists" },
@@ -3195,27 +3220,27 @@ const qa = {
3195
3220
  guidance: "Setting aria-hidden='true' on the body element hides all page content from assistive technologies, making the page completely inaccessible to screen reader users. Remove aria-hidden from the body element. If you need to hide content temporarily (e.g., behind a modal), use aria-hidden on specific sections instead.",
3196
3221
  prompt: "Instruct to remove aria-hidden='true' from the body element.",
3197
3222
  skipAriaHidden: !1
3198
- }, Ra = k(qa), Na = {
3223
+ }, $a = k(Na), Ma = {
3199
3224
  id: "aria-hidden-focus",
3200
3225
  wcag: ["4.1.2"],
3201
3226
  level: "A",
3202
3227
  description: "Elements with aria-hidden='true' must not contain focusable elements.",
3203
3228
  guidance: "When aria-hidden='true' hides an element from assistive technologies but the element contains focusable children, keyboard users can focus those children but screen reader users won't know they exist. Either remove focusable elements from the hidden region, add tabindex='-1' to them, or remove aria-hidden.",
3204
3229
  prompt: "This element can receive keyboard focus but is inside an aria-hidden region, making it invisible to screen readers. The context explains why it's focusable. Fix by either: (1) adding tabindex='-1' to remove it from tab order, (2) moving it outside the aria-hidden region, or (3) removing aria-hidden='true' from the ancestor if the content should be accessible.",
3205
- run(t) {
3230
+ run(e) {
3206
3231
  const a = [];
3207
- for (const e of t.querySelectorAll('[aria-hidden="true"]')) {
3208
- if (e === t.body) continue;
3209
- const i = [...e.querySelectorAll(re)];
3210
- e.matches(re) && i.push(e);
3232
+ for (const t of e.querySelectorAll('[aria-hidden="true"]')) {
3233
+ if (t === e.body) continue;
3234
+ const i = [...t.querySelectorAll(re)];
3235
+ t.matches(re) && i.push(t);
3211
3236
  for (const n of i)
3212
3237
  if (n instanceof HTMLElement) {
3213
3238
  const r = n.getAttribute("tabindex");
3214
- if (r === "-1" || n.disabled || n instanceof HTMLInputElement && n.type === "hidden" || !La(n)) continue;
3239
+ if (r === "-1" || n.disabled || n instanceof HTMLInputElement && n.type === "hidden" || !Ra(n)) continue;
3215
3240
  const o = n.tagName.toLowerCase();
3216
3241
  let s;
3217
3242
  r !== null ? s = `has tabindex="${r}"` : o === "a" && n.hasAttribute("href") ? s = "is a link with href" : o === "button" ? s = "is a <button>" : o === "input" ? s = `is an <input type="${n.type}">` : o === "select" ? s = "is a <select>" : o === "textarea" ? s = "is a <textarea>" : o === "iframe" ? s = "is an <iframe>" : s = `is a natively focusable <${o}>`;
3218
- const l = n === e ? n : n.closest('[aria-hidden="true"]');
3243
+ const l = n === t ? n : n.closest('[aria-hidden="true"]');
3219
3244
  a.push({
3220
3245
  ruleId: "aria-hidden-focus",
3221
3246
  selector: m(n),
@@ -3228,21 +3253,21 @@ const qa = {
3228
3253
  }
3229
3254
  return a;
3230
3255
  }
3231
- }, $a = {
3256
+ }, Ha = {
3232
3257
  id: "aria-command-name",
3233
3258
  wcag: ["4.1.2"],
3234
3259
  level: "A",
3235
3260
  description: "ARIA commands must have an accessible name.",
3236
3261
  guidance: "Interactive ARIA command roles (button, link, menuitem) must have accessible names so users know what action they perform. Add visible text content, aria-label, or aria-labelledby to provide a name.",
3237
3262
  prompt: "Based on the element's content or context, suggest an aria-label describing what this command does.",
3238
- run(t) {
3239
- var e;
3263
+ run(e) {
3264
+ var t;
3240
3265
  const a = [];
3241
- for (const i of t.querySelectorAll('[role="button"], [role="link"], [role="menuitem"]')) {
3266
+ for (const i of e.querySelectorAll('[role="button"], [role="link"], [role="menuitem"]')) {
3242
3267
  if (g(i) || T(i) || i.getRootNode() instanceof ShadowRoot || i.tagName.toLowerCase() === "button" || i.tagName.toLowerCase() === "a") continue;
3243
3268
  if (!v(i)) {
3244
3269
  const r = i.querySelector("img[alt]");
3245
- if ((e = r == null ? void 0 : r.getAttribute("alt")) != null && e.trim()) continue;
3270
+ if ((t = r == null ? void 0 : r.getAttribute("alt")) != null && t.trim()) continue;
3246
3271
  a.push({
3247
3272
  ruleId: "aria-command-name",
3248
3273
  selector: m(i),
@@ -3254,16 +3279,16 @@ const qa = {
3254
3279
  }
3255
3280
  return a;
3256
3281
  }
3257
- }, Ma = {
3282
+ }, Da = {
3258
3283
  id: "aria-input-field-name",
3259
3284
  wcag: ["4.1.2"],
3260
3285
  level: "A",
3261
3286
  description: "ARIA input fields must have an accessible name.",
3262
3287
  guidance: "ARIA input widgets (combobox, listbox, searchbox, slider, spinbutton, textbox) must have accessible names so users understand what data to enter. Add a visible label with aria-labelledby, or use aria-label if a visible label is not possible.",
3263
3288
  prompt: "Based on the context, suggest an aria-label describing what data this input field accepts.",
3264
- run(t) {
3265
- const a = [], e = '[role="combobox"], [role="listbox"], [role="searchbox"], [role="slider"], [role="spinbutton"], [role="textbox"]';
3266
- for (const i of t.querySelectorAll(e)) {
3289
+ run(e) {
3290
+ const a = [], t = '[role="combobox"], [role="listbox"], [role="searchbox"], [role="slider"], [role="spinbutton"], [role="textbox"]';
3291
+ for (const i of e.querySelectorAll(t)) {
3267
3292
  if (g(i) || T(i) || i.getRootNode() instanceof ShadowRoot || i.matches("input, select, textarea")) continue;
3268
3293
  v(i) || a.push({
3269
3294
  ruleId: "aria-input-field-name",
@@ -3275,16 +3300,16 @@ const qa = {
3275
3300
  }
3276
3301
  return a;
3277
3302
  }
3278
- }, Ha = {
3303
+ }, Fa = {
3279
3304
  id: "aria-toggle-field-name",
3280
3305
  wcag: ["4.1.2"],
3281
3306
  level: "A",
3282
3307
  description: "ARIA toggle fields must have an accessible name.",
3283
3308
  guidance: "ARIA toggle controls (checkbox, switch, radio, menuitemcheckbox, menuitemradio) must have accessible names so users understand what option they're selecting. Add visible text content, aria-label, or use aria-labelledby to reference a visible label.",
3284
3309
  prompt: "Based on the context, suggest an aria-label describing what option this toggle controls.",
3285
- run(t) {
3286
- const a = [], e = '[role="checkbox"], [role="switch"], [role="radio"], [role="menuitemcheckbox"], [role="menuitemradio"]';
3287
- for (const i of t.querySelectorAll(e)) {
3310
+ run(e) {
3311
+ const a = [], t = '[role="checkbox"], [role="switch"], [role="radio"], [role="menuitemcheckbox"], [role="menuitemradio"]';
3312
+ for (const i of e.querySelectorAll(t)) {
3288
3313
  if (g(i) || T(i) || i.getRootNode() instanceof ShadowRoot || i.matches('input[type="checkbox"], input[type="radio"]')) continue;
3289
3314
  v(i) || a.push({
3290
3315
  ruleId: "aria-toggle-field-name",
@@ -3296,21 +3321,21 @@ const qa = {
3296
3321
  }
3297
3322
  return a;
3298
3323
  }
3299
- }, Da = {
3324
+ }, Wa = {
3300
3325
  id: "aria-meter-name",
3301
3326
  wcag: ["4.1.2"],
3302
3327
  level: "A",
3303
3328
  description: "ARIA meter elements must have an accessible name.",
3304
3329
  guidance: "Meter elements display a value within a known range (like disk usage or password strength). They must have accessible names so screen reader users understand what is being measured. Use aria-label or aria-labelledby to provide context.",
3305
3330
  prompt: "Based on the context or value attributes, suggest an aria-label describing what this meter measures.",
3306
- run(t) {
3331
+ run(e) {
3307
3332
  const a = [];
3308
- for (const e of t.querySelectorAll('[role="meter"], meter')) {
3309
- if (g(e)) continue;
3310
- v(e) || a.push({
3333
+ for (const t of e.querySelectorAll('[role="meter"], meter')) {
3334
+ if (g(t)) continue;
3335
+ v(t) || a.push({
3311
3336
  ruleId: "aria-meter-name",
3312
- selector: m(e),
3313
- html: d(e),
3337
+ selector: m(t),
3338
+ html: d(t),
3314
3339
  impact: "serious",
3315
3340
  message: "Meter has no accessible name."
3316
3341
  });
@@ -3324,14 +3349,14 @@ const qa = {
3324
3349
  description: "ARIA progressbar elements must have an accessible name.",
3325
3350
  guidance: "Progress indicators must have accessible names so screen reader users understand what process is being tracked. Use aria-label (e.g., 'File upload progress') or aria-labelledby to reference a visible heading or label.",
3326
3351
  prompt: "Based on the context, suggest an aria-label describing what process this progressbar tracks.",
3327
- run(t) {
3352
+ run(e) {
3328
3353
  const a = [];
3329
- for (const e of t.querySelectorAll('[role="progressbar"], progress')) {
3330
- if (g(e)) continue;
3331
- v(e) || a.push({
3354
+ for (const t of e.querySelectorAll('[role="progressbar"], progress')) {
3355
+ if (g(t)) continue;
3356
+ v(t) || a.push({
3332
3357
  ruleId: "aria-progressbar-name",
3333
- selector: m(e),
3334
- html: d(e),
3358
+ selector: m(t),
3359
+ html: d(t),
3335
3360
  impact: "serious",
3336
3361
  message: "Progressbar has no accessible name."
3337
3362
  });
@@ -3345,80 +3370,80 @@ const qa = {
3345
3370
  description: "ARIA dialogs must have an accessible name.",
3346
3371
  guidance: "Dialog and alertdialog elements must have accessible names so screen reader users understand the dialog's purpose when it opens. Use aria-label or aria-labelledby pointing to the dialog's heading. Native <dialog> elements should also have an accessible name.",
3347
3372
  prompt: "Suggest adding aria-labelledby pointing to the dialog's heading element, or an aria-label describing the dialog's purpose.",
3348
- run(t) {
3373
+ run(e) {
3349
3374
  const a = [];
3350
- for (const e of t.querySelectorAll('[role="dialog"], [role="alertdialog"], dialog')) {
3351
- if (g(e)) continue;
3352
- v(e) || a.push({
3375
+ for (const t of e.querySelectorAll('[role="dialog"], [role="alertdialog"], dialog')) {
3376
+ if (g(t)) continue;
3377
+ v(t) || a.push({
3353
3378
  ruleId: "aria-dialog-name",
3354
- selector: m(e),
3355
- html: d(e),
3379
+ selector: m(t),
3380
+ html: d(t),
3356
3381
  impact: "serious",
3357
3382
  message: "Dialog has no accessible name."
3358
3383
  });
3359
3384
  }
3360
3385
  return a;
3361
3386
  }
3362
- }, Fa = {
3387
+ }, _a = {
3363
3388
  id: "aria-tooltip-name",
3364
3389
  wcag: ["4.1.2"],
3365
3390
  level: "A",
3366
3391
  description: "ARIA tooltips must have an accessible name.",
3367
3392
  guidance: "Tooltip elements must have accessible names (usually their text content). The tooltip content itself typically serves as the accessible name. Ensure the tooltip contains descriptive text content or has aria-label.",
3368
3393
  prompt: "Add text content to the tooltip describing the information it provides, or add aria-label.",
3369
- run(t) {
3394
+ run(e) {
3370
3395
  const a = [];
3371
- for (const e of t.querySelectorAll('[role="tooltip"]')) {
3372
- if (g(e)) continue;
3373
- v(e) || a.push({
3396
+ for (const t of e.querySelectorAll('[role="tooltip"]')) {
3397
+ if (g(t)) continue;
3398
+ v(t) || a.push({
3374
3399
  ruleId: "aria-tooltip-name",
3375
- selector: m(e),
3376
- html: d(e),
3400
+ selector: m(t),
3401
+ html: d(t),
3377
3402
  impact: "serious",
3378
3403
  message: "Tooltip has no accessible name."
3379
3404
  });
3380
3405
  }
3381
3406
  return a;
3382
3407
  }
3383
- }, Wa = {
3408
+ }, Pa = {
3384
3409
  id: "aria-treeitem-name",
3385
3410
  wcag: ["4.1.2"],
3386
3411
  level: "A",
3387
3412
  description: "ARIA treeitem elements must have an accessible name.",
3388
3413
  guidance: "Tree items must have accessible names so screen reader users can understand the tree structure and navigate it effectively. Provide text content, aria-label, or aria-labelledby for each treeitem.",
3389
3414
  prompt: "Add text content describing this tree item, or add aria-label.",
3390
- run(t) {
3415
+ run(e) {
3391
3416
  const a = [];
3392
- for (const e of t.querySelectorAll('[role="treeitem"]')) {
3393
- if (g(e)) continue;
3394
- v(e) || a.push({
3417
+ for (const t of e.querySelectorAll('[role="treeitem"]')) {
3418
+ if (g(t)) continue;
3419
+ v(t) || a.push({
3395
3420
  ruleId: "aria-treeitem-name",
3396
- selector: m(e),
3397
- html: d(e),
3421
+ selector: m(t),
3422
+ html: d(t),
3398
3423
  impact: "serious",
3399
3424
  message: "Treeitem has no accessible name."
3400
3425
  });
3401
3426
  }
3402
3427
  return a;
3403
3428
  }
3404
- }, _a = {
3429
+ }, ja = {
3405
3430
  id: "aria-prohibited-attr",
3406
3431
  wcag: ["4.1.2"],
3407
3432
  level: "A",
3408
3433
  description: "ARIA attributes must not be prohibited for the element's role.",
3409
3434
  guidance: "Some ARIA roles prohibit certain attributes. For example, roles like 'none', 'presentation', 'generic', and text-level roles (code, emphasis, strong) prohibit aria-label and aria-labelledby because naming is not supported for these roles. Remove the prohibited attributes or change the role.",
3410
3435
  prompt: "Identify the prohibited attribute and recommend removing it from this element.",
3411
- run(t) {
3412
- return U(t).prohibitedAttr;
3436
+ run(e) {
3437
+ return U(e).prohibitedAttr;
3413
3438
  }
3414
- }, Pa = [
3439
+ }, Va = [
3415
3440
  "a[href]",
3416
3441
  "button:not([disabled])",
3417
3442
  'input:not([disabled]):not([type="hidden"])',
3418
3443
  "select:not([disabled])",
3419
3444
  "textarea:not([disabled])",
3420
3445
  '[tabindex]:not([tabindex="-1"])'
3421
- ].join(", "), ja = [
3446
+ ].join(", "), za = [
3422
3447
  "aria-atomic",
3423
3448
  "aria-busy",
3424
3449
  "aria-controls",
@@ -3433,64 +3458,64 @@ const qa = {
3433
3458
  "aria-owns",
3434
3459
  "aria-relevant"
3435
3460
  ];
3436
- function oe(t) {
3461
+ function oe(e) {
3437
3462
  const a = [];
3438
- t.matches(Pa) && a.push("element is focusable");
3439
- for (const e of ja)
3440
- if (t.hasAttribute(e)) {
3441
- a.push(`has ${e}`);
3463
+ e.matches(Va) && a.push("element is focusable");
3464
+ for (const t of za)
3465
+ if (e.hasAttribute(t)) {
3466
+ a.push(`has ${t}`);
3442
3467
  break;
3443
3468
  }
3444
- return (t.hasAttribute("aria-label") || t.hasAttribute("aria-labelledby")) && a.push("has accessible name"), a;
3469
+ return (e.hasAttribute("aria-label") || e.hasAttribute("aria-labelledby")) && a.push("has accessible name"), a;
3445
3470
  }
3446
- const Va = {
3471
+ const Ua = {
3447
3472
  id: "presentation-role-conflict",
3448
3473
  wcag: ["4.1.2"],
3449
3474
  level: "A",
3450
3475
  description: "Elements with role='presentation' or role='none' must not be focusable or have global ARIA attributes.",
3451
3476
  guidance: "When an element has role='presentation' or role='none', it's marked as decorative and removed from the accessibility tree. However, if the element is focusable or has certain ARIA attributes, the presentation role is ignored and the element remains accessible. This creates confusion. Either remove the presentation role, or remove the focusability/ARIA attributes.",
3452
3477
  prompt: "Identify the conflict (focusable or ARIA attribute) and suggest either removing the presentation role or removing the conflicting attribute/focusability.",
3453
- run(t) {
3478
+ run(e) {
3454
3479
  const a = [];
3455
- for (const e of t.querySelectorAll('[role="presentation"], [role="none"]')) {
3456
- if (g(e)) continue;
3457
- const i = oe(e);
3480
+ for (const t of e.querySelectorAll('[role="presentation"], [role="none"]')) {
3481
+ if (g(t)) continue;
3482
+ const i = oe(t);
3458
3483
  i.length > 0 && a.push({
3459
3484
  ruleId: "presentation-role-conflict",
3460
- selector: m(e),
3461
- html: d(e),
3485
+ selector: m(t),
3486
+ html: d(t),
3462
3487
  impact: "serious",
3463
3488
  message: `Presentation role conflicts with: ${i.join(", ")}. The role will be ignored.`
3464
3489
  });
3465
3490
  }
3466
- for (const e of t.querySelectorAll('img[alt=""]')) {
3467
- if (g(e) || e.hasAttribute("role")) continue;
3468
- const i = oe(e);
3491
+ for (const t of e.querySelectorAll('img[alt=""]')) {
3492
+ if (g(t) || t.hasAttribute("role")) continue;
3493
+ const i = oe(t);
3469
3494
  i.length > 0 && a.push({
3470
3495
  ruleId: "presentation-role-conflict",
3471
- selector: m(e),
3472
- html: d(e),
3496
+ selector: m(t),
3497
+ html: d(t),
3473
3498
  impact: "serious",
3474
3499
  message: `Element with implicit presentation role (alt="") conflicts with: ${i.join(", ")}. The decorative role will be ignored.`
3475
3500
  });
3476
3501
  }
3477
3502
  return a;
3478
3503
  }
3479
- }, za = {
3504
+ }, Ga = {
3480
3505
  id: "summary-name",
3481
3506
  wcag: ["4.1.2"],
3482
3507
  level: "A",
3483
3508
  description: "<summary> elements must have an accessible name.",
3484
3509
  guidance: "The <summary> element provides the visible label for a <details> disclosure widget. It must have descriptive text content so screen reader users understand what will be revealed when expanded. Add clear, concise text that indicates what content is contained in the details section.",
3485
3510
  prompt: "Based on the surrounding context or details content, suggest text to add inside the <summary> element.",
3486
- run(t) {
3511
+ run(e) {
3487
3512
  const a = [];
3488
- for (const e of t.querySelectorAll("details > summary:first-of-type")) {
3489
- if (g(e)) continue;
3490
- v(e) || a.push({
3513
+ for (const t of e.querySelectorAll("details > summary:first-of-type")) {
3514
+ if (g(t)) continue;
3515
+ v(t) || a.push({
3491
3516
  ruleId: "summary-name",
3492
- selector: m(e),
3493
- html: d(e),
3517
+ selector: m(t),
3518
+ html: d(t),
3494
3519
  impact: "serious",
3495
3520
  message: "<summary> element has no accessible name. Add descriptive text."
3496
3521
  });
@@ -3498,11 +3523,11 @@ const Va = {
3498
3523
  return a;
3499
3524
  }
3500
3525
  };
3501
- function Ua(t) {
3526
+ function Ya(e) {
3502
3527
  var n, r;
3503
- const a = [], e = t.getAttribute("href");
3504
- e && a.push(`href: ${e}`);
3505
- const i = t.parentElement;
3528
+ const a = [], t = e.getAttribute("href");
3529
+ t && a.push(`href: ${t}`);
3530
+ const i = e.parentElement;
3506
3531
  if (i) {
3507
3532
  const o = i.closest("h1, h2, h3, h4, h5, h6");
3508
3533
  if ((n = o == null ? void 0 : o.textContent) != null && n.trim())
@@ -3515,29 +3540,29 @@ function Ua(t) {
3515
3540
  return a.length > 0 ? a.join(`
3516
3541
  `) : void 0;
3517
3542
  }
3518
- const Ga = {
3543
+ const Xa = {
3519
3544
  id: "link-name",
3520
3545
  wcag: ["2.4.4", "4.1.2"],
3521
3546
  level: "A",
3522
3547
  description: "Links must have discernible text via content, aria-label, or aria-labelledby.",
3523
3548
  guidance: "Screen reader users need to know where a link goes. Add descriptive text content, aria-label, or use aria-labelledby. For image links, ensure the image has alt text describing the link destination. Avoid generic text like 'click here' or 'read more'—link text should make sense out of context.",
3524
3549
  prompt: "Based on the href or surrounding context, suggest descriptive link text or an aria-label.",
3525
- run(t) {
3550
+ run(e) {
3526
3551
  const a = [];
3527
- for (const e of t.querySelectorAll('a[href], area[href], [role="link"]')) {
3528
- if (g(e) || T(e) || e.getRootNode() instanceof ShadowRoot) continue;
3529
- v(e) || a.push({
3552
+ for (const t of e.querySelectorAll('a[href], area[href], [role="link"]')) {
3553
+ if (g(t) || T(t) || t.getRootNode() instanceof ShadowRoot) continue;
3554
+ v(t) || a.push({
3530
3555
  ruleId: "link-name",
3531
- selector: m(e),
3532
- html: d(e),
3556
+ selector: m(t),
3557
+ html: d(t),
3533
3558
  impact: "serious",
3534
3559
  message: "Link has no discernible text.",
3535
- context: Ua(e)
3560
+ context: Ya(t)
3536
3561
  });
3537
3562
  }
3538
3563
  return a;
3539
3564
  }
3540
- }, Ya = {
3565
+ }, Ka = {
3541
3566
  id: "skip-link",
3542
3567
  wcag: ["2.4.1"],
3543
3568
  level: "A",
@@ -3545,15 +3570,15 @@ const Ga = {
3545
3570
  description: "Skip links must point to a valid target on the page.",
3546
3571
  guidance: "Skip links allow keyboard users to bypass repetitive navigation and jump directly to main content. The skip link should be the first focusable element on the page, link to the main content (e.g., href='#main'), and become visible when focused. It can be visually hidden until focused using CSS.",
3547
3572
  prompt: "A skip link is a single <a href='#main'>Skip to main content</a> as the first element in <body>. It can be visually hidden with CSS until focused. Explain this simple pattern.",
3548
- run(t) {
3549
- const a = [], e = t.querySelectorAll('a[href^="#"]');
3550
- for (const i of e) {
3573
+ run(e) {
3574
+ const a = [], t = e.querySelectorAll('a[href^="#"]');
3575
+ for (const i of t) {
3551
3576
  const n = i.getAttribute("href");
3552
3577
  if (!n || n === "#") continue;
3553
3578
  const r = A(i).toLowerCase();
3554
3579
  if (!(r.includes("skip") || r.includes("jump") || r.includes("main content") || r.includes("navigation"))) continue;
3555
3580
  const s = n.slice(1);
3556
- t.getElementById(s) || a.push({
3581
+ e.getElementById(s) || a.push({
3557
3582
  ruleId: "skip-link",
3558
3583
  selector: m(i),
3559
3584
  html: d(i),
@@ -3563,7 +3588,7 @@ const Ga = {
3563
3588
  }
3564
3589
  return a;
3565
3590
  }
3566
- }, Xa = /* @__PURE__ */ new Set([
3591
+ }, Ja = /* @__PURE__ */ new Set([
3567
3592
  "block",
3568
3593
  "flex",
3569
3594
  "grid",
@@ -3571,52 +3596,52 @@ const Ga = {
3571
3596
  "table-cell",
3572
3597
  "list-item",
3573
3598
  "flow-root"
3574
- ]), Ka = /* @__PURE__ */ new Set([
3599
+ ]), Qa = /* @__PURE__ */ new Set([
3575
3600
  "inline",
3576
3601
  "inline-block",
3577
3602
  "inline-flex",
3578
3603
  "inline-grid"
3579
3604
  ]);
3580
- function Ja(t) {
3581
- let a = t.parentElement;
3605
+ function Za(e) {
3606
+ let a = e.parentElement;
3582
3607
  for (; a; ) {
3583
- const e = w(a).display;
3584
- if (Xa.has(e))
3585
- return Qa(a) ? a : null;
3608
+ const t = y(a).display;
3609
+ if (Ja.has(t))
3610
+ return ei(a) ? a : null;
3586
3611
  a = a.parentElement;
3587
3612
  }
3588
3613
  return null;
3589
3614
  }
3590
- function Qa(t) {
3591
- const a = t.ownerDocument.createTreeWalker(
3592
- t,
3615
+ function ei(e) {
3616
+ const a = e.ownerDocument.createTreeWalker(
3617
+ e,
3593
3618
  NodeFilter.SHOW_TEXT
3594
3619
  );
3595
- let e = "", i;
3620
+ let t = "", i;
3596
3621
  for (; i = a.nextNode(); ) {
3597
3622
  if (!i.data.trim()) continue;
3598
3623
  let n = i.parentElement, r = !1;
3599
- for (; n && n !== t; ) {
3624
+ for (; n && n !== e; ) {
3600
3625
  if (n.tagName === "A") {
3601
3626
  r = !0;
3602
3627
  break;
3603
3628
  }
3604
3629
  n = n.parentElement;
3605
3630
  }
3606
- r || (e += i.data);
3631
+ r || (t += i.data);
3607
3632
  }
3608
- return /[a-zA-Z\u00C0-\u024F]{2,}/.test(e);
3633
+ return new RegExp("\\p{L}{2,}", "u").test(t);
3609
3634
  }
3610
- function Za(t, a) {
3611
- const e = t.ownerDocument.createTreeWalker(
3612
- t,
3635
+ function ti(e, a) {
3636
+ const t = e.ownerDocument.createTreeWalker(
3637
+ e,
3613
3638
  NodeFilter.SHOW_TEXT
3614
3639
  );
3615
3640
  let i;
3616
- for (; i = e.nextNode(); ) {
3641
+ for (; i = t.nextNode(); ) {
3617
3642
  if (!i.data.trim()) continue;
3618
3643
  let n = i.parentElement, r = !1, o = n;
3619
- for (; o && o !== t; ) {
3644
+ for (; o && o !== e; ) {
3620
3645
  if (o.tagName === "A") {
3621
3646
  r = !0;
3622
3647
  break;
@@ -3624,61 +3649,61 @@ function Za(t, a) {
3624
3649
  o = o.parentElement;
3625
3650
  }
3626
3651
  if (!r && n)
3627
- return N(w(n).color);
3652
+ return N(y(n).color);
3628
3653
  }
3629
3654
  return null;
3630
3655
  }
3631
- function ei(t, a) {
3632
- const e = t.textDecorationLine || t.textDecoration || "", i = a.textDecorationLine || a.textDecoration || "";
3633
- if ((e.includes("underline") || e.includes("line-through")) && e !== i)
3656
+ function ai(e, a) {
3657
+ const t = e.textDecorationLine || e.textDecoration || "", i = a.textDecorationLine || a.textDecoration || "";
3658
+ if ((t.includes("underline") || t.includes("line-through")) && t !== i)
3634
3659
  return !0;
3635
- const n = parseFloat(t.borderBottomWidth) || 0, r = t.borderBottomStyle || "";
3660
+ const n = parseFloat(e.borderBottomWidth) || 0, r = e.borderBottomStyle || "";
3636
3661
  if (n > 0 && r !== "none" && r !== "hidden")
3637
3662
  return !0;
3638
- const o = parseFloat(t.outlineWidth) || 0, s = t.outlineStyle || "";
3663
+ const o = parseFloat(e.outlineWidth) || 0, s = e.outlineStyle || "";
3639
3664
  if (o > 0 && s !== "none")
3640
3665
  return !0;
3641
- const l = t.backgroundImage || "";
3666
+ const l = e.backgroundImage || "";
3642
3667
  if (l && l !== "none" && l !== "initial")
3643
3668
  return !0;
3644
- const h = se(t.fontWeight), c = se(a.fontWeight);
3645
- if (Math.abs(h - c) >= 300 || t.fontStyle !== a.fontStyle)
3669
+ const p = se(e.fontWeight), c = se(a.fontWeight);
3670
+ if (Math.abs(p - c) >= 300 || e.fontStyle !== a.fontStyle)
3646
3671
  return !0;
3647
- const u = parseFloat(t.fontSize) || 16, p = parseFloat(a.fontSize) || 16;
3648
- return p > 0 && u / p >= 1.2;
3672
+ const u = parseFloat(e.fontSize) || 16, h = parseFloat(a.fontSize) || 16;
3673
+ return h > 0 && u / h >= 1.2;
3649
3674
  }
3650
- function se(t) {
3651
- return t === "bold" ? 700 : t === "normal" ? 400 : parseInt(t) || 400;
3675
+ function se(e) {
3676
+ return e === "bold" ? 700 : e === "normal" ? 400 : parseInt(e) || 400;
3652
3677
  }
3653
- function le(t, a, e) {
3654
- return "#" + [t, a, e].map((i) => i.toString(16).padStart(2, "0")).join("");
3678
+ function le(e, a, t) {
3679
+ return "#" + [e, a, t].map((i) => i.toString(16).padStart(2, "0")).join("");
3655
3680
  }
3656
- const ti = {
3681
+ const ii = {
3657
3682
  id: "link-in-text-block",
3658
3683
  wcag: ["1.4.1"],
3659
3684
  level: "A",
3660
3685
  description: "Links within text blocks must be distinguishable by more than color alone.",
3661
3686
  guidance: "Users who cannot perceive color differences need other visual cues to identify links. Links in text should have underlines or other non-color indicators. If using color alone, ensure 3:1 contrast with surrounding text AND provide additional indication on focus/hover.",
3662
3687
  prompt: "Explain how to make this link visually distinguishable without relying on color alone.",
3663
- run(t) {
3688
+ run(e) {
3664
3689
  const a = [];
3665
- for (const e of t.querySelectorAll("a[href]")) {
3666
- if (g(e) || !A(e).trim() || e.closest('nav, header, footer, [role="navigation"], [role="banner"], [role="contentinfo"]')) continue;
3667
- const i = w(e), n = i.display || "inline";
3668
- if (!Ka.has(n)) continue;
3669
- const r = Ja(e);
3690
+ for (const t of e.querySelectorAll("a[href]")) {
3691
+ if (g(t) || !A(t).trim() || t.closest('nav, header, footer, [role="navigation"], [role="banner"], [role="contentinfo"]')) continue;
3692
+ const i = y(t), n = i.display || "inline";
3693
+ if (!Qa.has(n)) continue;
3694
+ const r = Za(t);
3670
3695
  if (!r) continue;
3671
- const o = w(r);
3672
- if (ei(i, o)) continue;
3673
- const s = N(i.color), l = Za(r);
3696
+ const o = y(r);
3697
+ if (ai(i, o)) continue;
3698
+ const s = N(i.color), l = ti(r);
3674
3699
  if (!s || !l) continue;
3675
- const h = R(...s), c = R(...l), u = me(h, c);
3700
+ const p = R(...s), c = R(...l), u = ge(p, c);
3676
3701
  if (u >= 3) continue;
3677
- const p = le(...s), b = le(...l), f = `link color: ${p} rgb(${s.join(", ")}), surrounding text: ${b} rgb(${l.join(", ")}), ratio: ${u.toFixed(2)}:1`;
3702
+ const h = le(...s), b = le(...l), f = `link color: ${h} rgb(${s.join(", ")}), surrounding text: ${b} rgb(${l.join(", ")}), ratio: ${u.toFixed(2)}:1`;
3678
3703
  a.push({
3679
3704
  ruleId: "link-in-text-block",
3680
- selector: m(e),
3681
- html: d(e),
3705
+ selector: m(t),
3706
+ html: d(t),
3682
3707
  impact: "serious",
3683
3708
  message: "Link in text block is not visually distinguishable from surrounding text. Add an underline, border, or ensure 3:1 color contrast with surrounding text.",
3684
3709
  context: f
@@ -3686,27 +3711,27 @@ const ti = {
3686
3711
  }
3687
3712
  return a;
3688
3713
  }
3689
- }, ai = {
3714
+ }, ni = {
3690
3715
  id: "html-has-lang",
3691
3716
  wcag: ["3.1.1"],
3692
3717
  level: "A",
3693
3718
  description: "The <html> element must have a lang attribute.",
3694
3719
  guidance: "Screen readers use the lang attribute to determine which language rules and pronunciation to use. Without it, content may be mispronounced. Set lang to the primary language of the page (e.g., lang='en' for English, lang='es' for Spanish).",
3695
3720
  prompt: `The page is missing a lang attribute on <html>. Use the text sample in context to determine the primary language and suggest the correct BCP 47 code (e.g. 'en' for English, 'es' for Spanish, 'fr' for French, 'de' for German, 'ja' for Japanese, 'zh' for Chinese, 'pt' for Portuguese, 'ar' for Arabic). Add lang to the <html> element: <html lang="...">.`,
3696
- run(t) {
3697
- var e, i;
3698
- const a = t.documentElement;
3721
+ run(e) {
3722
+ var t, i;
3723
+ const a = e.documentElement;
3699
3724
  if (a.tagName.toLowerCase() !== "html") return [];
3700
- if (!t.doctype && t.body) {
3701
- const n = t.body.children;
3725
+ if (!e.doctype && e.body) {
3726
+ const n = e.body.children;
3702
3727
  if (n.length > 0 && Array.from(n).every(
3703
3728
  (r) => r.tagName.toLowerCase() === "svg" || r.tagName.toLowerCase() === "math"
3704
3729
  )) return [];
3705
3730
  }
3706
- if (!((e = a.getAttribute("lang")) != null && e.trim())) {
3731
+ if (!((t = a.getAttribute("lang")) != null && t.trim())) {
3707
3732
  let n;
3708
- if (t.body) {
3709
- const r = ((i = t.body.textContent) == null ? void 0 : i.trim().replace(/\s+/g, " ")) || "";
3733
+ if (e.body) {
3734
+ const r = ((i = e.body.textContent) == null ? void 0 : i.trim().replace(/\s+/g, " ")) || "";
3710
3735
  r && (n = r.slice(0, 200));
3711
3736
  }
3712
3737
  return [{
@@ -3720,45 +3745,45 @@ const ti = {
3720
3745
  }
3721
3746
  return [];
3722
3747
  }
3723
- }, ii = new Set(
3748
+ }, ri = new Set(
3724
3749
  "aa ab ae af ak am an ar as av ay az ba be bg bh bi bm bn bo br bs ca ce ch co cr cs cu cv cy da de dv dz ee el en eo es et eu fa ff fi fj fo fr fy ga gd gl gn gu gv ha he hi ho hr ht hu hy hz ia id ie ig ii ik io is it iu ja jv ka kg ki kj kk kl km kn ko kr ks ku kv kw ky la lb lg li ln lo lt lu lv mg mh mi mk ml mn mr ms mt my na nb nd ne ng nl nn no nr nv ny oc oj om or os pa pi pl ps pt qu rm rn ro ru rw sa sc sd se sg si sk sl sm sn so sq sr ss st su sv sw ta te tg th ti tk tl tn to tr ts tt tw ty ug uk ur uz ve vi vo wa wo xh yi yo za zh zu".split(" ")
3725
- ), ni = new Set(
3750
+ ), oi = new Set(
3726
3751
  "aar abk afr aka amh ara arg asm ava ave aym aze bak bam bel ben bih bis bod bos bre bul cat ces cha che chu chv cor cos cre cym dan deu div dzo ell eng epo est eus ewe fao fas fij fin fra fry ful gla gle glg glv grn guj hat hau hbs heb her hin hmo hrv hun hye ibo iii iku ile ina ind ipk isl ita jav jpn kal kan kas kat kau kaz khm kik kin kir kom kon kor kua kur lao lat lav lim lin lit ltz lub lug mah mal mar mkd mlg mlt mon mri msa mya nau nav nbl nde ndo nep nld nno nob nor nya oci oji ori orm oss pan pli pol por pus que roh ron run rus sag san sin slk slv sme smo sna snd som sot spa sqi srd srp ssw sun swa swe tah tam tat tel tgk tgl tha tir ton tsn tso tuk tur twi uig ukr urd uzb ven vie vol wln wol xho yid yor zha zho zul".split(" ")
3727
- ), ri = /^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i;
3728
- function pe(t) {
3729
- if (!ri.test(t)) return !1;
3730
- const a = t.split("-")[0].toLowerCase();
3731
- return a.length === 2 ? ii.has(a) : a.length === 3 ? !ni.has(a) : !1;
3752
+ ), si = /^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i;
3753
+ function fe(e) {
3754
+ if (!si.test(e)) return !1;
3755
+ const a = e.split("-")[0].toLowerCase();
3756
+ return a.length === 2 ? ri.has(a) : a.length === 3 ? !oi.has(a) : !1;
3732
3757
  }
3733
- const oi = {
3758
+ const li = {
3734
3759
  id: "html-lang-valid",
3735
3760
  wcag: ["3.1.1"],
3736
3761
  level: "A",
3737
3762
  description: "The lang attribute on <html> must have a valid value.",
3738
3763
  guidance: "The lang attribute must use a valid BCP 47 language tag. Use a 2 or 3 letter language code (e.g., 'en', 'fr', 'zh'), optionally followed by a region code (e.g., 'en-US', 'pt-BR'). Invalid tags prevent screen readers from correctly pronouncing content.",
3739
3764
  prompt: "Suggest the correct BCP 47 language tag based on the invalid value provided.",
3740
- run(t) {
3741
- var e;
3742
- const a = (e = t.documentElement.getAttribute("lang")) == null ? void 0 : e.trim();
3743
- return a && !pe(a) ? [{
3765
+ run(e) {
3766
+ var t;
3767
+ const a = (t = e.documentElement.getAttribute("lang")) == null ? void 0 : t.trim();
3768
+ return a && !fe(a) ? [{
3744
3769
  ruleId: "html-lang-valid",
3745
3770
  selector: "html",
3746
- html: d(t.documentElement),
3771
+ html: d(e.documentElement),
3747
3772
  impact: "serious",
3748
3773
  message: `Invalid lang attribute value "${a}".`
3749
3774
  }] : [];
3750
3775
  }
3751
3776
  };
3752
- function ce(t) {
3777
+ function ce(e) {
3753
3778
  var i;
3754
- const a = t.ownerDocument.createTreeWalker(t, NodeFilter.SHOW_TEXT);
3755
- let e;
3756
- for (; e = a.nextNode(); ) {
3757
- if (!e.data.trim()) continue;
3758
- const n = e.parentElement;
3779
+ const a = e.ownerDocument.createTreeWalker(e, NodeFilter.SHOW_TEXT);
3780
+ let t;
3781
+ for (; t = a.nextNode(); ) {
3782
+ if (!t.data.trim()) continue;
3783
+ const n = t.parentElement;
3759
3784
  if (!n || n instanceof HTMLElement && (n.hidden || n.style.display === "none")) continue;
3760
3785
  let r = n, o = !1;
3761
- for (; r && r !== t; ) {
3786
+ for (; r && r !== e; ) {
3762
3787
  if (r.hasAttribute("lang")) {
3763
3788
  o = !0;
3764
3789
  break;
@@ -3767,10 +3792,10 @@ function ce(t) {
3767
3792
  }
3768
3793
  if (!o) return !0;
3769
3794
  }
3770
- for (const n of t.querySelectorAll("img[alt]")) {
3795
+ for (const n of e.querySelectorAll("img[alt]")) {
3771
3796
  if (!((i = n.getAttribute("alt")) == null ? void 0 : i.trim())) continue;
3772
3797
  let o = n.parentElement, s = !1;
3773
- for (; o && o !== t; ) {
3798
+ for (; o && o !== e; ) {
3774
3799
  if (o.hasAttribute("lang")) {
3775
3800
  s = !0;
3776
3801
  break;
@@ -3781,81 +3806,81 @@ function ce(t) {
3781
3806
  }
3782
3807
  return !1;
3783
3808
  }
3784
- const si = {
3809
+ const ci = {
3785
3810
  id: "valid-lang",
3786
3811
  wcag: ["3.1.2"],
3787
3812
  level: "AA",
3788
3813
  description: "The lang attribute must have a valid value on all elements.",
3789
3814
  guidance: "When content in a different language appears within a page (e.g., a French quote in an English document), wrap it with a lang attribute to ensure correct pronunciation. The lang value must be a valid BCP 47 tag. Common codes: en, es, fr, de, zh, ja, pt, ar, ru.",
3790
3815
  prompt: "Identify the content's language and suggest the correct BCP 47 tag.",
3791
- run(t) {
3816
+ run(e) {
3792
3817
  const a = [];
3793
- for (const e of t.querySelectorAll("[lang]")) {
3794
- if (g(e) || e === t.documentElement) continue;
3795
- const i = e.getAttribute("lang"), n = i == null ? void 0 : i.trim();
3818
+ for (const t of e.querySelectorAll("[lang]")) {
3819
+ if (g(t) || t === e.documentElement) continue;
3820
+ const i = t.getAttribute("lang"), n = i == null ? void 0 : i.trim();
3796
3821
  if (i && !n) {
3797
- ce(e) && a.push({
3822
+ ce(t) && a.push({
3798
3823
  ruleId: "valid-lang",
3799
- selector: m(e),
3800
- html: d(e),
3824
+ selector: m(t),
3825
+ html: d(t),
3801
3826
  impact: "serious",
3802
3827
  message: "Empty lang attribute value."
3803
3828
  });
3804
3829
  continue;
3805
3830
  }
3806
- n && ce(e) && (pe(n) || a.push({
3831
+ n && ce(t) && (fe(n) || a.push({
3807
3832
  ruleId: "valid-lang",
3808
- selector: m(e),
3809
- html: d(e),
3833
+ selector: m(t),
3834
+ html: d(t),
3810
3835
  impact: "serious",
3811
3836
  message: `Invalid lang attribute value "${n}".`
3812
3837
  }));
3813
3838
  }
3814
3839
  return a;
3815
3840
  }
3816
- }, li = {
3841
+ }, ui = {
3817
3842
  id: "html-xml-lang-mismatch",
3818
3843
  wcag: ["3.1.1"],
3819
3844
  level: "A",
3820
3845
  description: "The lang and xml:lang attributes on <html> must match.",
3821
3846
  guidance: "In XHTML documents, if both lang and xml:lang are present, they must specify the same base language. Mismatched values confuse assistive technologies. Either remove xml:lang (preferred for HTML5) or ensure both attributes have identical values.",
3822
3847
  prompt: "Explain whether to remove xml:lang or align it with the lang value.",
3823
- run(t) {
3848
+ run(e) {
3824
3849
  var n, r;
3825
- const a = t.documentElement, e = (n = a.getAttribute("lang")) == null ? void 0 : n.trim().toLowerCase(), i = (r = a.getAttribute("xml:lang")) == null ? void 0 : r.trim().toLowerCase();
3826
- if (e && i) {
3827
- const o = e.split("-")[0], s = i.split("-")[0];
3850
+ const a = e.documentElement, t = (n = a.getAttribute("lang")) == null ? void 0 : n.trim().toLowerCase(), i = (r = a.getAttribute("xml:lang")) == null ? void 0 : r.trim().toLowerCase();
3851
+ if (t && i) {
3852
+ const o = t.split("-")[0], s = i.split("-")[0];
3828
3853
  if (o !== s)
3829
3854
  return [{
3830
3855
  ruleId: "html-xml-lang-mismatch",
3831
3856
  selector: "html",
3832
3857
  html: d(a),
3833
3858
  impact: "moderate",
3834
- message: `lang="${e}" and xml:lang="${i}" do not match.`
3859
+ message: `lang="${t}" and xml:lang="${i}" do not match.`
3835
3860
  }];
3836
3861
  }
3837
3862
  return [];
3838
3863
  }
3839
- }, ci = {
3864
+ }, di = {
3840
3865
  id: "td-headers-attr",
3841
3866
  wcag: ["1.3.1"],
3842
3867
  level: "A",
3843
3868
  description: "All cells in a table using headers attribute must reference valid header IDs.",
3844
3869
  guidance: "The headers attribute on table cells must reference IDs of header cells (th or td) within the same table. This creates explicit associations for screen readers. Verify all referenced IDs exist and spell them correctly. For simple tables, consider using scope on th elements instead.",
3845
3870
  prompt: "Identify the invalid header ID reference and suggest the correct ID or how to fix it.",
3846
- run(t) {
3871
+ run(e) {
3847
3872
  const a = [];
3848
- for (const e of t.querySelectorAll("td[headers]")) {
3849
- if (g(e)) continue;
3850
- const i = e.closest("table");
3873
+ for (const t of e.querySelectorAll("td[headers]")) {
3874
+ if (g(t)) continue;
3875
+ const i = t.closest("table");
3851
3876
  if (!i) continue;
3852
- const n = e.getAttribute("id"), r = e.getAttribute("headers").split(/\s+/);
3877
+ const n = t.getAttribute("id"), r = t.getAttribute("headers").split(/\s+/);
3853
3878
  for (const o of r) {
3854
3879
  if (o === n) {
3855
3880
  a.push({
3856
3881
  ruleId: "td-headers-attr",
3857
- selector: m(e),
3858
- html: d(e),
3882
+ selector: m(t),
3883
+ html: d(t),
3859
3884
  impact: "serious",
3860
3885
  message: `Headers attribute references the cell itself ("${o}").`
3861
3886
  });
@@ -3864,8 +3889,8 @@ const si = {
3864
3889
  if (!i.querySelector(`th#${CSS.escape(o)}, td#${CSS.escape(o)}`)) {
3865
3890
  a.push({
3866
3891
  ruleId: "td-headers-attr",
3867
- selector: m(e),
3868
- html: d(e),
3892
+ selector: m(t),
3893
+ html: d(t),
3869
3894
  impact: "serious",
3870
3895
  message: `Headers attribute references non-existent ID "${o}".`
3871
3896
  });
@@ -3875,68 +3900,68 @@ const si = {
3875
3900
  }
3876
3901
  return a;
3877
3902
  }
3878
- }, ui = {
3903
+ }, mi = {
3879
3904
  id: "th-has-data-cells",
3880
3905
  wcag: ["1.3.1"],
3881
3906
  level: "A",
3882
3907
  description: "Table headers should be associated with data cells.",
3883
3908
  guidance: "A table with header cells (th) but no data cells (td) is likely a misuse of table markup for layout or has missing content. Either add data cells that the headers describe, or use appropriate non-table markup if this is not tabular data.",
3884
3909
  prompt: "Explain whether this table needs data cells or if non-table layout would be more appropriate.",
3885
- run(t) {
3910
+ run(e) {
3886
3911
  const a = [];
3887
- for (const e of t.querySelectorAll("table")) {
3888
- if (g(e) || e.getAttribute("role") === "presentation" || e.getAttribute("role") === "none") continue;
3889
- const i = e.querySelectorAll("th"), n = e.querySelectorAll("td");
3912
+ for (const t of e.querySelectorAll("table")) {
3913
+ if (g(t) || t.getAttribute("role") === "presentation" || t.getAttribute("role") === "none") continue;
3914
+ const i = t.querySelectorAll("th"), n = t.querySelectorAll("td");
3890
3915
  i.length > 0 && n.length === 0 && a.push({
3891
3916
  ruleId: "th-has-data-cells",
3892
- selector: m(e),
3893
- html: d(e),
3917
+ selector: m(t),
3918
+ html: d(t),
3894
3919
  impact: "serious",
3895
3920
  message: "Table has header cells but no data cells."
3896
3921
  });
3897
3922
  }
3898
3923
  return a;
3899
3924
  }
3900
- }, di = {
3925
+ }, hi = {
3901
3926
  id: "td-has-header",
3902
3927
  wcag: ["1.3.1"],
3903
3928
  level: "A",
3904
3929
  description: "Data cells in tables larger than 3x3 should have associated headers.",
3905
3930
  guidance: "In complex tables, screen reader users need header associations to understand data cells. Use th elements with scope attribute, or the headers attribute on td elements. For simple tables (≤3x3), this is less critical as context is usually clear.",
3906
3931
  prompt: "Explain whether to use scope attributes on headers or headers attribute on this cell.",
3907
- run(t) {
3908
- var e, i;
3932
+ run(e) {
3933
+ var t, i;
3909
3934
  const a = [];
3910
- for (const n of t.querySelectorAll("table")) {
3935
+ for (const n of e.querySelectorAll("table")) {
3911
3936
  if (g(n) || n.getAttribute("role") === "presentation" || n.getAttribute("role") === "none") continue;
3912
3937
  const r = n.querySelectorAll("tr"), o = r.length;
3913
3938
  let s = 0;
3914
3939
  for (const u of r) {
3915
- const p = u.querySelectorAll("td, th");
3940
+ const h = u.querySelectorAll("td, th");
3916
3941
  let b = 0;
3917
- for (const f of p)
3942
+ for (const f of h)
3918
3943
  b += parseInt(f.getAttribute("colspan") || "1", 10);
3919
3944
  s = Math.max(s, b);
3920
3945
  }
3921
3946
  if (o <= 3 && s <= 3) continue;
3922
- const l = n.querySelector("th") !== null, h = n.querySelector("th[scope]") !== null, c = n.querySelector("td[headers]") !== null;
3947
+ const l = n.querySelector("th") !== null, p = n.querySelector("th[scope]") !== null, c = n.querySelector("td[headers]") !== null;
3923
3948
  if (l)
3924
3949
  for (const u of n.querySelectorAll("td")) {
3925
3950
  if (g(u) || u.hasAttribute("headers")) continue;
3926
- const p = u.closest("tr");
3927
- if (!p) continue;
3928
- const b = p.querySelector("th") !== null, f = Array.from(p.children).indexOf(u);
3929
- let y = !1;
3951
+ const h = u.closest("tr");
3952
+ if (!h) continue;
3953
+ const b = h.querySelector("th") !== null, f = Array.from(h.children).indexOf(u);
3954
+ let w = !1;
3930
3955
  const I = n.querySelector("thead");
3931
3956
  if (I) {
3932
3957
  const x = I.querySelector("tr");
3933
- x && ((e = x.querySelectorAll("th, td")[f]) == null ? void 0 : e.tagName.toLowerCase()) === "th" && (y = !0);
3958
+ x && ((t = x.querySelectorAll("th, td")[f]) == null ? void 0 : t.tagName.toLowerCase()) === "th" && (w = !0);
3934
3959
  }
3935
- if (!y) {
3960
+ if (!w) {
3936
3961
  const x = n.querySelector("tbody > tr, tr");
3937
- x && ((i = x.querySelectorAll("th, td")[f]) == null ? void 0 : i.tagName.toLowerCase()) === "th" && (y = !0);
3962
+ x && ((i = x.querySelectorAll("th, td")[f]) == null ? void 0 : i.tagName.toLowerCase()) === "th" && (w = !0);
3938
3963
  }
3939
- if (!b && !y && !h && !c) {
3964
+ if (!b && !w && !p && !c) {
3940
3965
  a.push({
3941
3966
  ruleId: "td-has-header",
3942
3967
  selector: m(u),
@@ -3950,20 +3975,20 @@ const si = {
3950
3975
  }
3951
3976
  return a;
3952
3977
  }
3953
- }, mi = {
3978
+ }, pi = {
3954
3979
  id: "scope-attr-valid",
3955
3980
  wcag: ["1.3.1"],
3956
3981
  level: "A",
3957
3982
  description: "The scope attribute on table headers must have a valid value.",
3958
3983
  guidance: "The scope attribute tells screen readers which cells a header applies to. Valid values are: row, col, rowgroup, colgroup. Using invalid values breaks the association between headers and cells.",
3959
3984
  prompt: "Explain which scope value (row, col, rowgroup, colgroup) is appropriate for this header.",
3960
- run(t) {
3985
+ run(e) {
3961
3986
  var i;
3962
- const a = [], e = /* @__PURE__ */ new Set(["row", "col", "rowgroup", "colgroup"]);
3963
- for (const n of t.querySelectorAll("th[scope]")) {
3987
+ const a = [], t = /* @__PURE__ */ new Set(["row", "col", "rowgroup", "colgroup"]);
3988
+ for (const n of e.querySelectorAll("th[scope]")) {
3964
3989
  if (g(n)) continue;
3965
3990
  const r = (i = n.getAttribute("scope")) == null ? void 0 : i.toLowerCase();
3966
- r && !e.has(r) && a.push({
3991
+ r && !t.has(r) && a.push({
3967
3992
  ruleId: "scope-attr-valid",
3968
3993
  selector: m(n),
3969
3994
  html: d(n),
@@ -3973,7 +3998,7 @@ const si = {
3973
3998
  }
3974
3999
  return a;
3975
4000
  }
3976
- }, hi = {
4001
+ }, gi = {
3977
4002
  id: "empty-table-header",
3978
4003
  wcag: [],
3979
4004
  level: "A",
@@ -3981,113 +4006,113 @@ const si = {
3981
4006
  description: "Table header cells should have visible text.",
3982
4007
  guidance: "Empty table headers provide no information to screen reader users. Either add descriptive text to the header, or if the header is intentionally empty (like a corner cell), consider using a td element instead or adding a visually hidden label.",
3983
4008
  prompt: "Suggest header text based on the column/row content, or explain if this should be a td instead.",
3984
- run(t) {
4009
+ run(e) {
3985
4010
  const a = [];
3986
- for (const e of t.querySelectorAll("th")) {
3987
- if (g(e)) continue;
3988
- const i = e.closest("table");
3989
- (i == null ? void 0 : i.getAttribute("role")) === "presentation" || (i == null ? void 0 : i.getAttribute("role")) === "none" || v(e) || a.push({
4011
+ for (const t of e.querySelectorAll("th")) {
4012
+ if (g(t)) continue;
4013
+ const i = t.closest("table");
4014
+ (i == null ? void 0 : i.getAttribute("role")) === "presentation" || (i == null ? void 0 : i.getAttribute("role")) === "none" || v(t) || a.push({
3990
4015
  ruleId: "empty-table-header",
3991
- selector: m(e),
3992
- html: d(e),
4016
+ selector: m(t),
4017
+ html: d(t),
3993
4018
  impact: "minor",
3994
4019
  message: "Table header cell is empty. Add text or use aria-label."
3995
4020
  });
3996
4021
  }
3997
4022
  return a;
3998
4023
  }
3999
- }, O = ["aria-labelledby", "aria-describedby", "aria-controls", "aria-owns", "aria-flowto"], pi = {
4024
+ }, F = ["aria-labelledby", "aria-describedby", "aria-controls", "aria-owns", "aria-flowto"], bi = {
4000
4025
  id: "duplicate-id-aria",
4001
4026
  wcag: ["4.1.2"],
4002
4027
  level: "A",
4003
4028
  description: "IDs used in ARIA and label associations must be unique to avoid broken references.",
4004
4029
  guidance: "When aria-labelledby, aria-describedby, aria-controls, or label[for] reference a duplicate ID, only the first matching element is used. This breaks the intended relationship and may leave controls unnamed or descriptions missing. Ensure IDs referenced by ARIA attributes and label associations are unique throughout the document.",
4005
4030
  prompt: "Identify which attribute references this ID and suggest a unique replacement.",
4006
- run(t) {
4007
- const a = [], e = /* @__PURE__ */ new Set();
4008
- for (const n of t.querySelectorAll("[aria-labelledby], [aria-describedby], [aria-controls], [aria-owns], [aria-flowto]"))
4009
- for (const r of O) {
4031
+ run(e) {
4032
+ const a = [], t = /* @__PURE__ */ new Set();
4033
+ for (const n of e.querySelectorAll("[aria-labelledby], [aria-describedby], [aria-controls], [aria-owns], [aria-flowto]"))
4034
+ for (const r of F) {
4010
4035
  const o = n.getAttribute(r);
4011
- o && o.split(/\s+/).forEach((s) => e.add(s));
4036
+ o && o.split(/\s+/).forEach((s) => t.add(s));
4012
4037
  }
4013
- for (const n of t.querySelectorAll("label[for]")) {
4038
+ for (const n of e.querySelectorAll("label[for]")) {
4014
4039
  const r = n.getAttribute("for");
4015
- r && e.add(r);
4040
+ r && t.add(r);
4016
4041
  }
4017
4042
  const i = /* @__PURE__ */ new Map();
4018
- for (const n of t.querySelectorAll("[id]"))
4019
- e.has(n.id) && (n instanceof HTMLElement && (n.style.display === "none" || n.style.visibility === "hidden" || n.hidden) || i.set(n.id, (i.get(n.id) ?? 0) + 1));
4043
+ for (const n of e.querySelectorAll("[id]"))
4044
+ t.has(n.id) && (n instanceof HTMLElement && (n.style.display === "none" || n.style.visibility === "hidden" || n.hidden) || i.set(n.id, (i.get(n.id) ?? 0) + 1));
4020
4045
  for (const [n, r] of i) {
4021
4046
  if (r <= 1) continue;
4022
- const o = t.querySelectorAll(`#${CSS.escape(n)}`), s = t.querySelector(
4023
- O.map((c) => `[${c}~="${CSS.escape(n)}"]`).join(", ")
4024
- ), l = t.querySelector(`label[for="${CSS.escape(n)}"]`);
4025
- let h;
4047
+ const o = e.querySelectorAll(`#${CSS.escape(n)}`), s = e.querySelector(
4048
+ F.map((c) => `[${c}~="${CSS.escape(n)}"]`).join(", ")
4049
+ ), l = e.querySelector(`label[for="${CSS.escape(n)}"]`);
4050
+ let p;
4026
4051
  if (s) {
4027
- const c = O.find(
4052
+ const c = F.find(
4028
4053
  (u) => {
4029
- var p;
4030
- return (p = s.getAttribute(u)) == null ? void 0 : p.split(/\s+/).includes(n);
4054
+ var h;
4055
+ return (h = s.getAttribute(u)) == null ? void 0 : h.split(/\s+/).includes(n);
4031
4056
  }
4032
4057
  );
4033
- c && (h = c);
4034
- } else l && (h = "label[for]");
4058
+ c && (p = c);
4059
+ } else l && (p = "label[for]");
4035
4060
  a.push({
4036
4061
  ruleId: "duplicate-id-aria",
4037
4062
  selector: m(o[1]),
4038
4063
  html: d(o[1]),
4039
4064
  impact: "critical",
4040
- message: `Duplicate ID "${n}" referenced by ${h ?? "an accessibility attribute"}.`,
4041
- context: `First element: ${d(o[0])}${h ? `
4042
- Referenced by: ${h}` : ""}`
4065
+ message: `Duplicate ID "${n}" referenced by ${p ?? "an accessibility attribute"}.`,
4066
+ context: `First element: ${d(o[0])}${p ? `
4067
+ Referenced by: ${p}` : ""}`
4043
4068
  });
4044
4069
  }
4045
4070
  return a;
4046
4071
  }
4047
- }, gi = {
4072
+ }, fi = {
4048
4073
  id: "video-caption",
4049
4074
  wcag: ["1.2.2"],
4050
4075
  level: "A",
4051
4076
  description: "Video elements must have captions via <track kind='captions'>.",
4052
4077
  guidance: "Captions provide text alternatives for audio content in videos, benefiting deaf users and those who cannot hear audio. Add a <track> element with kind='captions' pointing to a WebVTT caption file. Captions should include both dialogue and important sound effects.",
4053
4078
  prompt: "Explain how to add a captions track element to this video.",
4054
- run(t) {
4079
+ run(e) {
4055
4080
  const a = [];
4056
- for (const e of t.querySelectorAll("video")) {
4057
- if (g(e) || e.hasAttribute("muted") || e.hasAttribute("autoplay")) continue;
4058
- e.querySelector('track[kind="captions"], track[kind="subtitles"]') || a.push({
4081
+ for (const t of e.querySelectorAll("video")) {
4082
+ if (g(t) || t.hasAttribute("muted") || t.hasAttribute("autoplay")) continue;
4083
+ t.querySelector('track[kind="captions"], track[kind="subtitles"]') || a.push({
4059
4084
  ruleId: "video-caption",
4060
- selector: m(e),
4061
- html: d(e),
4085
+ selector: m(t),
4086
+ html: d(t),
4062
4087
  impact: "critical",
4063
4088
  message: "Video element has no captions track."
4064
4089
  });
4065
4090
  }
4066
4091
  return a;
4067
4092
  }
4068
- }, bi = {
4093
+ }, vi = {
4069
4094
  id: "audio-caption",
4070
4095
  wcag: ["1.2.1"],
4071
4096
  level: "A",
4072
4097
  description: "Audio elements should have a text alternative or transcript.",
4073
4098
  guidance: "Audio-only content like podcasts or recordings needs a text alternative for deaf users. Provide a transcript either on the same page or linked nearby. The transcript should include all spoken content and descriptions of relevant sounds.",
4074
4099
  prompt: "Explain options for providing a text alternative: transcript link or aria-describedby.",
4075
- run(t) {
4100
+ run(e) {
4076
4101
  const a = [];
4077
- for (const e of t.querySelectorAll("audio")) {
4078
- if (g(e) || e.querySelector('track[kind="captions"], track[kind="descriptions"]') || e.hasAttribute("aria-describedby")) continue;
4079
- const n = e.parentElement;
4102
+ for (const t of e.querySelectorAll("audio")) {
4103
+ if (g(t) || t.querySelector('track[kind="captions"], track[kind="descriptions"]') || t.hasAttribute("aria-describedby")) continue;
4104
+ const n = t.parentElement;
4080
4105
  n && n.querySelector('a[href*="transcript"], a[href*="text"]') || a.push({
4081
4106
  ruleId: "audio-caption",
4082
- selector: m(e),
4083
- html: d(e),
4107
+ selector: m(t),
4108
+ html: d(t),
4084
4109
  impact: "critical",
4085
4110
  message: "Audio element has no transcript or text alternative. Add a transcript or track element."
4086
4111
  });
4087
4112
  }
4088
4113
  return a;
4089
4114
  }
4090
- }, fi = /* @__PURE__ */ new Set([
4115
+ }, wi = /* @__PURE__ */ new Set([
4091
4116
  "SCRIPT",
4092
4117
  "STYLE",
4093
4118
  "NOSCRIPT",
@@ -4103,231 +4128,256 @@ Referenced by: ${h}` : ""}`
4103
4128
  "BR",
4104
4129
  "HR"
4105
4130
  ]);
4106
- function ue([t, a, e]) {
4107
- return "#" + [t, a, e].map((i) => i.toString(16).padStart(2, "0")).join("");
4131
+ function ue([e, a, t]) {
4132
+ return "#" + [e, a, t].map((i) => i.toString(16).padStart(2, "0")).join("");
4108
4133
  }
4109
- function vi(t) {
4110
- return t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement || t instanceof HTMLSelectElement || t instanceof HTMLButtonElement ? t.disabled : !!(t.closest("fieldset[disabled]") || t.getAttribute("aria-disabled") === "true");
4134
+ function yi(e) {
4135
+ return e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement || e instanceof HTMLSelectElement || e instanceof HTMLButtonElement ? e.disabled : !!(e.closest("fieldset[disabled]") || e.getAttribute("aria-disabled") === "true");
4111
4136
  }
4112
- function yi(t, a) {
4113
- if (t.tagName !== "LABEL") return !1;
4114
- const e = t, i = e.htmlFor;
4137
+ function Ai(e, a) {
4138
+ if (e.tagName !== "LABEL") return !1;
4139
+ const t = e, i = t.htmlFor;
4115
4140
  if (i) {
4116
4141
  const o = a.getElementById(i);
4117
4142
  if (o && (o.disabled || o.getAttribute("aria-disabled") === "true")) return !0;
4118
4143
  }
4119
- const n = e.querySelector("input, select, textarea, button");
4144
+ const n = t.querySelector("input, select, textarea, button");
4120
4145
  if (n && (n.disabled || n.getAttribute("aria-disabled") === "true")) return !0;
4121
- const r = e.id;
4146
+ const r = t.id;
4122
4147
  return !!(r && a.querySelector(`[aria-labelledby~="${r}"][aria-disabled="true"]`));
4123
4148
  }
4124
- function wi(t) {
4125
- const a = t.clip;
4149
+ function xi(e) {
4150
+ const a = e.clip;
4126
4151
  if (a && a.startsWith("rect(")) {
4127
4152
  const i = a.match(/[\d.]+/g);
4128
4153
  if (!i || i.every((n) => parseFloat(n) === 0)) return !0;
4129
4154
  }
4130
- const e = t.clipPath;
4131
- if (e === "inset(50%)" || e === "inset(100%)") return !0;
4132
- if (t.overflow === "hidden" && t.position === "absolute") {
4133
- const i = parseFloat(t.width), n = parseFloat(t.height);
4155
+ const t = e.clipPath;
4156
+ if (t === "inset(50%)" || t === "inset(100%)") return !0;
4157
+ if (e.overflow === "hidden" && e.position === "absolute") {
4158
+ const i = parseFloat(e.width), n = parseFloat(e.height);
4134
4159
  if (i <= 1 && n <= 1) return !0;
4135
4160
  }
4136
4161
  return !1;
4137
4162
  }
4138
- function Ai(t) {
4139
- if (g(t)) return !0;
4140
- let a = t;
4163
+ function Si(e) {
4164
+ if (g(e)) return !0;
4165
+ let a = e;
4141
4166
  for (; a; ) {
4142
- const e = w(a);
4143
- if (e.display === "none" || e.visibility === "hidden" || wi(e)) return !0;
4167
+ const t = y(a);
4168
+ if (t.display === "none" || t.visibility === "hidden" || xi(t)) return !0;
4144
4169
  a = a.parentElement;
4145
4170
  }
4146
4171
  return !1;
4147
4172
  }
4148
- function xi(t) {
4149
- let a = 1, e = t;
4150
- for (; e; ) {
4151
- const i = w(e), n = parseFloat(i.opacity);
4152
- isNaN(n) || (a *= n), e = e.parentElement;
4173
+ function ki(e) {
4174
+ let a = 1, t = e;
4175
+ for (; t; ) {
4176
+ const i = y(t), n = parseFloat(i.opacity);
4177
+ isNaN(n) || (a *= n), t = t.parentElement;
4153
4178
  }
4154
4179
  return a;
4155
4180
  }
4156
- function Si(t) {
4157
- let a = t;
4181
+ const Ii = {
4182
+ grayscale: 0,
4183
+ blur: 0,
4184
+ "hue-rotate": 0,
4185
+ invert: 0,
4186
+ sepia: 0,
4187
+ brightness: 1,
4188
+ contrast: 1,
4189
+ saturate: 1,
4190
+ opacity: 1
4191
+ };
4192
+ function Ti(e) {
4193
+ const a = parseFloat(e);
4194
+ return isNaN(a) ? NaN : e.trim().endsWith("%") ? a / 100 : a;
4195
+ }
4196
+ const de = /([a-z-]+)\(([^)]*)\)/g;
4197
+ function me(e) {
4198
+ let a, t = !1;
4199
+ for (de.lastIndex = 0; a = de.exec(e); ) {
4200
+ t = !0;
4201
+ const i = Ii[a[1]];
4202
+ if (i === void 0 || Ti(a[2]) !== i) return !1;
4203
+ }
4204
+ return t;
4205
+ }
4206
+ function Ei(e) {
4207
+ let a = e;
4158
4208
  for (; a; ) {
4159
- const e = w(a), i = e.filter;
4160
- if (i && i !== "none" && i !== "initial") return !0;
4161
- const n = e.mixBlendMode;
4209
+ const t = y(a), i = t.filter;
4210
+ if (i && i !== "none" && i !== "initial" && !me(i)) return !0;
4211
+ const n = t.mixBlendMode;
4162
4212
  if (n && n !== "normal" && n !== "initial") return !0;
4163
- const r = e.backdropFilter;
4164
- if (r && r !== "none" && r !== "initial") return !0;
4213
+ const r = t.backdropFilter;
4214
+ if (r && r !== "none" && r !== "initial" && !me(r)) return !0;
4165
4215
  a = a.parentElement;
4166
4216
  }
4167
4217
  return !1;
4168
4218
  }
4169
- function ki(t) {
4170
- return t.closest("select") !== null;
4219
+ function Ci(e) {
4220
+ return e.closest("select") !== null;
4171
4221
  }
4172
- const Ii = {
4222
+ const Li = {
4173
4223
  id: "color-contrast",
4174
4224
  wcag: ["1.4.3"],
4175
4225
  level: "AA",
4176
4226
  description: "Text elements must have sufficient color contrast against the background.",
4177
4227
  guidance: "WCAG SC 1.4.3 requires a contrast ratio of at least 4.5:1 for normal text and 3:1 for large text (>=24px or >=18.66px bold). Increase the contrast by darkening the text or lightening the background, or vice versa.",
4178
4228
  prompt: "Suggest changing the text or background color to meet the minimum contrast ratio.",
4179
- run(t) {
4180
- const a = [], e = t.body;
4181
- if (!e) return [];
4182
- const i = t.createTreeWalker(e, NodeFilter.SHOW_TEXT), n = /* @__PURE__ */ new Set();
4229
+ run(e) {
4230
+ const a = [], t = e.body;
4231
+ if (!t) return [];
4232
+ const i = e.createTreeWalker(t, NodeFilter.SHOW_TEXT), n = /* @__PURE__ */ new Set();
4183
4233
  let r;
4184
4234
  for (; r = i.nextNode(); ) {
4185
4235
  if (!r.textContent || !r.textContent.trim()) continue;
4186
4236
  const o = r.parentElement;
4187
- if (!o || n.has(o) || (n.add(o), fi.has(o.tagName))) continue;
4237
+ if (!o || n.has(o) || (n.add(o), wi.has(o.tagName))) continue;
4188
4238
  const s = o.tagName;
4189
- if (s === "BODY" || s === "HTML" || ki(o) || vi(o) || yi(o, t) || Ai(o)) continue;
4190
- const l = w(o);
4191
- if (parseFloat(l.opacity) === 0 || xi(o) < 0.1) continue;
4192
- const h = l.textShadow;
4193
- if (h && h !== "none" && h !== "initial" || Si(o)) continue;
4239
+ if (s === "BODY" || s === "HTML" || Ci(o) || yi(o) || Ai(o, e) || Si(o)) continue;
4240
+ const l = y(o);
4241
+ if (parseFloat(l.opacity) === 0 || ki(o) < 0.1) continue;
4242
+ const p = l.textShadow;
4243
+ if (p && p !== "none" && p !== "initial" || Ei(o)) continue;
4194
4244
  const c = N(l.color);
4195
4245
  if (!c) continue;
4196
4246
  const u = l.color.match(/rgba\(.+?,\s*([\d.]+)\s*\)/) || l.color.match(/rgba?\(.+?\/\s*([\d.]+%?)\s*\)/);
4197
- if (u && (u[1].endsWith("%") ? parseFloat(u[1]) / 100 : parseFloat(u[1])) === 0 || _e(o)) continue;
4198
- const p = Be(o);
4199
- if (!p) continue;
4200
- const b = R(c[0], c[1], c[2]), f = R(p[0], p[1], p[2]), y = me(b, f), I = Ve(o) ? 3 : 4.5;
4201
- if (y < I) {
4202
- const x = Math.round(y * 100) / 100, M = ue(c), ye = ue(p);
4247
+ if (u && (u[1].endsWith("%") ? parseFloat(u[1]) / 100 : parseFloat(u[1])) === 0 || je(o)) continue;
4248
+ const h = Be(o);
4249
+ if (!h) continue;
4250
+ const b = R(c[0], c[1], c[2]), f = R(h[0], h[1], h[2]), w = ge(b, f), I = Ue(o) ? 3 : 4.5;
4251
+ if (w < I) {
4252
+ const x = Math.round(w * 100) / 100, M = ue(c), xe = ue(h);
4203
4253
  a.push({
4204
4254
  ruleId: "color-contrast",
4205
4255
  selector: m(o),
4206
4256
  html: d(o),
4207
4257
  impact: "serious",
4208
4258
  message: `Insufficient color contrast ratio of ${x}:1 (required ${I}:1).`,
4209
- context: `foreground: ${M} rgb(${c.join(", ")}), background: ${ye} rgb(${p.join(", ")}), ratio: ${x}:1, required: ${I}:1`
4259
+ context: `foreground: ${M} rgb(${c.join(", ")}), background: ${xe} rgb(${h.join(", ")}), ratio: ${x}:1, required: ${I}:1`
4210
4260
  });
4211
4261
  }
4212
4262
  }
4213
4263
  return a;
4214
4264
  }
4215
- }, ge = [
4265
+ }, ve = [
4216
4266
  // Document Structure
4217
- Zt,
4218
- ea,
4219
4267
  ta,
4220
4268
  aa,
4221
4269
  ia,
4270
+ na,
4222
4271
  ra,
4223
- oa,
4272
+ sa,
4224
4273
  la,
4225
4274
  ua,
4275
+ ma,
4226
4276
  // Images
4227
- ze,
4228
4277
  Ge,
4229
- Ye,
4230
4278
  Xe,
4279
+ Ke,
4231
4280
  Je,
4232
- Qe,
4281
+ Ze,
4233
4282
  et,
4234
- tt,
4235
- rt,
4283
+ at,
4284
+ it,
4285
+ st,
4236
4286
  // Forms
4237
- ut,
4238
- dt,
4239
4287
  mt,
4240
4288
  ht,
4241
- wt,
4242
- At,
4289
+ pt,
4290
+ gt,
4243
4291
  xt,
4244
- // Keyboard
4292
+ St,
4245
4293
  kt,
4294
+ // Keyboard
4246
4295
  Tt,
4247
- $t,
4248
- Mt,
4296
+ Ct,
4249
4297
  Ht,
4250
- // Structure
4251
4298
  Dt,
4252
- na,
4253
- da,
4254
- Ot,
4255
- Bt,
4256
4299
  Ft,
4300
+ // Structure
4257
4301
  Wt,
4302
+ oa,
4303
+ ha,
4304
+ Ot,
4305
+ Bt,
4258
4306
  _t,
4259
4307
  Pt,
4260
4308
  jt,
4261
4309
  Vt,
4262
4310
  zt,
4263
4311
  Ut,
4312
+ Gt,
4264
4313
  Yt,
4265
- Qt,
4266
- Xt,
4314
+ Kt,
4315
+ ea,
4267
4316
  Jt,
4317
+ Zt,
4268
4318
  // ARIA
4269
- ma,
4270
- ha,
4271
4319
  pa,
4320
+ ga,
4272
4321
  ba,
4273
- Aa,
4274
- ka,
4275
- Ea,
4276
- Ca,
4277
- Ra,
4278
- Na,
4322
+ va,
4323
+ Sa,
4324
+ Ta,
4325
+ La,
4326
+ qa,
4279
4327
  $a,
4280
4328
  Ma,
4281
4329
  Ha,
4282
4330
  Da,
4283
- Oa,
4284
- Ba,
4285
4331
  Fa,
4286
4332
  Wa,
4333
+ Oa,
4334
+ Ba,
4287
4335
  _a,
4288
- Va,
4289
- va,
4290
- za,
4291
- // Links
4336
+ Pa,
4337
+ ja,
4338
+ Ua,
4339
+ ya,
4292
4340
  Ga,
4293
- Ya,
4294
- ti,
4341
+ // Links
4342
+ Xa,
4343
+ Ka,
4344
+ ii,
4295
4345
  // Language
4296
- ai,
4297
- oi,
4298
- si,
4346
+ ni,
4299
4347
  li,
4300
- // Tables
4301
4348
  ci,
4302
4349
  ui,
4350
+ // Tables
4303
4351
  di,
4304
4352
  mi,
4305
4353
  hi,
4306
- // Parsing
4307
4354
  pi,
4308
- // Media
4309
4355
  gi,
4356
+ // Parsing
4310
4357
  bi,
4358
+ // Media
4359
+ fi,
4360
+ vi,
4311
4361
  // Color
4312
- Ii
4362
+ Li
4313
4363
  ];
4314
- let G = [], be = /* @__PURE__ */ new Set();
4315
- function Li(t) {
4316
- t.additionalRules && (G = t.additionalRules), t.disabledRules && (be = new Set(t.disabledRules));
4364
+ let G = [], we = /* @__PURE__ */ new Set();
4365
+ function $i(e) {
4366
+ e.additionalRules && (G = e.additionalRules), e.disabledRules && (we = new Set(e.disabledRules));
4317
4367
  }
4318
- function fe() {
4319
- return ge.filter((a) => !be.has(a.id)).concat(G);
4368
+ function ye() {
4369
+ return ve.filter((a) => !we.has(a.id)).concat(G);
4320
4370
  }
4321
- function qi(t) {
4322
- ve();
4323
- const a = fe(), e = [];
4371
+ function Mi(e) {
4372
+ Ae();
4373
+ const a = ye(), t = [];
4324
4374
  let i = 0;
4325
4375
  return {
4326
4376
  processChunk(n) {
4327
4377
  const r = performance.now();
4328
4378
  for (; i < a.length; ) {
4329
4379
  try {
4330
- e.push(...a[i].run(t));
4380
+ t.push(...a[i].run(e));
4331
4381
  } catch {
4332
4382
  }
4333
4383
  if (i++, performance.now() - r >= n) break;
@@ -4335,55 +4385,55 @@ function qi(t) {
4335
4385
  return i < a.length;
4336
4386
  },
4337
4387
  getViolations() {
4338
- return e;
4388
+ return t;
4339
4389
  }
4340
4390
  };
4341
4391
  }
4342
- function ve() {
4343
- Ie(), we(), Ae(), Oe(), De(), Ee();
4392
+ function Ae() {
4393
+ Ce(), Se(), ke(), Oe(), We(), Le();
4344
4394
  }
4345
- function Ri(t) {
4395
+ function Hi(e) {
4346
4396
  var i;
4347
- ve();
4348
- const a = fe(), e = [];
4397
+ Ae();
4398
+ const a = ye(), t = [];
4349
4399
  for (const n of a)
4350
4400
  try {
4351
- e.push(...n.run(t));
4401
+ t.push(...n.run(e));
4352
4402
  } catch {
4353
4403
  }
4354
4404
  return {
4355
- url: ((i = t.location) == null ? void 0 : i.href) ?? "",
4405
+ url: ((i = e.location) == null ? void 0 : i.href) ?? "",
4356
4406
  timestamp: Date.now(),
4357
- violations: e,
4407
+ violations: t,
4358
4408
  ruleCount: a.length
4359
4409
  };
4360
4410
  }
4361
- const Ti = new Map(ge.map((t) => [t.id, t]));
4362
- function Ni(t) {
4363
- const a = Ti.get(t);
4364
- return a || G.find((e) => e.id === t);
4411
+ const qi = new Map(ve.map((e) => [e.id, e]));
4412
+ function Di(e) {
4413
+ const a = qi.get(e);
4414
+ return a || G.find((t) => t.id === e);
4365
4415
  }
4366
4416
  export {
4367
- ve as clearAllCaches,
4368
- De as clearAriaAttrAuditCache,
4369
- Ie as clearAriaHiddenCache,
4417
+ Ae as clearAllCaches,
4418
+ We as clearAriaAttrAuditCache,
4419
+ Ce as clearAriaHiddenCache,
4370
4420
  Oe as clearColorCaches,
4371
- we as clearComputedRoleCache,
4421
+ Se as clearComputedRoleCache,
4372
4422
  k as compileDeclarativeRule,
4373
- Li as configureRules,
4374
- qi as createChunkedAudit,
4423
+ $i as configureRules,
4424
+ Mi as createChunkedAudit,
4375
4425
  v as getAccessibleName,
4376
4426
  A as getAccessibleTextContent,
4377
- fe as getActiveRules,
4427
+ ye as getActiveRules,
4378
4428
  q as getComputedRole,
4379
4429
  d as getHtmlSnippet,
4380
- de as getImplicitRole,
4381
- Ni as getRuleById,
4430
+ he as getImplicitRole,
4431
+ Di as getRuleById,
4382
4432
  m as getSelector,
4383
4433
  g as isAriaHidden,
4384
- ke as isValidRole,
4385
- Ei as querySelectorShadowAware,
4386
- ge as rules,
4387
- Ri as runAudit,
4388
- Ci as validateDeclarativeRule
4434
+ Ee as isValidRole,
4435
+ Ri as querySelectorShadowAware,
4436
+ ve as rules,
4437
+ Hi as runAudit,
4438
+ Ni as validateDeclarativeRule
4389
4439
  };