@accesslint/core 0.3.4 → 0.3.6

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,5 +1,5 @@
1
1
  let O = /* @__PURE__ */ new WeakMap();
2
- function fe() {
2
+ function ve() {
3
3
  O = /* @__PURE__ */ new WeakMap();
4
4
  }
5
5
  function ue(t) {
@@ -117,71 +117,71 @@ function ue(t) {
117
117
  return null;
118
118
  }
119
119
  }
120
- function C(t) {
120
+ function L(t) {
121
121
  var n;
122
122
  const a = O.get(t);
123
123
  if (a !== void 0) return a;
124
124
  const i = ((n = t.getAttribute("role")) == null ? void 0 : n.trim().toLowerCase()) || null || ue(t);
125
125
  return O.set(t, i), i;
126
126
  }
127
- let W = /* @__PURE__ */ new WeakMap();
128
- function ve() {
129
- W = /* @__PURE__ */ new WeakMap();
127
+ let B = /* @__PURE__ */ new WeakMap();
128
+ function ye() {
129
+ B = /* @__PURE__ */ new WeakMap();
130
130
  }
131
131
  function v(t) {
132
- const a = W.get(t);
132
+ const a = B.get(t);
133
133
  if (a !== void 0) return a;
134
134
  const e = we(t);
135
- return W.set(t, e), e;
135
+ return B.set(t, e), e;
136
136
  }
137
137
  function we(t) {
138
- var r, o, s, u, h;
138
+ var r, o, s, l, h;
139
139
  const a = t.getAttribute("aria-labelledby");
140
140
  if (a) {
141
- const l = a.split(/\s+/).map((m) => {
142
- const g = t.ownerDocument.getElementById(m);
143
- return g ? y(g).trim() : "";
141
+ const c = a.split(/\s+/).map((d) => {
142
+ const g = t.ownerDocument.getElementById(d);
143
+ return g ? w(g).trim() : "";
144
144
  }).filter(Boolean);
145
- if (l.length) return l.join(" ");
145
+ if (c.length) return c.join(" ");
146
146
  }
147
147
  const e = (r = t.getAttribute("aria-label")) == null ? void 0 : r.trim();
148
148
  if (e) return e;
149
149
  if (t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement || t instanceof HTMLSelectElement) {
150
150
  if (t.id) {
151
- const g = t.ownerDocument.querySelector(`label[for="${CSS.escape(t.id)}"]`), b = g ? y(g).trim() : "";
151
+ const g = t.ownerDocument.querySelector(`label[for="${CSS.escape(t.id)}"]`), b = g ? w(g).trim() : "";
152
152
  if (b) return b;
153
153
  }
154
- const l = t.closest("label"), m = l ? y(l).trim() : "";
155
- if (m) return m;
154
+ const c = t.closest("label"), d = c ? w(c).trim() : "";
155
+ if (d) return d;
156
156
  }
157
157
  const i = (o = t.getAttribute("title")) == null ? void 0 : o.trim();
158
158
  if (i) return i;
159
159
  if (t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement) {
160
- const l = (s = t.getAttribute("placeholder")) == null ? void 0 : s.trim();
161
- if (l) return l;
160
+ const c = (s = t.getAttribute("placeholder")) == null ? void 0 : s.trim();
161
+ if (c) return c;
162
162
  }
163
163
  const n = t.tagName.toLowerCase();
164
164
  if (n === "fieldset") {
165
- const l = t.querySelector(":scope > legend");
166
- if (l) {
167
- const m = y(l).trim();
168
- if (m) return m;
165
+ const c = t.querySelector(":scope > legend");
166
+ if (c) {
167
+ const d = w(c).trim();
168
+ if (d) return d;
169
169
  }
170
170
  }
171
171
  if (n === "table") {
172
- const l = t.querySelector(":scope > caption");
173
- if (l) {
174
- const m = y(l).trim();
175
- if (m) return m;
172
+ const c = t.querySelector(":scope > caption");
173
+ if (c) {
174
+ const d = w(c).trim();
175
+ if (d) return d;
176
176
  }
177
177
  }
178
178
  if (!(t instanceof HTMLInputElement)) {
179
- const l = y(t).trim();
180
- if (l) return l;
179
+ const c = w(t).trim();
180
+ if (c) return c;
181
181
  }
182
- return t instanceof HTMLImageElement || t instanceof HTMLAreaElement ? ((u = t.alt) == null ? void 0 : u.trim()) ?? "" : t instanceof HTMLInputElement && t.type === "image" ? ((h = t.alt) == null ? void 0 : h.trim()) ?? "" : "";
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()) ?? "" : "";
183
183
  }
184
- const ye = /* @__PURE__ */ new Set([
184
+ const Ae = /* @__PURE__ */ new Set([
185
185
  "alert",
186
186
  "alertdialog",
187
187
  "application",
@@ -265,64 +265,64 @@ const ye = /* @__PURE__ */ new Set([
265
265
  "treegrid",
266
266
  "treeitem"
267
267
  ]);
268
- function Ae(t) {
268
+ function xe(t) {
269
269
  const a = t.trim().toLowerCase().replace(/[\u201C\u201D\u2018\u2019\u00AB\u00BB]/g, "");
270
- return ye.has(a);
270
+ return Ae.has(a);
271
271
  }
272
- let B = /* @__PURE__ */ new WeakMap();
272
+ let F = /* @__PURE__ */ new WeakMap();
273
273
  function Se() {
274
- B = /* @__PURE__ */ new WeakMap();
274
+ F = /* @__PURE__ */ new WeakMap();
275
275
  }
276
276
  function p(t) {
277
- const a = B.get(t);
277
+ const a = F.get(t);
278
278
  if (a !== void 0) return a;
279
279
  let e;
280
- return t.getAttribute("aria-hidden") === "true" || t instanceof HTMLElement && (t.hidden || t.style.display === "none") ? e = !0 : t.parentElement ? e = p(t.parentElement) : e = !1, B.set(t, e), e;
280
+ return t.getAttribute("aria-hidden") === "true" || t instanceof HTMLElement && (t.hidden || t.style.display === "none") ? e = !0 : t.parentElement ? e = p(t.parentElement) : e = !1, F.set(t, e), e;
281
281
  }
282
- function xe(t) {
282
+ function ke(t) {
283
283
  return !!(t.getAttribute("aria-hidden") === "true" || t instanceof HTMLElement && (t.hidden || t.style.display === "none"));
284
284
  }
285
- function y(t) {
285
+ function w(t) {
286
286
  var e, i, n, r, o;
287
287
  let a = "";
288
288
  for (const s of t.childNodes)
289
289
  if (s.nodeType === 3)
290
290
  a += s.textContent ?? "";
291
291
  else if (s.nodeType === 1) {
292
- const u = s;
293
- if (!xe(u)) {
294
- const h = (e = u.tagName) == null ? void 0 : e.toLowerCase();
292
+ const l = s;
293
+ if (!ke(l)) {
294
+ const h = (e = l.tagName) == null ? void 0 : e.toLowerCase();
295
295
  if (h === "img" || h === "area") {
296
- const l = u.getAttribute("aria-labelledby");
297
- if (l) {
298
- const m = l.split(/\s+/).map((g) => {
296
+ const c = l.getAttribute("aria-labelledby");
297
+ if (c) {
298
+ const d = c.split(/\s+/).map((g) => {
299
299
  var b, f;
300
- return ((f = (b = u.ownerDocument.getElementById(g)) == null ? void 0 : b.textContent) == null ? void 0 : f.trim()) ?? "";
300
+ return ((f = (b = l.ownerDocument.getElementById(g)) == null ? void 0 : b.textContent) == null ? void 0 : f.trim()) ?? "";
301
301
  }).filter(Boolean);
302
- if (m.length) {
303
- a += m.join(" ");
302
+ if (d.length) {
303
+ a += d.join(" ");
304
304
  continue;
305
305
  }
306
306
  }
307
- a += ((i = u.getAttribute("aria-label")) == null ? void 0 : i.trim()) ?? u.getAttribute("alt") ?? ((n = u.getAttribute("title")) == null ? void 0 : n.trim()) ?? "";
307
+ a += ((i = l.getAttribute("aria-label")) == null ? void 0 : i.trim()) ?? l.getAttribute("alt") ?? ((n = l.getAttribute("title")) == null ? void 0 : n.trim()) ?? "";
308
308
  } else if (h === "svg") {
309
- const l = (r = u.getAttribute("aria-label")) == null ? void 0 : r.trim();
310
- if (l)
311
- a += l;
309
+ const c = (r = l.getAttribute("aria-label")) == null ? void 0 : r.trim();
310
+ if (c)
311
+ a += c;
312
312
  else {
313
- const m = u.querySelector("title");
314
- m && (a += m.textContent ?? "");
313
+ const d = l.querySelector("title");
314
+ d && (a += d.textContent ?? "");
315
315
  }
316
- } else (o = u.getAttribute("aria-label")) != null && o.trim() ? a += u.getAttribute("aria-label").trim() : a += y(u);
316
+ } else (o = l.getAttribute("aria-label")) != null && o.trim() ? a += l.getAttribute("aria-label").trim() : a += w(l);
317
317
  }
318
318
  }
319
319
  return a;
320
320
  }
321
- let _ = /* @__PURE__ */ new WeakMap();
322
- function ke() {
323
- _ = /* @__PURE__ */ new WeakMap();
321
+ let W = /* @__PURE__ */ new WeakMap();
322
+ function Ie() {
323
+ W = /* @__PURE__ */ new WeakMap();
324
324
  }
325
- function Ie(t) {
325
+ function Te(t) {
326
326
  return t.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
327
327
  }
328
328
  const Ee = [
@@ -335,12 +335,12 @@ const Ee = [
335
335
  "for",
336
336
  "aria-label"
337
337
  ];
338
- function Te(t) {
338
+ function Ce(t) {
339
339
  const a = t.tagName.toLowerCase();
340
340
  for (const i of Ee) {
341
341
  const n = t.getAttribute(i);
342
342
  if (n != null && n.length > 0 && n.length < 100)
343
- return `${a}[${i}="${Ie(n)}"]`;
343
+ return `${a}[${i}="${Te(n)}"]`;
344
344
  }
345
345
  const e = t.parentElement;
346
346
  if (e) {
@@ -352,7 +352,7 @@ function Te(t) {
352
352
  }
353
353
  return a;
354
354
  }
355
- function $(t) {
355
+ function M(t) {
356
356
  if (t.id) return `#${CSS.escape(t.id)}`;
357
357
  const a = t.getRootNode(), e = a instanceof ShadowRoot ? null : a.documentElement, i = [];
358
358
  let n = t;
@@ -361,7 +361,7 @@ function $(t) {
361
361
  i.unshift(`#${CSS.escape(n.id)}`);
362
362
  break;
363
363
  }
364
- if (i.unshift(Te(n)), i.length >= 2) {
364
+ if (i.unshift(Ce(n)), i.length >= 2) {
365
365
  const r = i.join(" > ");
366
366
  try {
367
367
  const o = a.querySelectorAll(r);
@@ -373,28 +373,28 @@ function $(t) {
373
373
  }
374
374
  return i.join(" > ");
375
375
  }
376
- function d(t) {
376
+ function m(t) {
377
377
  var r;
378
- const a = _.get(t);
378
+ const a = W.get(t);
379
379
  if (a !== void 0) return a;
380
380
  const e = [];
381
381
  let i = t;
382
382
  for (; i; ) {
383
383
  const o = i.getRootNode();
384
384
  if (o instanceof ShadowRoot)
385
- e.unshift({ selector: $(i), delimiter: " >>> " }), i = o.host;
385
+ e.unshift({ selector: M(i), delimiter: " >>> " }), i = o.host;
386
386
  else {
387
387
  const s = (r = o.defaultView) == null ? void 0 : r.frameElement;
388
388
  if (s)
389
- e.unshift({ selector: $(i), delimiter: " >>>iframe> " }), i = s;
389
+ e.unshift({ selector: M(i), delimiter: " >>>iframe> " }), i = s;
390
390
  else {
391
- e.unshift({ selector: $(i), delimiter: "" });
391
+ e.unshift({ selector: M(i), delimiter: "" });
392
392
  break;
393
393
  }
394
394
  }
395
395
  }
396
396
  const n = e.map((o, s) => (s === 0 ? "" : o.delimiter) + o.selector).join("");
397
- return _.set(t, n), n;
397
+ return W.set(t, n), n;
398
398
  }
399
399
  function wi(t) {
400
400
  const a = [], e = [];
@@ -429,7 +429,7 @@ function wi(t) {
429
429
  }
430
430
  return null;
431
431
  }
432
- function c(t) {
432
+ function u(t) {
433
433
  const a = t.outerHTML;
434
434
  return a.length > 200 ? a.slice(0, 200) + "..." : a;
435
435
  }
@@ -498,7 +498,7 @@ const Le = /* @__PURE__ */ new Set([
498
498
  "aria-multiselectable",
499
499
  "aria-readonly",
500
500
  "aria-required"
501
- ]), X = /* @__PURE__ */ new Set(["aria-checked", "aria-pressed"]), Ce = /* @__PURE__ */ new Set([
501
+ ]), X = /* @__PURE__ */ new Set(["aria-checked", "aria-pressed"]), qe = /* @__PURE__ */ new Set([
502
502
  "aria-colcount",
503
503
  "aria-colindex",
504
504
  "aria-colspan",
@@ -508,7 +508,7 @@ const Le = /* @__PURE__ */ new Set([
508
508
  "aria-rowindex",
509
509
  "aria-rowspan",
510
510
  "aria-setsize"
511
- ]), qe = /* @__PURE__ */ new Set([
511
+ ]), Re = /* @__PURE__ */ new Set([
512
512
  "aria-valuemax",
513
513
  "aria-valuemin",
514
514
  "aria-valuenow"
@@ -540,7 +540,7 @@ const Le = /* @__PURE__ */ new Set([
540
540
  "suggestion",
541
541
  "term",
542
542
  "time"
543
- ]), Re = {
543
+ ]), Ne = {
544
544
  abbr: !0,
545
545
  bdi: !0,
546
546
  bdo: !0,
@@ -568,7 +568,7 @@ const Le = /* @__PURE__ */ new Set([
568
568
  u: !0,
569
569
  var: !0,
570
570
  wbr: !0
571
- }, Ne = {
571
+ }, $e = {
572
572
  alert: /* @__PURE__ */ new Set(["aria-disabled", "aria-errormessage", "aria-haspopup", "aria-invalid"]),
573
573
  article: /* @__PURE__ */ new Set(["aria-disabled", "aria-errormessage", "aria-haspopup", "aria-invalid"]),
574
574
  banner: /* @__PURE__ */ new Set(["aria-disabled", "aria-errormessage", "aria-haspopup", "aria-invalid"]),
@@ -601,95 +601,95 @@ const Le = /* @__PURE__ */ new Set([
601
601
  time: /* @__PURE__ */ new Set(["aria-disabled", "aria-errormessage", "aria-haspopup", "aria-invalid"]),
602
602
  tooltip: /* @__PURE__ */ new Set(["aria-disabled", "aria-errormessage", "aria-haspopup", "aria-invalid"])
603
603
  };
604
- let T = null, L = null;
604
+ let E = null, C = null;
605
605
  function Me() {
606
- T = null, L = null;
606
+ E = null, C = null;
607
607
  }
608
608
  function z(t) {
609
609
  var n;
610
- if (L && (T == null ? void 0 : T.deref()) === t) return L;
610
+ if (C && (E == null ? void 0 : E.deref()) === t) return C;
611
611
  const a = [], e = [], i = [];
612
612
  for (const r of t.querySelectorAll("*")) {
613
613
  let o = !1;
614
- for (const l of r.attributes)
615
- if (l.name.startsWith("aria-")) {
614
+ for (const c of r.attributes)
615
+ if (c.name.startsWith("aria-")) {
616
616
  o = !0;
617
617
  break;
618
618
  }
619
619
  if (!o) continue;
620
- let s, u;
621
- const h = () => (s === void 0 && (s = d(r), u = c(r)), { selector: s, html: u });
622
- for (const l of r.attributes)
623
- if (l.name.startsWith("aria-") && !Le.has(l.name)) {
624
- const m = h();
620
+ let s, l;
621
+ const h = () => (s === void 0 && (s = m(r), l = u(r)), { selector: s, html: l });
622
+ for (const c of r.attributes)
623
+ if (c.name.startsWith("aria-") && !Le.has(c.name)) {
624
+ const d = h();
625
625
  a.push({
626
626
  ruleId: "aria-valid-attr",
627
- selector: m.selector,
628
- html: m.html,
627
+ selector: d.selector,
628
+ html: d.html,
629
629
  impact: "critical",
630
- message: `Invalid ARIA attribute "${l.name}".`
630
+ message: `Invalid ARIA attribute "${c.name}".`
631
631
  });
632
632
  break;
633
633
  }
634
- for (const l of r.attributes) {
635
- if (!l.name.startsWith("aria-")) continue;
636
- const m = l.value.trim();
637
- if (!(m === "" && !G.has(l.name) && !X.has(l.name))) {
638
- if (G.has(l.name)) {
639
- if (m !== "true" && m !== "false") {
634
+ for (const c of r.attributes) {
635
+ if (!c.name.startsWith("aria-")) continue;
636
+ const d = c.value.trim();
637
+ if (!(d === "" && !G.has(c.name) && !X.has(c.name))) {
638
+ if (G.has(c.name)) {
639
+ if (d !== "true" && d !== "false") {
640
640
  const g = h();
641
641
  e.push({
642
642
  ruleId: "aria-valid-attr-value",
643
643
  selector: g.selector,
644
644
  html: g.html,
645
645
  impact: "critical",
646
- message: `${l.name} must be "true" or "false", got "${m}".`
646
+ message: `${c.name} must be "true" or "false", got "${d}".`
647
647
  });
648
648
  }
649
- } else if (X.has(l.name)) {
650
- if (m !== "true" && m !== "false" && m !== "mixed") {
649
+ } else if (X.has(c.name)) {
650
+ if (d !== "true" && d !== "false" && d !== "mixed") {
651
651
  const g = h();
652
652
  e.push({
653
653
  ruleId: "aria-valid-attr-value",
654
654
  selector: g.selector,
655
655
  html: g.html,
656
656
  impact: "critical",
657
- message: `${l.name} must be "true", "false", or "mixed", got "${m}".`
657
+ message: `${c.name} must be "true", "false", or "mixed", got "${d}".`
658
658
  });
659
659
  }
660
- } else if (Ce.has(l.name)) {
661
- if (m === "" || !/^-?\d+$/.test(m)) {
660
+ } else if (qe.has(c.name)) {
661
+ if (d === "" || !/^-?\d+$/.test(d)) {
662
662
  const g = h();
663
663
  e.push({
664
664
  ruleId: "aria-valid-attr-value",
665
665
  selector: g.selector,
666
666
  html: g.html,
667
667
  impact: "critical",
668
- message: `${l.name} must be an integer, got "${m}".`
668
+ message: `${c.name} must be an integer, got "${d}".`
669
669
  });
670
670
  }
671
- } else if (qe.has(l.name)) {
672
- if (m === "" || isNaN(Number(m))) {
671
+ } else if (Re.has(c.name)) {
672
+ if (d === "" || isNaN(Number(d))) {
673
673
  const g = h();
674
674
  e.push({
675
675
  ruleId: "aria-valid-attr-value",
676
676
  selector: g.selector,
677
677
  html: g.html,
678
678
  impact: "critical",
679
- message: `${l.name} must be a number, got "${m}".`
679
+ message: `${c.name} must be a number, got "${d}".`
680
680
  });
681
681
  }
682
- } else if (Y[l.name]) {
683
- const g = m.split(/\s+/);
682
+ } else if (Y[c.name]) {
683
+ const g = d.split(/\s+/);
684
684
  for (const b of g)
685
- if (!Y[l.name].has(b)) {
685
+ if (!Y[c.name].has(b)) {
686
686
  const f = h();
687
687
  e.push({
688
688
  ruleId: "aria-valid-attr-value",
689
689
  selector: f.selector,
690
690
  html: f.html,
691
691
  impact: "critical",
692
- message: `Invalid value "${m}" for ${l.name}.`
692
+ message: `Invalid value "${d}" for ${c.name}.`
693
693
  });
694
694
  break;
695
695
  }
@@ -697,8 +697,8 @@ function z(t) {
697
697
  }
698
698
  }
699
699
  if (!p(r)) {
700
- const l = (n = r.getAttribute("role")) == null ? void 0 : n.trim().toLowerCase(), m = r.tagName.toLowerCase();
701
- if (!l && Re[m]) {
700
+ const c = (n = r.getAttribute("role")) == null ? void 0 : n.trim().toLowerCase(), d = r.tagName.toLowerCase();
701
+ if (!c && Ne[d]) {
702
702
  const g = r.hasAttribute("aria-label"), b = r.hasAttribute("aria-labelledby");
703
703
  if (g || b) {
704
704
  const f = h();
@@ -707,28 +707,28 @@ function z(t) {
707
707
  selector: f.selector,
708
708
  html: f.html,
709
709
  impact: "serious",
710
- message: `aria-label and aria-labelledby are prohibited on <${m}> elements.`
710
+ message: `aria-label and aria-labelledby are prohibited on <${d}> elements.`
711
711
  });
712
712
  }
713
- } else if (l) {
714
- if (K.has(l)) {
713
+ } else if (c) {
714
+ if (K.has(c)) {
715
715
  const b = r.hasAttribute("aria-label"), f = r.hasAttribute("aria-labelledby");
716
716
  if (b || f) {
717
- const w = h();
717
+ const y = h();
718
718
  i.push({
719
719
  ruleId: "aria-prohibited-attr",
720
- selector: w.selector,
721
- html: w.html,
720
+ selector: y.selector,
721
+ html: y.html,
722
722
  impact: "serious",
723
- message: `aria-label and aria-labelledby are prohibited on role "${l}".`
723
+ message: `aria-label and aria-labelledby are prohibited on role "${c}".`
724
724
  });
725
725
  }
726
726
  }
727
- const g = Ne[l];
727
+ const g = $e[c];
728
728
  if (g) {
729
729
  for (const b of r.attributes)
730
730
  if (b.name.startsWith("aria-") && g.has(b.name)) {
731
- if ((b.name === "aria-label" || b.name === "aria-labelledby") && K.has(l))
731
+ if ((b.name === "aria-label" || b.name === "aria-labelledby") && K.has(c))
732
732
  continue;
733
733
  const f = h();
734
734
  i.push({
@@ -736,22 +736,22 @@ function z(t) {
736
736
  selector: f.selector,
737
737
  html: f.html,
738
738
  impact: "serious",
739
- message: `Attribute "${b.name}" is prohibited on role "${l}".`
739
+ message: `Attribute "${b.name}" is prohibited on role "${c}".`
740
740
  });
741
741
  }
742
742
  }
743
743
  }
744
744
  }
745
745
  }
746
- return T = new WeakRef(t), L = { validAttr: a, validAttrValue: e, prohibitedAttr: i }, L;
746
+ return E = new WeakRef(t), C = { validAttr: a, validAttrValue: e, prohibitedAttr: i }, C;
747
747
  }
748
- let F = /* @__PURE__ */ new WeakMap(), j = /* @__PURE__ */ new WeakMap(), P = /* @__PURE__ */ new WeakMap();
749
- function $e() {
750
- F = /* @__PURE__ */ new WeakMap(), j = /* @__PURE__ */ new WeakMap(), P = /* @__PURE__ */ new WeakMap();
748
+ let _ = /* @__PURE__ */ new WeakMap(), P = /* @__PURE__ */ new WeakMap(), j = /* @__PURE__ */ new WeakMap();
749
+ function He() {
750
+ _ = /* @__PURE__ */ new WeakMap(), P = /* @__PURE__ */ new WeakMap(), j = /* @__PURE__ */ new WeakMap();
751
751
  }
752
752
  function A(t) {
753
- let a = F.get(t);
754
- return a || (a = getComputedStyle(t), F.set(t, a), a);
753
+ let a = _.get(t);
754
+ return a || (a = getComputedStyle(t), _.set(t, a), a);
755
755
  }
756
756
  function q(t, a, e) {
757
757
  const [i, n, r] = [t, a, e].map((o) => {
@@ -764,7 +764,7 @@ function de(t, a) {
764
764
  const e = Math.max(t, a), i = Math.min(t, a);
765
765
  return (e + 0.05) / (i + 0.05);
766
766
  }
767
- const Q = {
767
+ const J = {
768
768
  black: [0, 0, 0],
769
769
  white: [255, 255, 255],
770
770
  red: [255, 0, 0],
@@ -786,7 +786,7 @@ const Q = {
786
786
  };
787
787
  function R(t) {
788
788
  const a = t.trim().toLowerCase();
789
- if (Q[a]) return Q[a];
789
+ if (J[a]) return J[a];
790
790
  const e = a.match(/^#([0-9a-f])([0-9a-f])([0-9a-f])$/);
791
791
  if (e)
792
792
  return [parseInt(e[1] + e[1], 16), parseInt(e[2] + e[2], 16), parseInt(e[3] + e[3], 16)];
@@ -803,13 +803,13 @@ function R(t) {
803
803
  );
804
804
  return r ? [parseInt(r[1]), parseInt(r[2]), parseInt(r[3])] : null;
805
805
  }
806
- function He(t) {
807
- const a = j.get(t);
806
+ function De(t) {
807
+ const a = P.get(t);
808
808
  if (a !== void 0) return a;
809
- const e = De(t);
810
- return j.set(t, e), e;
809
+ const e = Oe(t);
810
+ return P.set(t, e), e;
811
811
  }
812
- function De(t) {
812
+ function Oe(t) {
813
813
  let a = t;
814
814
  for (; a; ) {
815
815
  const e = A(a), i = e.backgroundImage;
@@ -828,20 +828,20 @@ function De(t) {
828
828
  }
829
829
  return [255, 255, 255];
830
830
  }
831
- const Oe = /* @__PURE__ */ new Set(["IMG", "PICTURE", "VIDEO", "SVG"]);
832
- function We(t) {
833
- const a = P.get(t);
831
+ const Be = /* @__PURE__ */ new Set(["IMG", "PICTURE", "VIDEO", "SVG"]);
832
+ function Fe(t) {
833
+ const a = j.get(t);
834
834
  if (a !== void 0) return a;
835
- const e = Be(t);
836
- return P.set(t, e), e;
835
+ const e = We(t);
836
+ return j.set(t, e), e;
837
837
  }
838
- function Be(t) {
838
+ function We(t) {
839
839
  let a = t, e = !1;
840
840
  for (; a; ) {
841
841
  const i = A(a).position;
842
842
  if ((i === "absolute" || i === "fixed") && (e = !0), a !== t && i !== "static") {
843
843
  for (const n of a.children)
844
- if (!(n === t || n.contains(t)) && Oe.has(n.tagName)) {
844
+ if (!(n === t || n.contains(t)) && Be.has(n.tagName)) {
845
845
  if (e) return !0;
846
846
  const r = A(n).position;
847
847
  if (r === "absolute" || r === "fixed") return !0;
@@ -856,7 +856,7 @@ function _e(t) {
856
856
  const a = parseFloat(t);
857
857
  return t.endsWith("pt") ? a * (4 / 3) : a;
858
858
  }
859
- function Fe(t) {
859
+ function Pe(t) {
860
860
  const a = A(t), e = _e(a.fontSize), i = parseInt(a.fontWeight) || (a.fontWeight === "bold" ? 700 : 400);
861
861
  return e >= 23.5 || e >= 18.5 && i >= 700;
862
862
  }
@@ -874,13 +874,13 @@ function H(t) {
874
874
  }
875
875
  const n = t.parentElement;
876
876
  if (n && n !== e) {
877
- const s = t instanceof HTMLImageElement && t.alt || "", u = (o = n.textContent) == null ? void 0 : o.replace(s, "").trim().slice(0, 100);
878
- u && a.push(`Adjacent text: ${u}`);
877
+ const s = t instanceof HTMLImageElement && t.alt || "", l = (o = n.textContent) == null ? void 0 : o.replace(s, "").trim().slice(0, 100);
878
+ l && a.push(`Adjacent text: ${l}`);
879
879
  }
880
880
  return a.length > 0 ? a.join(`
881
881
  `) : void 0;
882
882
  }
883
- function J(t) {
883
+ function Q(t) {
884
884
  let a = t;
885
885
  for (; a; ) {
886
886
  if (a instanceof HTMLElement && a.style.visibility === "hidden") return !0;
@@ -898,7 +898,7 @@ const je = {
898
898
  run(t) {
899
899
  const a = [];
900
900
  for (const e of t.querySelectorAll("img")) {
901
- if (p(e) || J(e)) continue;
901
+ if (p(e) || Q(e)) continue;
902
902
  const i = e.getAttribute("role");
903
903
  if (i === "presentation" || i === "none") {
904
904
  const r = e.getAttribute("tabindex");
@@ -908,8 +908,8 @@ const je = {
908
908
  if (n !== null && n.trim() === "" && n !== "") {
909
909
  a.push({
910
910
  ruleId: "img-alt",
911
- selector: d(e),
912
- html: c(e),
911
+ selector: m(e),
912
+ html: u(e),
913
913
  impact: "critical",
914
914
  message: 'Image has whitespace-only alt text. Use alt="" for decorative images or provide descriptive text.',
915
915
  context: H(e)
@@ -918,18 +918,18 @@ const je = {
918
918
  }
919
919
  !e.hasAttribute("alt") && !v(e) && a.push({
920
920
  ruleId: "img-alt",
921
- selector: d(e),
922
- html: c(e),
921
+ selector: m(e),
922
+ html: u(e),
923
923
  impact: "critical",
924
924
  message: "Image element missing alt attribute.",
925
925
  context: H(e)
926
926
  });
927
927
  }
928
928
  for (const e of t.querySelectorAll('[role="img"]:not(img):not(svg)'))
929
- p(e) || J(e) || v(e) || a.push({
929
+ p(e) || Q(e) || v(e) || a.push({
930
930
  ruleId: "img-alt",
931
- selector: d(e),
932
- html: c(e),
931
+ selector: m(e),
932
+ html: u(e),
933
933
  impact: "critical",
934
934
  message: 'Element with role="img" has no accessible name. Add aria-label or aria-labelledby.',
935
935
  context: H(e)
@@ -937,15 +937,15 @@ const je = {
937
937
  return a;
938
938
  }
939
939
  };
940
- function Pe(t) {
940
+ function Ve(t) {
941
941
  var r, o, s;
942
942
  const a = t.getAttribute("aria-labelledby");
943
943
  if (a) {
944
- const u = a.split(/\s+/).map((h) => {
945
- var l, m;
946
- return ((m = (l = t.ownerDocument.getElementById(h)) == null ? void 0 : l.textContent) == null ? void 0 : m.trim()) ?? "";
944
+ const l = a.split(/\s+/).map((h) => {
945
+ var c, d;
946
+ return ((d = (c = t.ownerDocument.getElementById(h)) == null ? void 0 : c.textContent) == null ? void 0 : d.trim()) ?? "";
947
947
  }).filter(Boolean);
948
- if (u.length) return u.join(" ");
948
+ if (l.length) return l.join(" ");
949
949
  }
950
950
  const e = (r = t.getAttribute("aria-label")) == null ? void 0 : r.trim();
951
951
  if (e) return e;
@@ -954,7 +954,7 @@ function Pe(t) {
954
954
  const n = (s = t.getAttribute("title")) == null ? void 0 : s.trim();
955
955
  return n || "";
956
956
  }
957
- const Ve = {
957
+ const ze = {
958
958
  id: "svg-img-alt",
959
959
  wcag: ["1.1.1"],
960
960
  level: "A",
@@ -965,12 +965,12 @@ const Ve = {
965
965
  const a = [], e = 'svg[role="img"], [role="graphics-document"], [role="graphics-symbol"]';
966
966
  for (const i of t.querySelectorAll(e)) {
967
967
  if (p(i)) continue;
968
- if (!Pe(i)) {
968
+ if (!Ve(i)) {
969
969
  const r = i.getAttribute("role");
970
970
  a.push({
971
971
  ruleId: "svg-img-alt",
972
- selector: d(i),
973
- html: c(i),
972
+ selector: m(i),
973
+ html: u(i),
974
974
  impact: "serious",
975
975
  message: `${i.tagName.toLowerCase()} with role='${r}' has no accessible name.`
976
976
  });
@@ -978,7 +978,7 @@ const Ve = {
978
978
  }
979
979
  return a;
980
980
  }
981
- }, ze = {
981
+ }, Ue = {
982
982
  id: "input-image-alt",
983
983
  wcag: ["1.1.1", "4.1.2"],
984
984
  level: "A",
@@ -990,21 +990,21 @@ const Ve = {
990
990
  for (const e of t.querySelectorAll('input[type="image"]'))
991
991
  p(e) || v(e) || a.push({
992
992
  ruleId: "input-image-alt",
993
- selector: d(e),
994
- html: c(e),
993
+ selector: m(e),
994
+ html: u(e),
995
995
  impact: "critical",
996
996
  message: "Image input missing alt text."
997
997
  });
998
998
  return a;
999
999
  }
1000
- }, Ue = {
1000
+ }, Ge = {
1001
1001
  id: "image-redundant-alt",
1002
1002
  wcag: [],
1003
1003
  level: "A",
1004
1004
  tags: ["best-practice"],
1005
1005
  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.",
1006
1006
  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.",
1007
- prompt: "Show the duplicated text and suggest either an empty alt or a complementary description.",
1007
+ 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.",
1008
1008
  run(t) {
1009
1009
  var e;
1010
1010
  const a = [];
@@ -1014,40 +1014,47 @@ const Ve = {
1014
1014
  const r = i.closest("a, button");
1015
1015
  if (r) {
1016
1016
  const o = ((e = r.textContent) == null ? void 0 : e.trim().toLowerCase()) || "";
1017
- o && o === n && a.push({
1018
- ruleId: "image-redundant-alt",
1019
- selector: d(i),
1020
- html: c(i),
1021
- impact: "minor",
1022
- message: `Alt text "${i.getAttribute("alt")}" duplicates surrounding ${r.tagName.toLowerCase()} text.`
1023
- });
1017
+ if (o && o === n) {
1018
+ const s = r.tagName.toLowerCase(), l = r.getAttribute("href");
1019
+ a.push({
1020
+ ruleId: "image-redundant-alt",
1021
+ selector: m(i),
1022
+ html: u(i),
1023
+ impact: "minor",
1024
+ message: `Alt text "${i.getAttribute("alt")}" duplicates surrounding ${s} text.`,
1025
+ context: `Duplicated text: "${i.getAttribute("alt")}", parent element: <${s}>${l ? ` href="${l}"` : ""}`
1026
+ });
1027
+ }
1024
1028
  }
1025
1029
  }
1026
1030
  return a;
1027
1031
  }
1028
- }, Ge = ["image", "picture", "photo", "graphic", "icon", "img"], Xe = {
1032
+ }, Xe = ["image", "picture", "photo", "graphic", "icon", "img"], Ye = {
1029
1033
  id: "image-alt-redundant-words",
1030
1034
  wcag: [],
1031
1035
  level: "A",
1032
1036
  tags: ["best-practice"],
1033
1037
  description: "Image alt text should not contain words like 'image', 'photo', or 'picture' — screen readers already announce the element type.",
1034
1038
  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'.",
1035
- prompt: "Identify the redundant word(s) in the alt text and show the corrected version with those words removed.",
1039
+ 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'.",
1036
1040
  run(t) {
1037
1041
  const a = [];
1038
1042
  for (const e of t.querySelectorAll("img[alt]")) {
1039
1043
  const i = e.getAttribute("alt").toLowerCase();
1040
- i && Ge.some((n) => i.split(/\s+/).includes(n)) && a.push({
1044
+ if (!i) continue;
1045
+ const n = Xe.filter((r) => i.split(/\s+/).includes(r));
1046
+ n.length > 0 && a.push({
1041
1047
  ruleId: "image-alt-redundant-words",
1042
- selector: d(e),
1043
- html: c(e),
1048
+ selector: m(e),
1049
+ html: u(e),
1044
1050
  impact: "minor",
1045
- message: `Alt text "${e.getAttribute("alt")}" contains redundant word(s).`
1051
+ message: `Alt text "${e.getAttribute("alt")}" contains redundant word(s): ${n.join(", ")}.`,
1052
+ context: `Current alt: "${e.getAttribute("alt")}", redundant word(s): ${n.join(", ")}`
1046
1053
  });
1047
1054
  }
1048
1055
  return a;
1049
1056
  }
1050
- }, Ye = {
1057
+ }, Ke = {
1051
1058
  id: "area-alt",
1052
1059
  wcag: ["1.1.1", "4.1.2"],
1053
1060
  level: "A",
@@ -1060,8 +1067,8 @@ const Ve = {
1060
1067
  if (p(e)) continue;
1061
1068
  v(e) || a.push({
1062
1069
  ruleId: "area-alt",
1063
- selector: d(e),
1064
- html: c(e),
1070
+ selector: m(e),
1071
+ html: u(e),
1065
1072
  impact: "critical",
1066
1073
  message: "Image map <area> element is missing alternative text."
1067
1074
  });
@@ -1069,13 +1076,13 @@ const Ve = {
1069
1076
  return a;
1070
1077
  }
1071
1078
  };
1072
- function Ke(t) {
1079
+ function Je(t) {
1073
1080
  var n, r;
1074
1081
  const a = t.getAttribute("aria-labelledby");
1075
1082
  if (a) {
1076
1083
  const o = a.split(/\s+/).map((s) => {
1077
- var u, h;
1078
- return ((h = (u = t.ownerDocument.getElementById(s)) == null ? void 0 : u.textContent) == null ? void 0 : h.trim()) ?? "";
1084
+ var l, h;
1085
+ return ((h = (l = t.ownerDocument.getElementById(s)) == null ? void 0 : l.textContent) == null ? void 0 : h.trim()) ?? "";
1079
1086
  }).filter(Boolean);
1080
1087
  if (o.length) return o.join(" ");
1081
1088
  }
@@ -1104,7 +1111,7 @@ const Qe = {
1104
1111
  }
1105
1112
  n = n.parentElement;
1106
1113
  }
1107
- if (r || i.getAttribute("role") === "presentation" || i.getAttribute("role") === "none" || Ke(i)) continue;
1114
+ if (r || i.getAttribute("role") === "presentation" || i.getAttribute("role") === "none" || Je(i)) continue;
1108
1115
  const o = i.getAttribute("data") || "";
1109
1116
  if (!((i.getAttribute("type") || "").startsWith("image/") || /\.(png|jpg|jpeg|gif|svg|webp|bmp|ico)$/i.test(o))) {
1110
1117
  const h = i.querySelector("img[alt]");
@@ -1112,15 +1119,15 @@ const Qe = {
1112
1119
  }
1113
1120
  a.push({
1114
1121
  ruleId: "object-alt",
1115
- selector: d(i),
1116
- html: c(i),
1122
+ selector: m(i),
1123
+ html: u(i),
1117
1124
  impact: "serious",
1118
1125
  message: "<object> element is missing alternative text. Add aria-label, aria-labelledby, or a title attribute."
1119
1126
  });
1120
1127
  }
1121
1128
  return a;
1122
1129
  }
1123
- }, Je = {
1130
+ }, Ze = {
1124
1131
  id: "role-img-alt",
1125
1132
  wcag: ["1.1.1"],
1126
1133
  level: "A",
@@ -1133,8 +1140,8 @@ const Qe = {
1133
1140
  if (p(e) || e.tagName.toLowerCase() === "svg" || e.tagName.toLowerCase() === "img") continue;
1134
1141
  v(e) || a.push({
1135
1142
  ruleId: "role-img-alt",
1136
- selector: d(e),
1137
- html: c(e),
1143
+ selector: m(e),
1144
+ html: u(e),
1138
1145
  impact: "serious",
1139
1146
  message: "Element with role='img' has no accessible name. Add aria-label or aria-labelledby."
1140
1147
  });
@@ -1142,7 +1149,7 @@ const Qe = {
1142
1149
  return a;
1143
1150
  }
1144
1151
  };
1145
- function yi(t) {
1152
+ function Ai(t) {
1146
1153
  if (typeof t != "object" || t === null)
1147
1154
  return "Rule spec must be an object";
1148
1155
  const a = t;
@@ -1172,10 +1179,10 @@ function yi(t) {
1172
1179
  return "Rule must have a wcag array";
1173
1180
  if (typeof a.level != "string" || !["A", "AA"].includes(a.level))
1174
1181
  return "Rule must have level A or AA";
1175
- const n = Ze(e);
1182
+ const n = et(e);
1176
1183
  return n || null;
1177
1184
  }
1178
- function Ze(t) {
1185
+ function et(t) {
1179
1186
  switch (t.type) {
1180
1187
  case "selector-exists":
1181
1188
  return null;
@@ -1193,7 +1200,7 @@ function Ze(t) {
1193
1200
  return `Unknown check type: ${String(t.type)}`;
1194
1201
  }
1195
1202
  }
1196
- function x(t, a, e) {
1203
+ function S(t, a, e) {
1197
1204
  let i = t;
1198
1205
  if (i.includes("{{tag}}") && (i = i.replace(/\{\{tag\}\}/g, a.tagName.toLowerCase())), i.includes("{{value}}")) {
1199
1206
  let n = "";
@@ -1218,10 +1225,10 @@ function k(t) {
1218
1225
  for (const n of e.querySelectorAll(t.selector))
1219
1226
  a && p(n) || i.push({
1220
1227
  ruleId: t.id,
1221
- selector: d(n),
1222
- html: c(n),
1228
+ selector: m(n),
1229
+ html: u(n),
1223
1230
  impact: t.impact,
1224
- message: x(t.message, n, t.check),
1231
+ message: S(t.message, n, t.check),
1225
1232
  element: n
1226
1233
  });
1227
1234
  break;
@@ -1230,13 +1237,13 @@ function k(t) {
1230
1237
  const { attribute: n, operator: r, value: o } = t.check;
1231
1238
  for (const s of e.querySelectorAll(t.selector)) {
1232
1239
  if (a && p(s)) continue;
1233
- const u = s.getAttribute(n);
1234
- u !== null && et(u, r, o) && i.push({
1240
+ const l = s.getAttribute(n);
1241
+ l !== null && tt(l, r, o) && i.push({
1235
1242
  ruleId: t.id,
1236
- selector: d(s),
1237
- html: c(s),
1243
+ selector: m(s),
1244
+ html: u(s),
1238
1245
  impact: t.impact,
1239
- message: x(t.message, s, t.check),
1246
+ message: S(t.message, s, t.check),
1240
1247
  element: s
1241
1248
  });
1242
1249
  }
@@ -1247,40 +1254,40 @@ function k(t) {
1247
1254
  for (const r of e.querySelectorAll(t.selector))
1248
1255
  a && p(r) || r.hasAttribute(n) || i.push({
1249
1256
  ruleId: t.id,
1250
- selector: d(r),
1251
- html: c(r),
1257
+ selector: m(r),
1258
+ html: u(r),
1252
1259
  impact: t.impact,
1253
- message: x(t.message, r, t.check),
1260
+ message: S(t.message, r, t.check),
1254
1261
  element: r
1255
1262
  });
1256
1263
  break;
1257
1264
  }
1258
1265
  case "attribute-regex": {
1259
1266
  const { attribute: n, pattern: r, flags: o, shouldMatch: s } = t.check;
1260
- let u;
1267
+ let l;
1261
1268
  try {
1262
- u = new RegExp(r, o);
1269
+ l = new RegExp(r, o);
1263
1270
  } catch {
1264
1271
  break;
1265
1272
  }
1266
1273
  for (const h of e.querySelectorAll(t.selector)) {
1267
1274
  if (a && p(h)) continue;
1268
- const l = h.getAttribute(n);
1269
- if (l === null) continue;
1270
- const m = u.test(l);
1271
- s && !m ? i.push({
1275
+ const c = h.getAttribute(n);
1276
+ if (c === null) continue;
1277
+ const d = l.test(c);
1278
+ s && !d ? i.push({
1272
1279
  ruleId: t.id,
1273
- selector: d(h),
1274
- html: c(h),
1280
+ selector: m(h),
1281
+ html: u(h),
1275
1282
  impact: t.impact,
1276
- message: x(t.message, h, t.check),
1283
+ message: S(t.message, h, t.check),
1277
1284
  element: h
1278
- }) : !s && m && i.push({
1285
+ }) : !s && d && i.push({
1279
1286
  ruleId: t.id,
1280
- selector: d(h),
1281
- html: c(h),
1287
+ selector: m(h),
1288
+ html: u(h),
1282
1289
  impact: t.impact,
1283
- message: x(t.message, h, t.check),
1290
+ message: S(t.message, h, t.check),
1284
1291
  element: h
1285
1292
  });
1286
1293
  }
@@ -1291,10 +1298,10 @@ function k(t) {
1291
1298
  for (const r of e.querySelectorAll(t.selector))
1292
1299
  a && p(r) || r.querySelector(n) || i.push({
1293
1300
  ruleId: t.id,
1294
- selector: d(r),
1295
- html: c(r),
1301
+ selector: m(r),
1302
+ html: u(r),
1296
1303
  impact: t.impact,
1297
- message: x(t.message, r, t.check),
1304
+ message: S(t.message, r, t.check),
1298
1305
  element: r
1299
1306
  });
1300
1307
  break;
@@ -1309,10 +1316,10 @@ function k(t) {
1309
1316
  if (!n.has(o.tagName.toLowerCase())) {
1310
1317
  i.push({
1311
1318
  ruleId: t.id,
1312
- selector: d(o),
1313
- html: c(o),
1319
+ selector: m(o),
1320
+ html: u(o),
1314
1321
  impact: t.impact,
1315
- message: x(t.message, o, t.check),
1322
+ message: S(t.message, o, t.check),
1316
1323
  element: o
1317
1324
  });
1318
1325
  break;
@@ -1325,7 +1332,7 @@ function k(t) {
1325
1332
  }
1326
1333
  };
1327
1334
  }
1328
- function et(t, a, e) {
1335
+ function tt(t, a, e) {
1329
1336
  switch (a) {
1330
1337
  case ">":
1331
1338
  return parseFloat(t) > e;
@@ -1343,7 +1350,7 @@ function et(t, a, e) {
1343
1350
  return !1;
1344
1351
  }
1345
1352
  }
1346
- const tt = {
1353
+ const at = {
1347
1354
  id: "server-side-image-map",
1348
1355
  selector: "img[ismap], input[type='image'][ismap]",
1349
1356
  check: { type: "selector-exists" },
@@ -1354,7 +1361,7 @@ const tt = {
1354
1361
  level: "A",
1355
1362
  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.",
1356
1363
  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."
1357
- }, at = k(tt), it = [
1364
+ }, it = k(at), nt = [
1358
1365
  '[role="checkbox"]',
1359
1366
  '[role="combobox"]',
1360
1367
  '[role="listbox"]',
@@ -1366,13 +1373,13 @@ const tt = {
1366
1373
  '[role="spinbutton"]',
1367
1374
  '[role="switch"]',
1368
1375
  '[role="textbox"]'
1369
- ].join(", "), nt = /* @__PURE__ */ new Set([
1376
+ ].join(", "), rt = /* @__PURE__ */ new Set([
1370
1377
  "checkbox",
1371
1378
  "menuitemcheckbox",
1372
1379
  "menuitemradio",
1373
1380
  "radio",
1374
1381
  "switch"
1375
- ]), rt = /* @__PURE__ */ new Set([
1382
+ ]), ot = /* @__PURE__ */ new Set([
1376
1383
  "combobox",
1377
1384
  "listbox",
1378
1385
  "searchbox",
@@ -1380,68 +1387,78 @@ const tt = {
1380
1387
  "spinbutton",
1381
1388
  "textbox"
1382
1389
  ]);
1383
- function ot(t) {
1384
- var o, s, u, h;
1390
+ function st(t) {
1391
+ var o, s, l, h;
1385
1392
  const a = (o = t.getAttribute("role")) == null ? void 0 : o.trim().toLowerCase();
1386
- if (a && nt.has(a) || (t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement) && !(a && rt.has(a)))
1393
+ if (a && rt.has(a) || (t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement) && !(a && ot.has(a)))
1387
1394
  return v(t);
1388
1395
  const i = t.getAttribute("aria-labelledby");
1389
1396
  if (i) {
1390
- const l = i.split(/\s+/).map((m) => {
1391
- const g = t.ownerDocument.getElementById(m);
1392
- return g ? y(g).trim() : "";
1397
+ const c = i.split(/\s+/).map((d) => {
1398
+ const g = t.ownerDocument.getElementById(d);
1399
+ return g ? w(g).trim() : "";
1393
1400
  }).filter(Boolean);
1394
- if (l.length) return l.join(" ");
1401
+ if (c.length) return c.join(" ");
1395
1402
  }
1396
1403
  const n = (s = t.getAttribute("aria-label")) == null ? void 0 : s.trim();
1397
1404
  if (n) return n;
1398
1405
  if (t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement || t instanceof HTMLSelectElement) {
1399
1406
  if (t.id) {
1400
- const m = t.ownerDocument.querySelector(`label[for="${CSS.escape(t.id)}"]`);
1401
- if (m) {
1402
- const g = y(m).trim();
1407
+ const d = t.ownerDocument.querySelector(`label[for="${CSS.escape(t.id)}"]`);
1408
+ if (d) {
1409
+ const g = w(d).trim();
1403
1410
  if (g) return g;
1404
1411
  }
1405
1412
  }
1406
- const l = t.closest("label");
1407
- if (l) {
1408
- const m = y(l).trim();
1409
- if (m) return m;
1413
+ const c = t.closest("label");
1414
+ if (c) {
1415
+ const d = w(c).trim();
1416
+ if (d) return d;
1410
1417
  }
1411
1418
  }
1412
- const r = (u = t.getAttribute("title")) == null ? void 0 : u.trim();
1419
+ const r = (l = t.getAttribute("title")) == null ? void 0 : l.trim();
1413
1420
  if (r) return r;
1414
1421
  if (t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement) {
1415
- const l = (h = t.getAttribute("placeholder")) == null ? void 0 : h.trim();
1416
- if (l) return l;
1422
+ const c = (h = t.getAttribute("placeholder")) == null ? void 0 : h.trim();
1423
+ if (c) return c;
1417
1424
  }
1418
1425
  return "";
1419
1426
  }
1420
- const st = {
1427
+ const lt = {
1421
1428
  id: "label",
1422
1429
  wcag: ["4.1.2"],
1423
1430
  level: "A",
1424
1431
  description: "Form elements must have labels. Use <label>, aria-label, or aria-labelledby.",
1425
1432
  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.",
1426
- prompt: "Based on the input type, name attribute, or placeholder, suggest a label element with appropriate text, or an aria-label.",
1433
+ 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'.`,
1427
1434
  run(t) {
1428
1435
  var n;
1429
- const a = [], i = t.querySelectorAll(`input:not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]), textarea, select, ${it}`);
1436
+ const a = [], i = t.querySelectorAll(`input:not([type="hidden"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]), textarea, select, ${nt}`);
1430
1437
  for (const r of i) {
1431
1438
  if (p(r) || r instanceof HTMLElement && (r.hidden || r.style.display === "none")) continue;
1432
1439
  const o = (n = r.getAttribute("role")) == null ? void 0 : n.trim().toLowerCase();
1433
1440
  if (o === "presentation" || o === "none") continue;
1434
- ot(r) || a.push({
1435
- ruleId: "label",
1436
- selector: d(r),
1437
- html: c(r),
1438
- impact: "critical",
1439
- message: "Form element has no accessible label."
1440
- });
1441
+ if (!st(r)) {
1442
+ const l = [], h = r.tagName.toLowerCase(), c = r.getAttribute("type");
1443
+ c && h === "input" && l.push(`type: ${c}`);
1444
+ const d = r.getAttribute("name");
1445
+ d && l.push(`name: "${d}"`);
1446
+ const g = r.getAttribute("placeholder");
1447
+ g && l.push(`placeholder: "${g}"`), o && l.push(`role: ${o}`);
1448
+ const b = r.getAttribute("id");
1449
+ b && l.push(`id: "${b}"`), a.push({
1450
+ ruleId: "label",
1451
+ selector: m(r),
1452
+ html: u(r),
1453
+ impact: "critical",
1454
+ message: "Form element has no accessible label.",
1455
+ context: l.length > 0 ? l.join(", ") : void 0
1456
+ });
1457
+ }
1441
1458
  }
1442
1459
  return a;
1443
1460
  }
1444
- }, lt = {
1461
+ }, ct = {
1445
1462
  id: "form-field-multiple-labels",
1446
1463
  wcag: [],
1447
1464
  level: "A",
@@ -1465,15 +1482,15 @@ const st = {
1465
1482
  const s = n.length + r;
1466
1483
  s > 1 && a.push({
1467
1484
  ruleId: "form-field-multiple-labels",
1468
- selector: d(i),
1469
- html: c(i),
1485
+ selector: m(i),
1486
+ html: u(i),
1470
1487
  impact: "moderate",
1471
1488
  message: `Form field has ${s} labels. Use a single label element.`
1472
1489
  });
1473
1490
  }
1474
1491
  return a;
1475
1492
  }
1476
- }, ct = {
1493
+ }, ut = {
1477
1494
  id: "select-name",
1478
1495
  wcag: ["4.1.2"],
1479
1496
  level: "A",
@@ -1485,14 +1502,14 @@ const st = {
1485
1502
  for (const e of t.querySelectorAll("select"))
1486
1503
  p(e) || v(e) || a.push({
1487
1504
  ruleId: "select-name",
1488
- selector: d(e),
1489
- html: c(e),
1505
+ selector: m(e),
1506
+ html: u(e),
1490
1507
  impact: "critical",
1491
1508
  message: "Select element has no accessible name."
1492
1509
  });
1493
1510
  return a;
1494
1511
  }
1495
- }, ut = {
1512
+ }, dt = {
1496
1513
  id: "input-button-name",
1497
1514
  wcag: ["4.1.2"],
1498
1515
  level: "A",
@@ -1509,15 +1526,15 @@ const st = {
1509
1526
  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");
1510
1527
  !r && !s && !v(n) && a.push({
1511
1528
  ruleId: "input-button-name",
1512
- selector: d(n),
1513
- html: c(n),
1529
+ selector: m(n),
1530
+ html: u(n),
1514
1531
  impact: "critical",
1515
1532
  message: "Input button has no discernible text."
1516
1533
  });
1517
1534
  }
1518
1535
  return a;
1519
1536
  }
1520
- }, dt = /* @__PURE__ */ new Set([
1537
+ }, mt = /* @__PURE__ */ new Set([
1521
1538
  "off",
1522
1539
  "on",
1523
1540
  "name",
@@ -1572,7 +1589,7 @@ const st = {
1572
1589
  "impp",
1573
1590
  "url",
1574
1591
  "photo"
1575
- ]), mt = /* @__PURE__ */ new Set([
1592
+ ]), ht = /* @__PURE__ */ new Set([
1576
1593
  "tel",
1577
1594
  "tel-country-code",
1578
1595
  "tel-national",
@@ -1581,18 +1598,18 @@ const st = {
1581
1598
  "tel-extension",
1582
1599
  "email",
1583
1600
  "impp"
1584
- ]), ht = /* @__PURE__ */ new Set(["home", "work", "mobile", "fax", "pager"]), pt = /* @__PURE__ */ new Set(["shipping", "billing"]), gt = /* @__PURE__ */ new Set(["webauthn"]);
1585
- function bt(t) {
1601
+ ]), pt = /* @__PURE__ */ new Set(["home", "work", "mobile", "fax", "pager"]), gt = /* @__PURE__ */ new Set(["shipping", "billing"]), bt = /* @__PURE__ */ new Set(["webauthn"]);
1602
+ function ft(t) {
1586
1603
  const a = t.toLowerCase().split(/\s+/).filter(Boolean);
1587
1604
  if (a.length === 0) return !0;
1588
1605
  let e = 0;
1589
- a[e].startsWith("section-") && e++, e < a.length && pt.has(a[e]) && e++;
1606
+ a[e].startsWith("section-") && e++, e < a.length && gt.has(a[e]) && e++;
1590
1607
  let i = !1;
1591
- if (e < a.length && ht.has(a[e]) && (i = !0, e++), e >= a.length) return !1;
1608
+ if (e < a.length && pt.has(a[e]) && (i = !0, e++), e >= a.length) return !1;
1592
1609
  const n = a[e];
1593
- return !dt.has(n) || i && !mt.has(n) ? !1 : (e++, e < a.length && gt.has(a[e]) && e++, e === a.length);
1610
+ return !mt.has(n) || i && !ht.has(n) ? !1 : (e++, e < a.length && bt.has(a[e]) && e++, e === a.length);
1594
1611
  }
1595
- const ft = {
1612
+ const vt = {
1596
1613
  id: "autocomplete-valid",
1597
1614
  wcag: ["1.3.5"],
1598
1615
  level: "AA",
@@ -1604,10 +1621,10 @@ const ft = {
1604
1621
  for (const e of t.querySelectorAll("[autocomplete]")) {
1605
1622
  if (p(e) || e instanceof HTMLElement && e.style.display === "none" || e.disabled || e.getAttribute("aria-disabled") === "true") continue;
1606
1623
  const i = e.getAttribute("autocomplete").trim();
1607
- i && (bt(i) || a.push({
1624
+ i && (ft(i) || a.push({
1608
1625
  ruleId: "autocomplete-valid",
1609
- selector: d(e),
1610
- html: c(e),
1626
+ selector: m(e),
1627
+ html: u(e),
1611
1628
  impact: "serious",
1612
1629
  message: `Invalid autocomplete value "${i}".`
1613
1630
  }));
@@ -1638,7 +1655,7 @@ function V(t) {
1638
1655
  }
1639
1656
  return a;
1640
1657
  }
1641
- const vt = {
1658
+ const yt = {
1642
1659
  id: "label-content-name-mismatch",
1643
1660
  wcag: ["2.5.3"],
1644
1661
  level: "A",
@@ -1658,8 +1675,8 @@ const vt = {
1658
1675
  const o = e.hasAttribute("aria-label"), s = e.hasAttribute("aria-labelledby");
1659
1676
  !o && !s || ee(i, n) || a.push({
1660
1677
  ruleId: "label-content-name-mismatch",
1661
- selector: d(e),
1662
- html: c(e),
1678
+ selector: m(e),
1679
+ html: u(e),
1663
1680
  impact: "serious",
1664
1681
  message: `Accessible name "${i}" does not contain visible text "${n.trim()}".`
1665
1682
  });
@@ -1676,8 +1693,8 @@ const vt = {
1676
1693
  }
1677
1694
  o.trim() && (ee(i, o) || a.push({
1678
1695
  ruleId: "label-content-name-mismatch",
1679
- selector: d(e),
1680
- html: c(e),
1696
+ selector: m(e),
1697
+ html: u(e),
1681
1698
  impact: "serious",
1682
1699
  message: `Accessible name "${i}" does not contain visible label "${o.trim()}".`
1683
1700
  }));
@@ -1699,25 +1716,25 @@ const vt = {
1699
1716
  );
1700
1717
  for (const s of e) {
1701
1718
  if (p(s)) continue;
1702
- const u = 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()), l = s.hasAttribute("aria-labelledby");
1703
- let m = !1;
1719
+ 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");
1720
+ let d = !1;
1704
1721
  const g = s.id;
1705
1722
  if (g) {
1706
1723
  const f = s.ownerDocument.querySelector(`label[for="${CSS.escape(g)}"]`);
1707
- (r = f == null ? void 0 : f.textContent) != null && r.trim() && (m = !0);
1724
+ (r = f == null ? void 0 : f.textContent) != null && r.trim() && (d = !0);
1708
1725
  }
1709
1726
  const b = s.closest("label");
1710
- (o = b == null ? void 0 : b.textContent) != null && o.trim() && (m = !0), u && !h && !l && !m && a.push({
1727
+ (o = b == null ? void 0 : b.textContent) != null && o.trim() && (d = !0), l && !h && !c && !d && a.push({
1711
1728
  ruleId: "label-title-only",
1712
- selector: d(s),
1713
- html: c(s),
1729
+ selector: m(s),
1730
+ html: u(s),
1714
1731
  impact: "serious",
1715
1732
  message: "Form element uses title attribute as only label. Use <label>, aria-label, or aria-labelledby instead."
1716
1733
  });
1717
1734
  }
1718
1735
  return a;
1719
1736
  }
1720
- }, yt = {
1737
+ }, At = {
1721
1738
  id: "tabindex",
1722
1739
  selector: "[tabindex]",
1723
1740
  check: { type: "attribute-value", attribute: "tabindex", operator: ">", value: 0 },
@@ -1729,7 +1746,7 @@ const vt = {
1729
1746
  tags: ["best-practice"],
1730
1747
  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.",
1731
1748
  prompt: "Change the positive tabindex value to tabindex='0' and rely on DOM order for tab sequence instead."
1732
- }, At = k(yt), St = /* @__PURE__ */ new Set([
1749
+ }, xt = k(At), St = /* @__PURE__ */ new Set([
1733
1750
  "div",
1734
1751
  "span",
1735
1752
  "p",
@@ -1756,7 +1773,7 @@ const vt = {
1756
1773
  "tr",
1757
1774
  "td",
1758
1775
  "th"
1759
- ]), xt = {
1776
+ ]), kt = {
1760
1777
  id: "focus-order-semantics",
1761
1778
  wcag: [],
1762
1779
  tags: ["best-practice"],
@@ -1771,15 +1788,15 @@ const vt = {
1771
1788
  if (!St.has(i)) continue;
1772
1789
  e.getAttribute("role") || a.push({
1773
1790
  ruleId: "focus-order-semantics",
1774
- selector: d(e),
1775
- html: c(e),
1791
+ selector: m(e),
1792
+ html: u(e),
1776
1793
  impact: "moderate",
1777
1794
  message: `Non-interactive <${i}> with tabindex="0" has no interactive role.`
1778
1795
  });
1779
1796
  }
1780
1797
  return a;
1781
1798
  }
1782
- }, kt = /* @__PURE__ */ new Set([
1799
+ }, It = /* @__PURE__ */ new Set([
1783
1800
  "a",
1784
1801
  "audio",
1785
1802
  "button",
@@ -1788,7 +1805,7 @@ const vt = {
1788
1805
  "select",
1789
1806
  "textarea",
1790
1807
  "video"
1791
- ]), It = /* @__PURE__ */ new Set([
1808
+ ]), Tt = /* @__PURE__ */ new Set([
1792
1809
  "button",
1793
1810
  "checkbox",
1794
1811
  "combobox",
@@ -1822,7 +1839,7 @@ const vt = {
1822
1839
  tree: /* @__PURE__ */ new Set(["treeitem"]),
1823
1840
  treegrid: /* @__PURE__ */ new Set(["gridcell", "row", "columnheader", "rowheader", "treeitem"])
1824
1841
  };
1825
- function Tt(t, a) {
1842
+ function Ct(t, a) {
1826
1843
  var n, r, o;
1827
1844
  const e = (n = t.getAttribute("role")) == null ? void 0 : n.toLowerCase(), i = (r = a.getAttribute("role")) == null ? void 0 : r.toLowerCase();
1828
1845
  return !e || !i ? !1 : ((o = Et[e]) == null ? void 0 : o.has(i)) ?? !1;
@@ -1830,18 +1847,18 @@ function Tt(t, a) {
1830
1847
  function Lt(t) {
1831
1848
  var n;
1832
1849
  const a = t.tagName.toLowerCase();
1833
- if (kt.has(a))
1850
+ if (It.has(a))
1834
1851
  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);
1835
1852
  const e = (n = t.getAttribute("role")) == null ? void 0 : n.toLowerCase();
1836
- if (e && It.has(e)) return !0;
1853
+ if (e && Tt.has(e)) return !0;
1837
1854
  const i = t.getAttribute("tabindex");
1838
1855
  return i !== null && i !== "-1" || t.getAttribute("contenteditable") === "true";
1839
1856
  }
1840
- function Ct(t) {
1857
+ function qt(t) {
1841
1858
  const a = t.tagName.toLowerCase();
1842
1859
  return !!(a === "a" && t.hasAttribute("href") || a === "button" && !t.disabled);
1843
1860
  }
1844
- const qt = {
1861
+ const Rt = {
1845
1862
  id: "nested-interactive",
1846
1863
  wcag: ["4.1.2"],
1847
1864
  level: "A",
@@ -1859,21 +1876,21 @@ const qt = {
1859
1876
  if (!p(o) && Lt(o)) {
1860
1877
  if (r.length > 0) {
1861
1878
  const s = r[r.length - 1];
1862
- Tt(s, o) || a.push({
1879
+ Ct(s, o) || a.push({
1863
1880
  ruleId: "nested-interactive",
1864
- selector: d(o),
1865
- html: c(o),
1881
+ selector: m(o),
1882
+ html: u(o),
1866
1883
  impact: "serious",
1867
1884
  message: `Interactive element <${o.tagName.toLowerCase()}> is nested inside <${s.tagName.toLowerCase()}>.`
1868
1885
  });
1869
1886
  }
1870
- Ct(o) && r.push(o);
1887
+ qt(o) && r.push(o);
1871
1888
  }
1872
1889
  o = n.nextNode();
1873
1890
  }
1874
1891
  return a;
1875
1892
  }
1876
- }, Rt = {
1893
+ }, Nt = {
1877
1894
  id: "scrollable-region-focusable",
1878
1895
  wcag: ["2.1.1"],
1879
1896
  level: "A",
@@ -1889,23 +1906,23 @@ const qt = {
1889
1906
  if (e.scrollHeight > 0 || e.clientHeight > 0) {
1890
1907
  if (e.scrollHeight <= e.clientHeight && e.scrollWidth <= e.clientWidth) continue;
1891
1908
  } else {
1892
- const l = i.height !== "" || i.maxHeight !== "", m = e.textContent != null && e.textContent.trim().length > 0;
1893
- if (!l || !m) continue;
1909
+ const c = i.height !== "" || i.maxHeight !== "", d = e.textContent != null && e.textContent.trim().length > 0;
1910
+ if (!c || !d) continue;
1894
1911
  }
1895
- const u = e.getAttribute("tabindex");
1896
- u !== null && u !== "-1" || e.querySelector(
1912
+ const l = e.getAttribute("tabindex");
1913
+ l !== null && l !== "-1" || e.querySelector(
1897
1914
  'a[href], button:not([disabled]), input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
1898
1915
  ) || a.push({
1899
1916
  ruleId: "scrollable-region-focusable",
1900
- selector: d(e),
1901
- html: c(e),
1917
+ selector: m(e),
1918
+ html: u(e),
1902
1919
  impact: "serious",
1903
1920
  message: "Scrollable region is not keyboard accessible. Add tabindex='0' or include focusable elements."
1904
1921
  });
1905
1922
  }
1906
1923
  return a;
1907
1924
  }
1908
- }, Nt = {
1925
+ }, $t = {
1909
1926
  id: "accesskeys",
1910
1927
  wcag: [],
1911
1928
  level: "A",
@@ -1928,8 +1945,8 @@ const qt = {
1928
1945
  for (const o of r.slice(1))
1929
1946
  a.push({
1930
1947
  ruleId: "accesskeys",
1931
- selector: d(o),
1932
- html: c(o),
1948
+ selector: m(o),
1949
+ html: u(o),
1933
1950
  impact: "serious",
1934
1951
  message: `Duplicate accesskey "${n}". Each accesskey must be unique.`
1935
1952
  });
@@ -1951,16 +1968,16 @@ const qt = {
1951
1968
  let o;
1952
1969
  r.hasAttribute("aria-level") ? o = parseInt(r.getAttribute("aria-level"), 10) : o = parseInt(r.tagName[1], 10), i > 0 && o > i + 1 && a.push({
1953
1970
  ruleId: "heading-order",
1954
- selector: d(r),
1955
- html: c(r),
1971
+ selector: m(r),
1972
+ html: u(r),
1956
1973
  impact: "moderate",
1957
1974
  message: `Heading level ${o} skipped from level ${i}.`,
1958
- context: n ? `Previous heading: ${c(n)}` : void 0
1975
+ context: n ? `Previous heading: ${u(n)}` : void 0
1959
1976
  }), i = o, n = r;
1960
1977
  }
1961
1978
  return a;
1962
1979
  }
1963
- }, N = 'article, aside, main, nav, section, [role="article"], [role="complementary"], [role="main"], [role="navigation"], [role="region"]', te = 'main, [role="main"], header, [role="banner"], footer, [role="contentinfo"], nav, [role="navigation"], aside, [role="complementary"], section[aria-label], section[aria-labelledby], [role="region"][aria-label], [role="region"][aria-labelledby], form[aria-label], form[aria-labelledby], [role="form"][aria-label], [role="form"][aria-labelledby], [role="search"]', $t = {
1980
+ }, N = 'article, aside, main, nav, section, [role="article"], [role="complementary"], [role="main"], [role="navigation"], [role="region"]', te = 'main, [role="main"], header, [role="banner"], footer, [role="contentinfo"], nav, [role="navigation"], aside, [role="complementary"], section[aria-label], section[aria-labelledby], [role="region"][aria-label], [role="region"][aria-labelledby], form[aria-label], form[aria-labelledby], [role="form"][aria-label], [role="form"][aria-labelledby], [role="search"]', Ht = {
1964
1981
  id: "landmark-one-main",
1965
1982
  wcag: [],
1966
1983
  level: "A",
@@ -1978,13 +1995,13 @@ const qt = {
1978
1995
  message: "Page has no main landmark."
1979
1996
  }] : a.length > 1 ? Array.from(a).slice(1).map((e) => ({
1980
1997
  ruleId: "landmark-one-main",
1981
- selector: d(e),
1982
- html: c(e),
1998
+ selector: m(e),
1999
+ html: u(e),
1983
2000
  impact: "moderate",
1984
2001
  message: "Page has multiple main landmarks."
1985
2002
  })) : [];
1986
2003
  }
1987
- }, Ht = {
2004
+ }, Dt = {
1988
2005
  id: "landmark-no-duplicate-banner",
1989
2006
  wcag: [],
1990
2007
  level: "A",
@@ -1997,14 +2014,14 @@ const qt = {
1997
2014
  return i.length > 1 && i.slice(1).forEach(
1998
2015
  (n) => a.push({
1999
2016
  ruleId: "landmark-no-duplicate-banner",
2000
- selector: d(n),
2001
- html: c(n),
2017
+ selector: m(n),
2018
+ html: u(n),
2002
2019
  impact: "moderate",
2003
2020
  message: "Page has multiple banner landmarks."
2004
2021
  })
2005
2022
  ), a;
2006
2023
  }
2007
- }, Dt = {
2024
+ }, Ot = {
2008
2025
  id: "landmark-no-duplicate-contentinfo",
2009
2026
  wcag: [],
2010
2027
  level: "A",
@@ -2017,14 +2034,14 @@ const qt = {
2017
2034
  return i.length > 1 && i.slice(1).forEach(
2018
2035
  (n) => a.push({
2019
2036
  ruleId: "landmark-no-duplicate-contentinfo",
2020
- selector: d(n),
2021
- html: c(n),
2037
+ selector: m(n),
2038
+ html: u(n),
2022
2039
  impact: "moderate",
2023
2040
  message: "Page has multiple contentinfo landmarks."
2024
2041
  })
2025
2042
  ), a;
2026
2043
  }
2027
- }, Ot = {
2044
+ }, Bt = {
2028
2045
  id: "landmark-no-duplicate-main",
2029
2046
  wcag: [],
2030
2047
  level: "A",
@@ -2037,14 +2054,14 @@ const qt = {
2037
2054
  return e.length > 1 && Array.from(e).slice(1).forEach(
2038
2055
  (i) => a.push({
2039
2056
  ruleId: "landmark-no-duplicate-main",
2040
- selector: d(i),
2041
- html: c(i),
2057
+ selector: m(i),
2058
+ html: u(i),
2042
2059
  impact: "moderate",
2043
2060
  message: "Page has multiple main landmarks."
2044
2061
  })
2045
2062
  ), a;
2046
2063
  }
2047
- }, Wt = {
2064
+ }, Ft = {
2048
2065
  id: "landmark-banner-is-top-level",
2049
2066
  wcag: [],
2050
2067
  level: "A",
@@ -2057,14 +2074,14 @@ const qt = {
2057
2074
  for (const i of e)
2058
2075
  i.closest(N) && a.push({
2059
2076
  ruleId: "landmark-banner-is-top-level",
2060
- selector: d(i),
2061
- html: c(i),
2077
+ selector: m(i),
2078
+ html: u(i),
2062
2079
  impact: "moderate",
2063
2080
  message: "Banner landmark is nested within another landmark."
2064
2081
  });
2065
2082
  return a;
2066
2083
  }
2067
- }, Bt = {
2084
+ }, Wt = {
2068
2085
  id: "landmark-contentinfo-is-top-level",
2069
2086
  wcag: [],
2070
2087
  level: "A",
@@ -2077,8 +2094,8 @@ const qt = {
2077
2094
  for (const i of e)
2078
2095
  i.closest(N) && a.push({
2079
2096
  ruleId: "landmark-contentinfo-is-top-level",
2080
- selector: d(i),
2081
- html: c(i),
2097
+ selector: m(i),
2098
+ html: u(i),
2082
2099
  impact: "moderate",
2083
2100
  message: "Contentinfo landmark is nested within another landmark."
2084
2101
  });
@@ -2098,15 +2115,15 @@ const qt = {
2098
2115
  const n = i.parentElement;
2099
2116
  n != null && n.closest('article, aside, nav, section, [role="article"], [role="complementary"], [role="navigation"], [role="region"]') && a.push({
2100
2117
  ruleId: "landmark-main-is-top-level",
2101
- selector: d(i),
2102
- html: c(i),
2118
+ selector: m(i),
2119
+ html: u(i),
2103
2120
  impact: "moderate",
2104
2121
  message: "Main landmark is nested within another landmark."
2105
2122
  });
2106
2123
  }
2107
2124
  return a;
2108
2125
  }
2109
- }, Ft = {
2126
+ }, Pt = {
2110
2127
  id: "landmark-complementary-is-top-level",
2111
2128
  wcag: [],
2112
2129
  level: "A",
@@ -2120,8 +2137,8 @@ const qt = {
2120
2137
  const n = i.parentElement;
2121
2138
  n && !n.matches('body, main, [role="main"]') && i.closest('article, nav, section, [role="article"], [role="navigation"], [role="region"]') && a.push({
2122
2139
  ruleId: "landmark-complementary-is-top-level",
2123
- selector: d(i),
2124
- html: c(i),
2140
+ selector: m(i),
2141
+ html: u(i),
2125
2142
  impact: "moderate",
2126
2143
  message: "Complementary landmark should be top-level."
2127
2144
  });
@@ -2148,23 +2165,23 @@ const qt = {
2148
2165
  if (r.length <= 1) continue;
2149
2166
  const o = /* @__PURE__ */ new Map();
2150
2167
  for (const s of r) {
2151
- const u = v(s).toLowerCase() || "", h = o.get(u) || [];
2152
- h.push(s), o.set(u, h);
2168
+ const l = v(s).toLowerCase() || "", h = o.get(l) || [];
2169
+ h.push(s), o.set(l, h);
2153
2170
  }
2154
- for (const [s, u] of o)
2155
- if (u.length > 1)
2156
- for (const h of u.slice(1))
2171
+ for (const [s, l] of o)
2172
+ if (l.length > 1)
2173
+ for (const h of l.slice(1))
2157
2174
  a.push({
2158
2175
  ruleId: "landmark-unique",
2159
- selector: d(h),
2160
- html: c(h),
2176
+ selector: m(h),
2177
+ html: u(h),
2161
2178
  impact: "moderate",
2162
2179
  message: s ? `Multiple ${n} landmarks have the same label "${s}".` : `Multiple ${n} landmarks have no label. Add unique aria-label attributes.`
2163
2180
  });
2164
2181
  }
2165
2182
  return a;
2166
2183
  }
2167
- }, Pt = {
2184
+ }, Vt = {
2168
2185
  id: "region",
2169
2186
  wcag: [],
2170
2187
  level: "A",
@@ -2181,15 +2198,15 @@ const qt = {
2181
2198
  const r = n.matches(te), o = (i = n.textContent) == null ? void 0 : i.trim();
2182
2199
  !r && o && (n.querySelector(te) || a.push({
2183
2200
  ruleId: "region",
2184
- selector: d(n),
2185
- html: c(n),
2201
+ selector: m(n),
2202
+ html: u(n),
2186
2203
  impact: "moderate",
2187
2204
  message: "Content is not contained within a landmark region."
2188
2205
  }));
2189
2206
  }
2190
2207
  return a;
2191
2208
  }
2192
- }, Vt = {
2209
+ }, zt = {
2193
2210
  id: "list",
2194
2211
  selector: "ul, ol",
2195
2212
  check: { type: "child-invalid", allowedChildren: ["li", "script", "template"] },
@@ -2200,7 +2217,7 @@ const qt = {
2200
2217
  level: "A",
2201
2218
  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.",
2202
2219
  prompt: "Explain how to restructure this element within the list properly."
2203
- }, zt = k(Vt), Ut = {
2220
+ }, Ut = k(zt), Gt = {
2204
2221
  id: "dlitem",
2205
2222
  wcag: ["1.3.1"],
2206
2223
  level: "A",
@@ -2212,14 +2229,14 @@ const qt = {
2212
2229
  for (const e of t.querySelectorAll("dt, dd"))
2213
2230
  (!e.parentElement || e.parentElement.tagName.toLowerCase() !== "dl") && a.push({
2214
2231
  ruleId: "dlitem",
2215
- selector: d(e),
2216
- html: c(e),
2232
+ selector: m(e),
2233
+ html: u(e),
2217
2234
  impact: "serious",
2218
2235
  message: `<${e.tagName.toLowerCase()}> is not contained in a <dl>.`
2219
2236
  });
2220
2237
  return a;
2221
2238
  }
2222
- }, Gt = {
2239
+ }, Xt = {
2223
2240
  id: "definition-list",
2224
2241
  selector: "dl",
2225
2242
  check: { type: "child-invalid", allowedChildren: ["dt", "dd", "div", "script", "template"] },
@@ -2230,49 +2247,64 @@ const qt = {
2230
2247
  level: "A",
2231
2248
  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.",
2232
2249
  prompt: "Explain whether to move this element outside the <dl> or convert it to dt/dd."
2233
- }, Xt = k(Gt), Yt = {
2250
+ }, Yt = k(Xt), Kt = {
2234
2251
  id: "document-title",
2235
2252
  wcag: ["2.4.2"],
2236
2253
  level: "A",
2237
2254
  description: "Documents must have a <title> element to provide users with an overview of content.",
2238
2255
  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').",
2239
- prompt: "Suggest a descriptive page title based on the visible content.",
2256
+ 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'.",
2240
2257
  run(t) {
2241
- var e;
2258
+ var e, i, n;
2242
2259
  const a = t.querySelector("title");
2243
- return !a || !((e = a.textContent) != null && e.trim()) ? [{
2244
- ruleId: "document-title",
2245
- selector: "html",
2246
- html: "<html>",
2247
- impact: "serious",
2248
- message: a ? "Document <title> element is empty." : "Document is missing a <title> element."
2249
- }] : [];
2260
+ if (!a || !((e = a.textContent) != null && e.trim())) {
2261
+ let r;
2262
+ const o = t.querySelector("h1");
2263
+ if ((i = o == null ? void 0 : o.textContent) != null && i.trim())
2264
+ r = `h1: "${o.textContent.trim().slice(0, 100)}"`;
2265
+ else if (t.body) {
2266
+ const s = ((n = t.body.textContent) == null ? void 0 : n.trim().replace(/\s+/g, " ")) || "";
2267
+ s && (r = `Page text: "${s.slice(0, 150)}"`);
2268
+ }
2269
+ return [{
2270
+ ruleId: "document-title",
2271
+ selector: "html",
2272
+ html: "<html>",
2273
+ impact: "serious",
2274
+ message: a ? "Document <title> element is empty." : "Document is missing a <title> element.",
2275
+ context: r
2276
+ }];
2277
+ }
2278
+ return [];
2250
2279
  }
2251
- }, Kt = {
2280
+ }, Jt = {
2252
2281
  id: "bypass",
2253
2282
  wcag: ["2.4.1"],
2254
2283
  level: "A",
2255
2284
  description: "Page must have a mechanism to bypass repeated blocks of content.",
2256
2285
  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.',
2257
- prompt: "Explain whether to add a skip link or a <main> landmark based on the page structure.",
2286
+ 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.',
2258
2287
  run(t) {
2259
2288
  if (t.querySelector(
2260
2289
  'main, [role="main"], nav, [role="navigation"], aside, [role="complementary"], header, [role="banner"], footer, [role="contentinfo"], [role="search"], [role="region"]'
2261
2290
  )) return [];
2262
2291
  const e = t.querySelector('a[href^="#"]');
2263
2292
  if (e) {
2264
- const n = e.getAttribute("href");
2265
- if (n && n.length > 1) {
2266
- const r = n.slice(1);
2267
- if (t.getElementById(r)) return [];
2293
+ const r = e.getAttribute("href");
2294
+ if (r && r.length > 1) {
2295
+ const o = r.slice(1);
2296
+ if (t.getElementById(o)) return [];
2268
2297
  }
2269
2298
  }
2270
- return t.querySelector("h1, h2, h3, [role='heading']") ? [] : [{
2299
+ if (t.querySelector("h1, h2, h3, [role='heading']")) return [];
2300
+ const n = [];
2301
+ return n.push("no landmarks (<main>, <nav>, <header>, <footer>)"), n.push("no skip link"), n.push("no headings"), [{
2271
2302
  ruleId: "bypass",
2272
2303
  selector: "html",
2273
2304
  html: "<html>",
2274
2305
  impact: "serious",
2275
- message: "Page has no mechanism to bypass repeated content. Add a <main> landmark or skip link."
2306
+ message: "Page has no mechanism to bypass repeated content. Add a <main> landmark or skip link.",
2307
+ context: `Missing: ${n.join(", ")}`
2276
2308
  }];
2277
2309
  }
2278
2310
  }, Qt = {
@@ -2282,43 +2314,63 @@ const qt = {
2282
2314
  tags: ["best-practice"],
2283
2315
  description: "Page should contain a level-one heading.",
2284
2316
  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.",
2285
- prompt: "Suggest appropriate h1 text based on the page's visible content.",
2317
+ 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.",
2286
2318
  run(t) {
2319
+ var o, s, l;
2287
2320
  const a = t.querySelector("h1");
2288
2321
  if (a && v(a)) return [];
2289
2322
  const e = t.querySelectorAll('[role="heading"][aria-level="1"]');
2290
- for (const i of e)
2291
- if (v(i)) return [];
2323
+ for (const h of e)
2324
+ if (v(h)) return [];
2325
+ const i = [], n = (s = (o = t.querySelector("title")) == null ? void 0 : o.textContent) == null ? void 0 : s.trim();
2326
+ n && i.push(`Page title: "${n}"`);
2327
+ const r = t.querySelector("main");
2328
+ if (r) {
2329
+ const h = ((l = r.textContent) == null ? void 0 : l.trim().replace(/\s+/g, " ")) || "";
2330
+ h && i.push(`Main content: "${h.slice(0, 100)}"`);
2331
+ }
2292
2332
  return [{
2293
2333
  ruleId: "page-has-heading-one",
2294
2334
  selector: "html",
2295
2335
  html: "<html>",
2296
2336
  impact: "moderate",
2297
- message: "Page does not contain a level-one heading."
2337
+ message: "Page does not contain a level-one heading.",
2338
+ context: i.length > 0 ? i.join(", ") : void 0
2298
2339
  }];
2299
2340
  }
2300
- }, Jt = {
2341
+ };
2342
+ function me(t) {
2343
+ if (!(t instanceof HTMLElement)) return !1;
2344
+ if (t.style.display === "none" || t.style.visibility === "hidden") return !0;
2345
+ const a = t.getAttribute("width"), e = t.getAttribute("height");
2346
+ return (a === "0" || a === "1") && (e === "0" || e === "1");
2347
+ }
2348
+ const Zt = {
2301
2349
  id: "frame-title",
2302
2350
  wcag: ["4.1.2"],
2303
2351
  level: "A",
2304
2352
  description: "Frames must have an accessible name.",
2305
2353
  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'.",
2306
- prompt: "Suggest a descriptive title based on the frame's src URL or visible content.",
2354
+ 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.",
2307
2355
  run(t) {
2308
2356
  const a = [];
2309
2357
  for (const e of t.querySelectorAll("iframe, frame")) {
2310
- if (p(e)) continue;
2311
- v(e) || a.push({
2312
- ruleId: "frame-title",
2313
- selector: d(e),
2314
- html: c(e),
2315
- impact: "serious",
2316
- message: "Frame is missing an accessible name. Add a title attribute."
2317
- });
2358
+ if (p(e) || me(e)) continue;
2359
+ if (!v(e)) {
2360
+ const n = e.getAttribute("src");
2361
+ a.push({
2362
+ ruleId: "frame-title",
2363
+ selector: m(e),
2364
+ html: u(e),
2365
+ impact: "serious",
2366
+ message: "Frame is missing an accessible name. Add a title attribute.",
2367
+ context: n ? `src: "${n}"` : void 0
2368
+ });
2369
+ }
2318
2370
  }
2319
2371
  return a;
2320
2372
  }
2321
- }, Zt = {
2373
+ }, ea = {
2322
2374
  id: "frame-title-unique",
2323
2375
  wcag: ["4.1.2"],
2324
2376
  level: "A",
@@ -2330,13 +2382,11 @@ const qt = {
2330
2382
  var n;
2331
2383
  const a = [], e = Array.from(t.querySelectorAll("iframe[title], frame[title]")), i = /* @__PURE__ */ new Map();
2332
2384
  for (const r of e) {
2333
- if (p(r)) continue;
2334
- const o = r.getAttribute("width"), s = r.getAttribute("height");
2335
- if (o === "0" || s === "0" || r instanceof HTMLElement && r.style.display === "none" || r instanceof HTMLElement && r.style.visibility === "hidden") continue;
2336
- const u = (n = r.getAttribute("title")) == null ? void 0 : n.trim().toLowerCase();
2337
- if (u) {
2338
- const h = i.get(u) || [];
2339
- h.push(r), i.set(u, h);
2385
+ if (p(r) || me(r)) continue;
2386
+ const o = (n = r.getAttribute("title")) == null ? void 0 : n.trim().toLowerCase();
2387
+ if (o) {
2388
+ const s = i.get(o) || [];
2389
+ s.push(r), i.set(o, s);
2340
2390
  }
2341
2391
  }
2342
2392
  for (const [, r] of i)
@@ -2344,66 +2394,77 @@ const qt = {
2344
2394
  for (const o of r.slice(1))
2345
2395
  a.push({
2346
2396
  ruleId: "frame-title-unique",
2347
- selector: d(o),
2348
- html: c(o),
2397
+ selector: m(o),
2398
+ html: u(o),
2349
2399
  impact: "moderate",
2350
2400
  message: "Frame title is not unique. Use a distinct title for each frame."
2351
2401
  });
2352
2402
  return a;
2353
2403
  }
2354
- }, ea = {
2404
+ }, ta = {
2355
2405
  id: "empty-heading",
2356
2406
  wcag: [],
2357
2407
  level: "A",
2358
2408
  tags: ["best-practice"],
2359
2409
  description: "Headings must have discernible text.",
2360
2410
  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.",
2361
- prompt: "Suggest appropriate heading text or explain why to use a different element.",
2411
+ 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.",
2362
2412
  run(t) {
2413
+ var i;
2363
2414
  const a = [], e = t.querySelectorAll('h1, h2, h3, h4, h5, h6, [role="heading"]');
2364
- for (const i of e)
2365
- p(i) || v(i) || a.push({
2366
- ruleId: "empty-heading",
2367
- selector: d(i),
2368
- html: c(i),
2369
- impact: "minor",
2370
- message: "Heading is empty. Add text content or remove the heading element."
2371
- });
2415
+ for (const n of e)
2416
+ if (!p(n) && !v(n)) {
2417
+ let r;
2418
+ const o = n.nextElementSibling;
2419
+ if (o) {
2420
+ const s = ((i = o.textContent) == null ? void 0 : i.trim().replace(/\s+/g, " ")) || "";
2421
+ s && (r = s.slice(0, 100));
2422
+ }
2423
+ a.push({
2424
+ ruleId: "empty-heading",
2425
+ selector: m(n),
2426
+ html: u(n),
2427
+ impact: "minor",
2428
+ message: "Heading is empty. Add text content or remove the heading element.",
2429
+ context: r ? `Following content: "${r}"` : void 0
2430
+ });
2431
+ }
2372
2432
  return a;
2373
2433
  }
2374
- }, ta = {
2434
+ }, aa = {
2375
2435
  id: "meta-viewport",
2376
2436
  wcag: ["1.4.4"],
2377
2437
  level: "AA",
2378
2438
  description: "Viewport meta tag must not disable user scaling.",
2379
2439
  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.",
2380
- prompt: "Explain which viewport restrictions to remove and show the corrected meta tag.",
2440
+ 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.",
2381
2441
  run(t) {
2382
- var r;
2383
2442
  const a = [], e = t.querySelector('meta[name="viewport"]');
2384
2443
  if (!e) return [];
2385
- const i = ((r = e.getAttribute("content")) == null ? void 0 : r.toLowerCase()) || "";
2386
- (/user-scalable\s*=\s*no/i.test(i) || /user-scalable\s*=\s*0/i.test(i)) && a.push({
2444
+ const i = e.getAttribute("content") || "", n = i.toLowerCase();
2445
+ (/user-scalable\s*=\s*no/i.test(n) || /user-scalable\s*=\s*0/i.test(n)) && a.push({
2387
2446
  ruleId: "meta-viewport",
2388
- selector: d(e),
2389
- html: c(e),
2447
+ selector: m(e),
2448
+ html: u(e),
2390
2449
  impact: "critical",
2391
- message: "Viewport disables user scaling. Remove user-scalable=no."
2450
+ message: "Viewport disables user scaling. Remove user-scalable=no.",
2451
+ context: `content: "${i}"`
2392
2452
  });
2393
- const n = i.match(/maximum-scale\s*=\s*([\d.]+|yes)/i);
2394
- if (n) {
2395
- const o = n[1], s = o.toLowerCase() === "yes" ? 1 : parseFloat(o);
2453
+ const r = n.match(/maximum-scale\s*=\s*([\d.]+|yes)/i);
2454
+ if (r) {
2455
+ const o = r[1], s = o.toLowerCase() === "yes" ? 1 : parseFloat(o);
2396
2456
  s < 2 && a.push({
2397
2457
  ruleId: "meta-viewport",
2398
- selector: d(e),
2399
- html: c(e),
2458
+ selector: m(e),
2459
+ html: u(e),
2400
2460
  impact: "critical",
2401
- message: `Viewport maximum-scale=${s} restricts zooming. Set to at least 2 or remove.`
2461
+ message: `Viewport maximum-scale=${s} restricts zooming. Set to at least 2 or remove.`,
2462
+ context: `content: "${i}"`
2402
2463
  });
2403
2464
  }
2404
2465
  return a;
2405
2466
  }
2406
- }, aa = {
2467
+ }, ia = {
2407
2468
  id: "meta-refresh",
2408
2469
  wcag: ["2.2.1", "2.2.4", "3.2.5"],
2409
2470
  level: "A",
@@ -2418,23 +2479,23 @@ const qt = {
2418
2479
  if (/^\d+\s*[;,]\s*url\s*=/i.test(e) || /^\d+\s*[;,]\s*['"]?\s*https?:/i.test(e))
2419
2480
  return n > 0 && n <= 72e3 ? [{
2420
2481
  ruleId: "meta-refresh",
2421
- selector: d(a),
2422
- html: c(a),
2482
+ selector: m(a),
2483
+ html: u(a),
2423
2484
  impact: "critical",
2424
2485
  message: `Page redirects after ${n} seconds without warning. Use server-side redirect.`
2425
2486
  }] : [];
2426
2487
  if (n > 0 && n <= 72e3)
2427
2488
  return [{
2428
2489
  ruleId: "meta-refresh",
2429
- selector: d(a),
2430
- html: c(a),
2490
+ selector: m(a),
2491
+ html: u(a),
2431
2492
  impact: "critical",
2432
2493
  message: `Page auto-refreshes after ${n} seconds. Provide user control over refresh.`
2433
2494
  }];
2434
2495
  }
2435
2496
  return [];
2436
2497
  }
2437
- }, ia = {
2498
+ }, na = {
2438
2499
  id: "blink",
2439
2500
  selector: "blink",
2440
2501
  check: { type: "selector-exists" },
@@ -2445,7 +2506,7 @@ const qt = {
2445
2506
  level: "A",
2446
2507
  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.",
2447
2508
  prompt: "Suggest static alternatives to the blinking effect."
2448
- }, na = k(ia), ra = {
2509
+ }, ra = k(na), oa = {
2449
2510
  id: "marquee",
2450
2511
  selector: "marquee",
2451
2512
  check: { type: "selector-exists" },
@@ -2456,7 +2517,7 @@ const qt = {
2456
2517
  level: "A",
2457
2518
  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.",
2458
2519
  prompt: "Suggest static alternatives or accessible carousel patterns."
2459
- }, oa = k(ra), sa = {
2520
+ }, sa = k(oa), la = {
2460
2521
  id: "p-as-heading",
2461
2522
  wcag: [],
2462
2523
  level: "A",
@@ -2469,13 +2530,13 @@ const qt = {
2469
2530
  const a = [];
2470
2531
  for (const n of t.querySelectorAll("p")) {
2471
2532
  if (p(n)) continue;
2472
- 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), u = ((e = n.className) == null ? void 0 : e.toLowerCase()) || "", h = /\bh[1-6]\b|\bheading\b/.test(u), l = ((i = n.textContent) == null ? void 0 : i.trim()) || "", m = l.length > 0 && l.length < 50, g = !l.match(/[.!?,;:]$/);
2473
- if ((o && s || o && h) && m && g) {
2474
- const w = n.nextElementSibling;
2475
- w && (w.tagName === "P" || w.tagName === "DIV" || w.tagName === "UL") && a.push({
2533
+ 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()) || "", d = c.length > 0 && c.length < 50, g = !c.match(/[.!?,;:]$/);
2534
+ if ((o && s || o && h) && d && g) {
2535
+ const y = n.nextElementSibling;
2536
+ y && (y.tagName === "P" || y.tagName === "DIV" || y.tagName === "UL") && a.push({
2476
2537
  ruleId: "p-as-heading",
2477
- selector: d(n),
2478
- html: c(n),
2538
+ selector: m(n),
2539
+ html: u(n),
2479
2540
  impact: "serious",
2480
2541
  message: "Paragraph appears to be styled as a heading. Use an h1-h6 element instead."
2481
2542
  });
@@ -2483,7 +2544,7 @@ const qt = {
2483
2544
  }
2484
2545
  return a;
2485
2546
  }
2486
- }, la = {
2547
+ }, ca = {
2487
2548
  id: "aria-roles",
2488
2549
  wcag: ["4.1.2"],
2489
2550
  level: "A",
@@ -2494,17 +2555,17 @@ const qt = {
2494
2555
  const a = [];
2495
2556
  for (const e of t.querySelectorAll("[role]")) {
2496
2557
  const r = e.getAttribute("role").replace(/[\u201C\u201D\u2018\u2019\u00AB\u00BB]/g, "").split(/\s+/).filter(Boolean);
2497
- !r.some((s) => Ae(s)) && r.length > 0 && a.push({
2558
+ !r.some((s) => xe(s)) && r.length > 0 && a.push({
2498
2559
  ruleId: "aria-roles",
2499
- selector: d(e),
2500
- html: c(e),
2560
+ selector: m(e),
2561
+ html: u(e),
2501
2562
  impact: "critical",
2502
2563
  message: `Invalid ARIA role "${r[0]}".`
2503
2564
  });
2504
2565
  }
2505
2566
  return a;
2506
2567
  }
2507
- }, ca = {
2568
+ }, ua = {
2508
2569
  id: "aria-valid-attr",
2509
2570
  wcag: ["4.1.2"],
2510
2571
  level: "A",
@@ -2514,7 +2575,7 @@ const qt = {
2514
2575
  run(t) {
2515
2576
  return z(t).validAttr;
2516
2577
  }
2517
- }, ua = {
2578
+ }, da = {
2518
2579
  id: "aria-valid-attr-value",
2519
2580
  wcag: ["4.1.2"],
2520
2581
  level: "A",
@@ -2524,7 +2585,7 @@ const qt = {
2524
2585
  run(t) {
2525
2586
  return z(t).validAttrValue;
2526
2587
  }
2527
- }, da = {
2588
+ }, ma = {
2528
2589
  checkbox: ["aria-checked"],
2529
2590
  combobox: ["aria-expanded"],
2530
2591
  heading: ["aria-level"],
@@ -2538,7 +2599,7 @@ const qt = {
2538
2599
  slider: ["aria-valuenow"],
2539
2600
  spinbutton: ["aria-valuenow"],
2540
2601
  switch: ["aria-checked"]
2541
- }, ma = {
2602
+ }, ha = {
2542
2603
  id: "aria-required-attr",
2543
2604
  wcag: ["4.1.2"],
2544
2605
  level: "A",
@@ -2549,7 +2610,7 @@ const qt = {
2549
2610
  const a = [];
2550
2611
  for (const e of t.querySelectorAll("[role]")) {
2551
2612
  if (p(e) || e instanceof HTMLElement && e.style.display === "none") continue;
2552
- const i = e.getAttribute("role").trim().toLowerCase(), n = da[i];
2613
+ const i = e.getAttribute("role").trim().toLowerCase(), n = ma[i];
2553
2614
  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))) {
2554
2615
  if (i === "separator") {
2555
2616
  const r = e.getAttribute("tabindex");
@@ -2560,8 +2621,8 @@ const qt = {
2560
2621
  if (!e.hasAttribute(r)) {
2561
2622
  a.push({
2562
2623
  ruleId: "aria-required-attr",
2563
- selector: d(e),
2564
- html: c(e),
2624
+ selector: m(e),
2625
+ html: u(e),
2565
2626
  impact: "critical",
2566
2627
  message: `Role "${i}" requires attribute "${r}".`
2567
2628
  });
@@ -2573,24 +2634,24 @@ const qt = {
2573
2634
  return a;
2574
2635
  }
2575
2636
  };
2576
- function ha(t) {
2637
+ function pa(t) {
2577
2638
  var r, o, s;
2578
2639
  const a = [], e = t.className;
2579
2640
  e && typeof e == "string" && e.trim() && a.push(`Classes: ${e.trim().slice(0, 100)}`);
2580
2641
  const i = t.closest("form");
2581
2642
  if (i) {
2582
- const u = i.getAttribute("aria-label") || ((o = (r = i.querySelector("legend")) == null ? void 0 : r.textContent) == null ? void 0 : o.trim());
2583
- u && a.push(`Form: ${u.slice(0, 60)}`);
2643
+ const l = i.getAttribute("aria-label") || ((o = (r = i.querySelector("legend")) == null ? void 0 : r.textContent) == null ? void 0 : o.trim());
2644
+ l && a.push(`Form: ${l.slice(0, 60)}`);
2584
2645
  }
2585
2646
  const n = t.parentElement;
2586
2647
  if (n) {
2587
- const u = n.closest("h1, h2, h3, h4, h5, h6") || n.querySelector("h1, h2, h3, h4, h5, h6");
2588
- (s = u == null ? void 0 : u.textContent) != null && s.trim() && a.push(`Nearby heading: ${u.textContent.trim().slice(0, 60)}`);
2648
+ const l = n.closest("h1, h2, h3, h4, h5, h6") || n.querySelector("h1, h2, h3, h4, h5, h6");
2649
+ (s = l == null ? void 0 : l.textContent) != null && s.trim() && a.push(`Nearby heading: ${l.textContent.trim().slice(0, 60)}`);
2589
2650
  }
2590
2651
  return a.length > 0 ? a.join(`
2591
2652
  `) : void 0;
2592
2653
  }
2593
- const pa = {
2654
+ const ga = {
2594
2655
  id: "button-name",
2595
2656
  wcag: ["4.1.2"],
2596
2657
  level: "A",
@@ -2605,16 +2666,16 @@ const pa = {
2605
2666
  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;
2606
2667
  v(e) || a.push({
2607
2668
  ruleId: "button-name",
2608
- selector: d(e),
2609
- html: c(e),
2669
+ selector: m(e),
2670
+ html: u(e),
2610
2671
  impact: "critical",
2611
2672
  message: "Button has no discernible text.",
2612
- context: ha(e)
2673
+ context: pa(e)
2613
2674
  });
2614
2675
  }
2615
2676
  return a;
2616
2677
  }
2617
- }, ga = {
2678
+ }, ba = {
2618
2679
  alert: /* @__PURE__ */ new Set(["aria-atomic", "aria-busy", "aria-live", "aria-relevant"]),
2619
2680
  alertdialog: /* @__PURE__ */ new Set(["aria-describedby", "aria-modal"]),
2620
2681
  application: /* @__PURE__ */ new Set(["aria-activedescendant", "aria-disabled", "aria-errormessage", "aria-expanded", "aria-haspopup", "aria-invalid"]),
@@ -2686,7 +2747,7 @@ const pa = {
2686
2747
  tree: /* @__PURE__ */ new Set(["aria-activedescendant", "aria-disabled", "aria-errormessage", "aria-invalid", "aria-multiselectable", "aria-orientation", "aria-required"]),
2687
2748
  treegrid: /* @__PURE__ */ new Set(["aria-activedescendant", "aria-colcount", "aria-disabled", "aria-errormessage", "aria-invalid", "aria-multiselectable", "aria-orientation", "aria-readonly", "aria-required", "aria-rowcount"]),
2688
2749
  treeitem: /* @__PURE__ */ new Set(["aria-checked", "aria-disabled", "aria-expanded", "aria-haspopup", "aria-level", "aria-posinset", "aria-selected", "aria-setsize"])
2689
- }, ba = /* @__PURE__ */ new Set([
2750
+ }, fa = /* @__PURE__ */ new Set([
2690
2751
  "aria-atomic",
2691
2752
  "aria-busy",
2692
2753
  "aria-controls",
@@ -2710,33 +2771,37 @@ const pa = {
2710
2771
  "aria-roledescription",
2711
2772
  "aria-braillelabel",
2712
2773
  "aria-brailleroledescription"
2713
- ]), fa = {
2774
+ ]), va = {
2714
2775
  id: "aria-allowed-attr",
2715
2776
  wcag: ["4.1.2"],
2716
2777
  level: "A",
2717
2778
  description: "ARIA attributes must be allowed for the element's role.",
2718
2779
  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.",
2719
- prompt: "State which attribute is not allowed on this role and recommend removing it or using a different element/role that supports the attribute.",
2780
+ 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.",
2720
2781
  run(t) {
2721
2782
  const a = [];
2722
2783
  for (const e of t.querySelectorAll("[role], [aria-*]")) {
2723
2784
  if (p(e)) continue;
2724
- const i = C(e);
2785
+ const i = L(e);
2725
2786
  if (!i) continue;
2726
- const n = ga[i];
2787
+ const n = ba[i];
2727
2788
  if (n)
2728
- for (const r of e.attributes)
2729
- r.name.startsWith("aria-") && (ba.has(r.name) || n.has(r.name) || a.push({
2789
+ for (const r of e.attributes) {
2790
+ if (!r.name.startsWith("aria-") || fa.has(r.name) || n.has(r.name)) continue;
2791
+ const o = n.size > 0 ? [...n].join(", ") : "none (only global ARIA attributes)";
2792
+ a.push({
2730
2793
  ruleId: "aria-allowed-attr",
2731
- selector: d(e),
2732
- html: c(e),
2794
+ selector: m(e),
2795
+ html: u(e),
2733
2796
  impact: "critical",
2734
- message: `ARIA attribute "${r.name}" is not allowed on role "${i}".`
2735
- }));
2797
+ message: `ARIA attribute "${r.name}" is not allowed on role "${i}".`,
2798
+ context: `Attribute: ${r.name}="${r.value}", role: ${i}, allowed role-specific attributes: ${o}`
2799
+ });
2800
+ }
2736
2801
  }
2737
2802
  return a;
2738
2803
  }
2739
- }, va = /* @__PURE__ */ new Set([
2804
+ }, ya = /* @__PURE__ */ new Set([
2740
2805
  "base",
2741
2806
  "col",
2742
2807
  "colgroup",
@@ -2751,7 +2816,7 @@ const pa = {
2751
2816
  "template",
2752
2817
  "title",
2753
2818
  "track"
2754
- ]), E = {
2819
+ ]), T = {
2755
2820
  a: /* @__PURE__ */ new Set(["button", "checkbox", "menuitem", "menuitemcheckbox", "menuitemradio", "option", "radio", "switch", "tab", "treeitem", "link"]),
2756
2821
  "a[href]": /* @__PURE__ */ new Set(["button", "checkbox", "menuitem", "menuitemcheckbox", "menuitemradio", "option", "radio", "switch", "tab", "treeitem"]),
2757
2822
  abbr: "any",
@@ -2861,19 +2926,19 @@ const pa = {
2861
2926
  function wa(t) {
2862
2927
  var e;
2863
2928
  const a = t.tagName.toLowerCase();
2864
- if (va.has(a))
2929
+ if (ya.has(a))
2865
2930
  return "none";
2866
2931
  if (a === "a" && t.hasAttribute("href"))
2867
- return E["a[href]"];
2932
+ return T["a[href]"];
2868
2933
  if (a === "img" && t.getAttribute("alt") === "")
2869
- return E["img[alt='']"];
2934
+ return T["img[alt='']"];
2870
2935
  if (a === "input") {
2871
2936
  const n = `input[type=${((e = t.getAttribute("type")) == null ? void 0 : e.toLowerCase()) || "text"}]`;
2872
- return n in E ? E[n] : "none";
2937
+ return n in T ? T[n] : "none";
2873
2938
  }
2874
- return E[a] || "any";
2939
+ return T[a] || "any";
2875
2940
  }
2876
- const ya = {
2941
+ const Aa = {
2877
2942
  id: "aria-allowed-role",
2878
2943
  wcag: ["4.1.2"],
2879
2944
  level: "A",
@@ -2892,14 +2957,14 @@ const ya = {
2892
2957
  const o = wa(i);
2893
2958
  o === "none" ? a.push({
2894
2959
  ruleId: "aria-allowed-role",
2895
- selector: d(i),
2896
- html: c(i),
2960
+ selector: m(i),
2961
+ html: u(i),
2897
2962
  impact: "minor",
2898
2963
  message: `Element <${i.tagName.toLowerCase()}> should not have an explicit role.`
2899
2964
  }) : o !== "any" && !o.has(n) && a.push({
2900
2965
  ruleId: "aria-allowed-role",
2901
- selector: d(i),
2902
- html: c(i),
2966
+ selector: m(i),
2967
+ html: u(i),
2903
2968
  impact: "minor",
2904
2969
  message: `Role "${n}" is not allowed on element <${i.tagName.toLowerCase()}>.`
2905
2970
  });
@@ -2937,22 +3002,22 @@ const ya = {
2937
3002
  tab: ["tablist"],
2938
3003
  treeitem: ["tree", "group"]
2939
3004
  };
2940
- function Aa(t, a) {
3005
+ function xa(t, a) {
2941
3006
  var r;
2942
3007
  const e = ((r = t.getAttribute("aria-owns")) == null ? void 0 : r.split(/\s+/)) || [], i = t.ownerDocument, n = /* @__PURE__ */ new Set();
2943
3008
  for (const o of t.querySelectorAll("*")) {
2944
- const s = C(o);
3009
+ const s = L(o);
2945
3010
  s && !p(o) && n.add(s);
2946
3011
  }
2947
3012
  for (const o of e) {
2948
3013
  const s = i.getElementById(o);
2949
3014
  if (s) {
2950
- const u = C(s);
2951
- u && !p(s) && n.add(u);
3015
+ const l = L(s);
3016
+ l && !p(s) && n.add(l);
2952
3017
  }
2953
3018
  }
2954
3019
  for (const o of a)
2955
- if (!o.some((u) => n.has(u))) return !1;
3020
+ if (!o.some((l) => n.has(l))) return !1;
2956
3021
  return !0;
2957
3022
  }
2958
3023
  const Sa = {
@@ -2970,12 +3035,12 @@ const Sa = {
2970
3035
  const n = (e = i.getAttribute("role")) == null ? void 0 : e.trim().toLowerCase();
2971
3036
  if (!n || !(n in ae)) continue;
2972
3037
  const r = ae[n];
2973
- if (!Aa(i, r)) {
3038
+ if (!xa(i, r)) {
2974
3039
  const o = r.map((s) => s.join(" or ")).join(", ");
2975
3040
  a.push({
2976
3041
  ruleId: "aria-required-children",
2977
- selector: d(i),
2978
- html: c(i),
3042
+ selector: m(i),
3043
+ html: u(i),
2979
3044
  impact: "critical",
2980
3045
  message: `Role "${n}" requires children with role: ${o}.`
2981
3046
  });
@@ -2983,7 +3048,7 @@ const Sa = {
2983
3048
  }
2984
3049
  return a;
2985
3050
  }
2986
- }, xa = {
3051
+ }, ka = {
2987
3052
  id: "aria-required-parent",
2988
3053
  wcag: ["4.1.2"],
2989
3054
  level: "A",
@@ -3000,8 +3065,8 @@ const Sa = {
3000
3065
  const r = ie[n];
3001
3066
  let o = i.parentElement, s = !1;
3002
3067
  for (; o && o !== t.documentElement; ) {
3003
- const u = C(o);
3004
- if (u && r.includes(u)) {
3068
+ const l = L(o);
3069
+ if (l && r.includes(l)) {
3005
3070
  s = !0;
3006
3071
  break;
3007
3072
  }
@@ -3009,8 +3074,8 @@ const Sa = {
3009
3074
  }
3010
3075
  s || a.push({
3011
3076
  ruleId: "aria-required-parent",
3012
- selector: d(i),
3013
- html: c(i),
3077
+ selector: m(i),
3078
+ html: u(i),
3014
3079
  impact: "critical",
3015
3080
  message: `Role "${n}" must be contained within: ${r.join(", ")}.`
3016
3081
  });
@@ -3033,7 +3098,7 @@ const Sa = {
3033
3098
  "embed",
3034
3099
  "area[href]"
3035
3100
  ].join(", ");
3036
- function ka(t) {
3101
+ function Ia(t) {
3037
3102
  let a = t;
3038
3103
  const e = t.ownerDocument, i = e.defaultView;
3039
3104
  for (; a && a !== e.body; ) {
@@ -3046,7 +3111,7 @@ function ka(t) {
3046
3111
  }
3047
3112
  return !0;
3048
3113
  }
3049
- const Ia = {
3114
+ const Ta = {
3050
3115
  id: "aria-hidden-body",
3051
3116
  selector: 'body[aria-hidden="true"]',
3052
3117
  check: { type: "selector-exists" },
@@ -3058,13 +3123,13 @@ const Ia = {
3058
3123
  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.",
3059
3124
  prompt: "Instruct to remove aria-hidden='true' from the body element.",
3060
3125
  skipAriaHidden: !1
3061
- }, Ea = k(Ia), Ta = {
3126
+ }, Ea = k(Ta), Ca = {
3062
3127
  id: "aria-hidden-focus",
3063
3128
  wcag: ["4.1.2"],
3064
3129
  level: "A",
3065
3130
  description: "Elements with aria-hidden='true' must not contain focusable elements.",
3066
3131
  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.",
3067
- prompt: "Suggest adding tabindex='-1' to this focusable element, or moving it outside the aria-hidden region, or removing aria-hidden from the ancestor.",
3132
+ 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.",
3068
3133
  run(t) {
3069
3134
  const a = [];
3070
3135
  for (const e of t.querySelectorAll('[aria-hidden="true"]')) {
@@ -3073,13 +3138,19 @@ const Ia = {
3073
3138
  e.matches(ne) && i.push(e);
3074
3139
  for (const n of i)
3075
3140
  if (n instanceof HTMLElement) {
3076
- if (n.getAttribute("tabindex") === "-1" || n.disabled || n instanceof HTMLInputElement && n.type === "hidden" || !ka(n)) continue;
3141
+ const r = n.getAttribute("tabindex");
3142
+ if (r === "-1" || n.disabled || n instanceof HTMLInputElement && n.type === "hidden" || !Ia(n)) continue;
3143
+ const o = n.tagName.toLowerCase();
3144
+ let s;
3145
+ 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}>`;
3146
+ const l = n === e ? n : n.closest('[aria-hidden="true"]');
3077
3147
  a.push({
3078
3148
  ruleId: "aria-hidden-focus",
3079
- selector: d(n),
3080
- html: c(n),
3149
+ selector: m(n),
3150
+ html: u(n),
3081
3151
  impact: "serious",
3082
- message: "Focusable element is inside an aria-hidden region."
3152
+ message: "Focusable element is inside an aria-hidden region.",
3153
+ context: `Focusable because: ${s}. aria-hidden ancestor: ${l ? u(l) : "unknown"}`
3083
3154
  });
3084
3155
  }
3085
3156
  }
@@ -3102,8 +3173,8 @@ const Ia = {
3102
3173
  if ((e = r == null ? void 0 : r.getAttribute("alt")) != null && e.trim()) continue;
3103
3174
  a.push({
3104
3175
  ruleId: "aria-command-name",
3105
- selector: d(i),
3106
- html: c(i),
3176
+ selector: m(i),
3177
+ html: u(i),
3107
3178
  impact: "serious",
3108
3179
  message: "ARIA command has no accessible name."
3109
3180
  });
@@ -3111,7 +3182,7 @@ const Ia = {
3111
3182
  }
3112
3183
  return a;
3113
3184
  }
3114
- }, Ca = {
3185
+ }, qa = {
3115
3186
  id: "aria-input-field-name",
3116
3187
  wcag: ["4.1.2"],
3117
3188
  level: "A",
@@ -3124,15 +3195,15 @@ const Ia = {
3124
3195
  if (p(i) || i.matches("input, select, textarea")) continue;
3125
3196
  v(i) || a.push({
3126
3197
  ruleId: "aria-input-field-name",
3127
- selector: d(i),
3128
- html: c(i),
3198
+ selector: m(i),
3199
+ html: u(i),
3129
3200
  impact: "serious",
3130
3201
  message: "ARIA input field has no accessible name."
3131
3202
  });
3132
3203
  }
3133
3204
  return a;
3134
3205
  }
3135
- }, qa = {
3206
+ }, Ra = {
3136
3207
  id: "aria-toggle-field-name",
3137
3208
  wcag: ["4.1.2"],
3138
3209
  level: "A",
@@ -3145,15 +3216,15 @@ const Ia = {
3145
3216
  if (p(i) || i.matches('input[type="checkbox"], input[type="radio"]')) continue;
3146
3217
  v(i) || a.push({
3147
3218
  ruleId: "aria-toggle-field-name",
3148
- selector: d(i),
3149
- html: c(i),
3219
+ selector: m(i),
3220
+ html: u(i),
3150
3221
  impact: "serious",
3151
3222
  message: "ARIA toggle field has no accessible name."
3152
3223
  });
3153
3224
  }
3154
3225
  return a;
3155
3226
  }
3156
- }, Ra = {
3227
+ }, Na = {
3157
3228
  id: "aria-meter-name",
3158
3229
  wcag: ["4.1.2"],
3159
3230
  level: "A",
@@ -3166,15 +3237,15 @@ const Ia = {
3166
3237
  if (p(e)) continue;
3167
3238
  v(e) || a.push({
3168
3239
  ruleId: "aria-meter-name",
3169
- selector: d(e),
3170
- html: c(e),
3240
+ selector: m(e),
3241
+ html: u(e),
3171
3242
  impact: "serious",
3172
3243
  message: "Meter has no accessible name."
3173
3244
  });
3174
3245
  }
3175
3246
  return a;
3176
3247
  }
3177
- }, Na = {
3248
+ }, $a = {
3178
3249
  id: "aria-progressbar-name",
3179
3250
  wcag: ["4.1.2"],
3180
3251
  level: "A",
@@ -3187,8 +3258,8 @@ const Ia = {
3187
3258
  if (p(e)) continue;
3188
3259
  v(e) || a.push({
3189
3260
  ruleId: "aria-progressbar-name",
3190
- selector: d(e),
3191
- html: c(e),
3261
+ selector: m(e),
3262
+ html: u(e),
3192
3263
  impact: "serious",
3193
3264
  message: "Progressbar has no accessible name."
3194
3265
  });
@@ -3208,15 +3279,15 @@ const Ia = {
3208
3279
  if (p(e)) continue;
3209
3280
  v(e) || a.push({
3210
3281
  ruleId: "aria-dialog-name",
3211
- selector: d(e),
3212
- html: c(e),
3282
+ selector: m(e),
3283
+ html: u(e),
3213
3284
  impact: "serious",
3214
3285
  message: "Dialog has no accessible name."
3215
3286
  });
3216
3287
  }
3217
3288
  return a;
3218
3289
  }
3219
- }, $a = {
3290
+ }, Ha = {
3220
3291
  id: "aria-tooltip-name",
3221
3292
  wcag: ["4.1.2"],
3222
3293
  level: "A",
@@ -3229,15 +3300,15 @@ const Ia = {
3229
3300
  if (p(e)) continue;
3230
3301
  v(e) || a.push({
3231
3302
  ruleId: "aria-tooltip-name",
3232
- selector: d(e),
3233
- html: c(e),
3303
+ selector: m(e),
3304
+ html: u(e),
3234
3305
  impact: "serious",
3235
3306
  message: "Tooltip has no accessible name."
3236
3307
  });
3237
3308
  }
3238
3309
  return a;
3239
3310
  }
3240
- }, Ha = {
3311
+ }, Da = {
3241
3312
  id: "aria-treeitem-name",
3242
3313
  wcag: ["4.1.2"],
3243
3314
  level: "A",
@@ -3250,15 +3321,15 @@ const Ia = {
3250
3321
  if (p(e)) continue;
3251
3322
  v(e) || a.push({
3252
3323
  ruleId: "aria-treeitem-name",
3253
- selector: d(e),
3254
- html: c(e),
3324
+ selector: m(e),
3325
+ html: u(e),
3255
3326
  impact: "serious",
3256
3327
  message: "Treeitem has no accessible name."
3257
3328
  });
3258
3329
  }
3259
3330
  return a;
3260
3331
  }
3261
- }, Da = {
3332
+ }, Oa = {
3262
3333
  id: "aria-prohibited-attr",
3263
3334
  wcag: ["4.1.2"],
3264
3335
  level: "A",
@@ -3268,14 +3339,14 @@ const Ia = {
3268
3339
  run(t) {
3269
3340
  return z(t).prohibitedAttr;
3270
3341
  }
3271
- }, Oa = [
3342
+ }, Ba = [
3272
3343
  "a[href]",
3273
3344
  "button:not([disabled])",
3274
3345
  'input:not([disabled]):not([type="hidden"])',
3275
3346
  "select:not([disabled])",
3276
3347
  "textarea:not([disabled])",
3277
3348
  '[tabindex]:not([tabindex="-1"])'
3278
- ].join(", "), Wa = [
3349
+ ].join(", "), Fa = [
3279
3350
  "aria-atomic",
3280
3351
  "aria-busy",
3281
3352
  "aria-controls",
@@ -3292,15 +3363,15 @@ const Ia = {
3292
3363
  ];
3293
3364
  function re(t) {
3294
3365
  const a = [];
3295
- t.matches(Oa) && a.push("element is focusable");
3296
- for (const e of Wa)
3366
+ t.matches(Ba) && a.push("element is focusable");
3367
+ for (const e of Fa)
3297
3368
  if (t.hasAttribute(e)) {
3298
3369
  a.push(`has ${e}`);
3299
3370
  break;
3300
3371
  }
3301
3372
  return (t.hasAttribute("aria-label") || t.hasAttribute("aria-labelledby")) && a.push("has accessible name"), a;
3302
3373
  }
3303
- const Ba = {
3374
+ const Wa = {
3304
3375
  id: "presentation-role-conflict",
3305
3376
  wcag: ["4.1.2"],
3306
3377
  level: "A",
@@ -3314,8 +3385,8 @@ const Ba = {
3314
3385
  const i = re(e);
3315
3386
  i.length > 0 && a.push({
3316
3387
  ruleId: "presentation-role-conflict",
3317
- selector: d(e),
3318
- html: c(e),
3388
+ selector: m(e),
3389
+ html: u(e),
3319
3390
  impact: "serious",
3320
3391
  message: `Presentation role conflicts with: ${i.join(", ")}. The role will be ignored.`
3321
3392
  });
@@ -3325,8 +3396,8 @@ const Ba = {
3325
3396
  const i = re(e);
3326
3397
  i.length > 0 && a.push({
3327
3398
  ruleId: "presentation-role-conflict",
3328
- selector: d(e),
3329
- html: c(e),
3399
+ selector: m(e),
3400
+ html: u(e),
3330
3401
  impact: "serious",
3331
3402
  message: `Element with implicit presentation role (alt="") conflicts with: ${i.join(", ")}. The decorative role will be ignored.`
3332
3403
  });
@@ -3346,8 +3417,8 @@ const Ba = {
3346
3417
  if (p(e)) continue;
3347
3418
  v(e) || a.push({
3348
3419
  ruleId: "summary-name",
3349
- selector: d(e),
3350
- html: c(e),
3420
+ selector: m(e),
3421
+ html: u(e),
3351
3422
  impact: "serious",
3352
3423
  message: "<summary> element has no accessible name. Add descriptive text."
3353
3424
  });
@@ -3355,7 +3426,7 @@ const Ba = {
3355
3426
  return a;
3356
3427
  }
3357
3428
  };
3358
- function Fa(t) {
3429
+ function Pa(t) {
3359
3430
  var n, r;
3360
3431
  const a = [], e = t.getAttribute("href");
3361
3432
  e && a.push(`href: ${e}`);
@@ -3385,16 +3456,16 @@ const ja = {
3385
3456
  if (p(e)) continue;
3386
3457
  v(e) || a.push({
3387
3458
  ruleId: "link-name",
3388
- selector: d(e),
3389
- html: c(e),
3459
+ selector: m(e),
3460
+ html: u(e),
3390
3461
  impact: "serious",
3391
3462
  message: "Link has no discernible text.",
3392
- context: Fa(e)
3463
+ context: Pa(e)
3393
3464
  });
3394
3465
  }
3395
3466
  return a;
3396
3467
  }
3397
- }, Pa = {
3468
+ }, Va = {
3398
3469
  id: "skip-link",
3399
3470
  wcag: ["2.4.1"],
3400
3471
  level: "A",
@@ -3407,20 +3478,20 @@ const ja = {
3407
3478
  for (const i of e) {
3408
3479
  const n = i.getAttribute("href");
3409
3480
  if (!n || n === "#") continue;
3410
- const r = y(i).toLowerCase();
3481
+ const r = w(i).toLowerCase();
3411
3482
  if (!(r.includes("skip") || r.includes("jump") || r.includes("main content") || r.includes("navigation"))) continue;
3412
3483
  const s = n.slice(1);
3413
3484
  t.getElementById(s) || a.push({
3414
3485
  ruleId: "skip-link",
3415
- selector: d(i),
3416
- html: c(i),
3486
+ selector: m(i),
3487
+ html: u(i),
3417
3488
  impact: "moderate",
3418
3489
  message: `Skip link points to "#${s}" which does not exist on the page.`
3419
3490
  });
3420
3491
  }
3421
3492
  return a;
3422
3493
  }
3423
- }, Va = /* @__PURE__ */ new Set([
3494
+ }, za = /* @__PURE__ */ new Set([
3424
3495
  "block",
3425
3496
  "flex",
3426
3497
  "grid",
@@ -3428,23 +3499,23 @@ const ja = {
3428
3499
  "table-cell",
3429
3500
  "list-item",
3430
3501
  "flow-root"
3431
- ]), za = /* @__PURE__ */ new Set([
3502
+ ]), Ua = /* @__PURE__ */ new Set([
3432
3503
  "inline",
3433
3504
  "inline-block",
3434
3505
  "inline-flex",
3435
3506
  "inline-grid"
3436
3507
  ]);
3437
- function Ua(t) {
3508
+ function Ga(t) {
3438
3509
  let a = t.parentElement;
3439
3510
  for (; a; ) {
3440
3511
  const e = A(a).display;
3441
- if (Va.has(e) && Ga(a))
3442
- return a;
3512
+ if (za.has(e))
3513
+ return Xa(a) ? a : null;
3443
3514
  a = a.parentElement;
3444
3515
  }
3445
3516
  return null;
3446
3517
  }
3447
- function Ga(t) {
3518
+ function Xa(t) {
3448
3519
  const a = t.ownerDocument.createTreeWalker(
3449
3520
  t,
3450
3521
  NodeFilter.SHOW_TEXT
@@ -3464,7 +3535,7 @@ function Ga(t) {
3464
3535
  }
3465
3536
  return !1;
3466
3537
  }
3467
- function Xa(t, a) {
3538
+ function Ya(t, a) {
3468
3539
  const e = t.ownerDocument.createTreeWalker(
3469
3540
  t,
3470
3541
  NodeFilter.SHOW_TEXT
@@ -3485,7 +3556,7 @@ function Xa(t, a) {
3485
3556
  }
3486
3557
  return null;
3487
3558
  }
3488
- function Ya(t, a) {
3559
+ function Ka(t, a) {
3489
3560
  const e = t.textDecorationLine || t.textDecoration || "", i = a.textDecorationLine || a.textDecoration || "";
3490
3561
  if ((e.includes("underline") || e.includes("line-through")) && e !== i)
3491
3562
  return !0;
@@ -3495,14 +3566,14 @@ function Ya(t, a) {
3495
3566
  const o = parseFloat(t.outlineWidth) || 0, s = t.outlineStyle || "";
3496
3567
  if (o > 0 && s !== "none")
3497
3568
  return !0;
3498
- const u = t.backgroundImage || "";
3499
- if (u && u !== "none" && u !== "initial")
3569
+ const l = t.backgroundImage || "";
3570
+ if (l && l !== "none" && l !== "initial")
3500
3571
  return !0;
3501
- const h = oe(t.fontWeight), l = oe(a.fontWeight);
3502
- if (Math.abs(h - l) >= 300 || t.fontStyle !== a.fontStyle)
3572
+ const h = oe(t.fontWeight), c = oe(a.fontWeight);
3573
+ if (Math.abs(h - c) >= 300 || t.fontStyle !== a.fontStyle)
3503
3574
  return !0;
3504
- const m = parseFloat(t.fontSize) || 16, g = parseFloat(a.fontSize) || 16;
3505
- return g > 0 && m / g >= 1.2;
3575
+ const d = parseFloat(t.fontSize) || 16, g = parseFloat(a.fontSize) || 16;
3576
+ return g > 0 && d / g >= 1.2;
3506
3577
  }
3507
3578
  function oe(t) {
3508
3579
  return t === "bold" ? 700 : t === "normal" ? 400 : parseInt(t) || 400;
@@ -3510,7 +3581,7 @@ function oe(t) {
3510
3581
  function se(t, a, e) {
3511
3582
  return "#" + [t, a, e].map((i) => i.toString(16).padStart(2, "0")).join("");
3512
3583
  }
3513
- const Ka = {
3584
+ const Ja = {
3514
3585
  id: "link-in-text-block",
3515
3586
  wcag: ["1.4.1"],
3516
3587
  level: "A",
@@ -3520,22 +3591,22 @@ const Ka = {
3520
3591
  run(t) {
3521
3592
  const a = [];
3522
3593
  for (const e of t.querySelectorAll("a[href]")) {
3523
- if (p(e) || !y(e).trim()) continue;
3594
+ if (p(e) || !w(e).trim() || e.closest('nav, [role="navigation"], [role="banner"], [role="contentinfo"]')) continue;
3524
3595
  const i = A(e), n = i.display || "inline";
3525
- if (!za.has(n)) continue;
3526
- const r = Ua(e);
3596
+ if (!Ua.has(n)) continue;
3597
+ const r = Ga(e);
3527
3598
  if (!r) continue;
3528
3599
  const o = A(r);
3529
- if (Ya(i, o)) continue;
3530
- const s = R(i.color), u = Xa(r);
3531
- if (!s || !u) continue;
3532
- const h = q(...s), l = q(...u), m = de(h, l);
3533
- if (m >= 3) continue;
3534
- const g = se(...s), b = se(...u), f = `link color: ${g} rgb(${s.join(", ")}), surrounding text: ${b} rgb(${u.join(", ")}), ratio: ${m.toFixed(2)}:1`;
3600
+ if (Ka(i, o)) continue;
3601
+ const s = R(i.color), l = Ya(r);
3602
+ if (!s || !l) continue;
3603
+ const h = q(...s), c = q(...l), d = de(h, c);
3604
+ if (d >= 3) continue;
3605
+ const g = se(...s), b = se(...l), f = `link color: ${g} rgb(${s.join(", ")}), surrounding text: ${b} rgb(${l.join(", ")}), ratio: ${d.toFixed(2)}:1`;
3535
3606
  a.push({
3536
3607
  ruleId: "link-in-text-block",
3537
- selector: d(e),
3538
- html: c(e),
3608
+ selector: m(e),
3609
+ html: u(e),
3539
3610
  impact: "serious",
3540
3611
  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.",
3541
3612
  context: f
@@ -3549,36 +3620,45 @@ const Ka = {
3549
3620
  level: "A",
3550
3621
  description: "The <html> element must have a lang attribute.",
3551
3622
  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).",
3552
- prompt: "Determine the page's primary language and suggest the appropriate lang value.",
3623
+ 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="...">.`,
3553
3624
  run(t) {
3554
- var e;
3625
+ var e, i;
3555
3626
  const a = t.documentElement;
3556
3627
  if (a.tagName.toLowerCase() !== "html") return [];
3557
3628
  if (!t.doctype && t.body) {
3558
- const i = t.body.children;
3559
- if (i.length > 0 && Array.from(i).every(
3560
- (n) => n.tagName.toLowerCase() === "svg" || n.tagName.toLowerCase() === "math"
3629
+ const n = t.body.children;
3630
+ if (n.length > 0 && Array.from(n).every(
3631
+ (r) => r.tagName.toLowerCase() === "svg" || r.tagName.toLowerCase() === "math"
3561
3632
  )) return [];
3562
3633
  }
3563
- return (e = a.getAttribute("lang")) != null && e.trim() ? [] : [{
3564
- ruleId: "html-has-lang",
3565
- selector: d(a),
3566
- html: c(a),
3567
- impact: "serious",
3568
- message: "<html> element missing lang attribute."
3569
- }];
3634
+ if (!((e = a.getAttribute("lang")) != null && e.trim())) {
3635
+ let n;
3636
+ if (t.body) {
3637
+ const r = ((i = t.body.textContent) == null ? void 0 : i.trim().replace(/\s+/g, " ")) || "";
3638
+ r && (n = r.slice(0, 200));
3639
+ }
3640
+ return [{
3641
+ ruleId: "html-has-lang",
3642
+ selector: m(a),
3643
+ html: u(a),
3644
+ impact: "serious",
3645
+ message: "<html> element missing lang attribute.",
3646
+ context: n ? `Page text sample: "${n}"` : void 0
3647
+ }];
3648
+ }
3649
+ return [];
3570
3650
  }
3571
- }, Ja = new Set(
3651
+ }, Za = new Set(
3572
3652
  "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(" ")
3573
- ), Za = new Set(
3653
+ ), ei = new Set(
3574
3654
  "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(" ")
3575
- ), ei = /^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i;
3576
- function me(t) {
3577
- if (!ei.test(t)) return !1;
3655
+ ), ti = /^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i;
3656
+ function he(t) {
3657
+ if (!ti.test(t)) return !1;
3578
3658
  const a = t.split("-")[0].toLowerCase();
3579
- return a.length === 2 ? Ja.has(a) : a.length === 3 ? !Za.has(a) : !1;
3659
+ return a.length === 2 ? Za.has(a) : a.length === 3 ? !ei.has(a) : !1;
3580
3660
  }
3581
- const ti = {
3661
+ const ai = {
3582
3662
  id: "html-lang-valid",
3583
3663
  wcag: ["3.1.1"],
3584
3664
  level: "A",
@@ -3588,10 +3668,10 @@ const ti = {
3588
3668
  run(t) {
3589
3669
  var e;
3590
3670
  const a = (e = t.documentElement.getAttribute("lang")) == null ? void 0 : e.trim();
3591
- return a && !me(a) ? [{
3671
+ return a && !he(a) ? [{
3592
3672
  ruleId: "html-lang-valid",
3593
3673
  selector: "html",
3594
- html: c(t.documentElement),
3674
+ html: u(t.documentElement),
3595
3675
  impact: "serious",
3596
3676
  message: `Invalid lang attribute value "${a}".`
3597
3677
  }] : [];
@@ -3629,7 +3709,7 @@ function le(t) {
3629
3709
  }
3630
3710
  return !1;
3631
3711
  }
3632
- const ai = {
3712
+ const ii = {
3633
3713
  id: "valid-lang",
3634
3714
  wcag: ["3.1.2"],
3635
3715
  level: "AA",
@@ -3644,24 +3724,24 @@ const ai = {
3644
3724
  if (i && !n) {
3645
3725
  le(e) && a.push({
3646
3726
  ruleId: "valid-lang",
3647
- selector: d(e),
3648
- html: c(e),
3727
+ selector: m(e),
3728
+ html: u(e),
3649
3729
  impact: "serious",
3650
3730
  message: "Empty lang attribute value."
3651
3731
  });
3652
3732
  continue;
3653
3733
  }
3654
- n && le(e) && (me(n) || a.push({
3734
+ n && le(e) && (he(n) || a.push({
3655
3735
  ruleId: "valid-lang",
3656
- selector: d(e),
3657
- html: c(e),
3736
+ selector: m(e),
3737
+ html: u(e),
3658
3738
  impact: "serious",
3659
3739
  message: `Invalid lang attribute value "${n}".`
3660
3740
  }));
3661
3741
  }
3662
3742
  return a;
3663
3743
  }
3664
- }, ii = {
3744
+ }, ni = {
3665
3745
  id: "html-xml-lang-mismatch",
3666
3746
  wcag: ["3.1.1"],
3667
3747
  level: "A",
@@ -3677,14 +3757,14 @@ const ai = {
3677
3757
  return [{
3678
3758
  ruleId: "html-xml-lang-mismatch",
3679
3759
  selector: "html",
3680
- html: c(a),
3760
+ html: u(a),
3681
3761
  impact: "moderate",
3682
3762
  message: `lang="${e}" and xml:lang="${i}" do not match.`
3683
3763
  }];
3684
3764
  }
3685
3765
  return [];
3686
3766
  }
3687
- }, ni = {
3767
+ }, ri = {
3688
3768
  id: "td-headers-attr",
3689
3769
  wcag: ["1.3.1"],
3690
3770
  level: "A",
@@ -3702,8 +3782,8 @@ const ai = {
3702
3782
  if (o === n) {
3703
3783
  a.push({
3704
3784
  ruleId: "td-headers-attr",
3705
- selector: d(e),
3706
- html: c(e),
3785
+ selector: m(e),
3786
+ html: u(e),
3707
3787
  impact: "serious",
3708
3788
  message: `Headers attribute references the cell itself ("${o}").`
3709
3789
  });
@@ -3712,8 +3792,8 @@ const ai = {
3712
3792
  if (!i.querySelector(`th#${CSS.escape(o)}, td#${CSS.escape(o)}`)) {
3713
3793
  a.push({
3714
3794
  ruleId: "td-headers-attr",
3715
- selector: d(e),
3716
- html: c(e),
3795
+ selector: m(e),
3796
+ html: u(e),
3717
3797
  impact: "serious",
3718
3798
  message: `Headers attribute references non-existent ID "${o}".`
3719
3799
  });
@@ -3723,7 +3803,7 @@ const ai = {
3723
3803
  }
3724
3804
  return a;
3725
3805
  }
3726
- }, ri = {
3806
+ }, oi = {
3727
3807
  id: "th-has-data-cells",
3728
3808
  wcag: ["1.3.1"],
3729
3809
  level: "A",
@@ -3737,15 +3817,15 @@ const ai = {
3737
3817
  const i = e.querySelectorAll("th"), n = e.querySelectorAll("td");
3738
3818
  i.length > 0 && n.length === 0 && a.push({
3739
3819
  ruleId: "th-has-data-cells",
3740
- selector: d(e),
3741
- html: c(e),
3820
+ selector: m(e),
3821
+ html: u(e),
3742
3822
  impact: "serious",
3743
3823
  message: "Table has header cells but no data cells."
3744
3824
  });
3745
3825
  }
3746
3826
  return a;
3747
3827
  }
3748
- }, oi = {
3828
+ }, si = {
3749
3829
  id: "td-has-header",
3750
3830
  wcag: ["1.3.1"],
3751
3831
  level: "A",
@@ -3759,36 +3839,36 @@ const ai = {
3759
3839
  if (p(n) || n.getAttribute("role") === "presentation" || n.getAttribute("role") === "none") continue;
3760
3840
  const r = n.querySelectorAll("tr"), o = r.length;
3761
3841
  let s = 0;
3762
- for (const m of r) {
3763
- const g = m.querySelectorAll("td, th");
3842
+ for (const d of r) {
3843
+ const g = d.querySelectorAll("td, th");
3764
3844
  let b = 0;
3765
3845
  for (const f of g)
3766
3846
  b += parseInt(f.getAttribute("colspan") || "1", 10);
3767
3847
  s = Math.max(s, b);
3768
3848
  }
3769
3849
  if (o <= 3 && s <= 3) continue;
3770
- const u = n.querySelector("th") !== null, h = n.querySelector("th[scope]") !== null, l = n.querySelector("td[headers]") !== null;
3771
- if (u)
3772
- for (const m of n.querySelectorAll("td")) {
3773
- if (p(m) || m.hasAttribute("headers")) continue;
3774
- const g = m.closest("tr");
3850
+ const l = n.querySelector("th") !== null, h = n.querySelector("th[scope]") !== null, c = n.querySelector("td[headers]") !== null;
3851
+ if (l)
3852
+ for (const d of n.querySelectorAll("td")) {
3853
+ if (p(d) || d.hasAttribute("headers")) continue;
3854
+ const g = d.closest("tr");
3775
3855
  if (!g) continue;
3776
- const b = g.querySelector("th") !== null, f = Array.from(g.children).indexOf(m);
3777
- let w = !1;
3856
+ const b = g.querySelector("th") !== null, f = Array.from(g.children).indexOf(d);
3857
+ let y = !1;
3778
3858
  const I = n.querySelector("thead");
3779
3859
  if (I) {
3780
- const S = I.querySelector("tr");
3781
- S && ((e = S.querySelectorAll("th, td")[f]) == null ? void 0 : e.tagName.toLowerCase()) === "th" && (w = !0);
3860
+ const x = I.querySelector("tr");
3861
+ x && ((e = x.querySelectorAll("th, td")[f]) == null ? void 0 : e.tagName.toLowerCase()) === "th" && (y = !0);
3782
3862
  }
3783
- if (!w) {
3784
- const S = n.querySelector("tbody > tr, tr");
3785
- S && ((i = S.querySelectorAll("th, td")[f]) == null ? void 0 : i.tagName.toLowerCase()) === "th" && (w = !0);
3863
+ if (!y) {
3864
+ const x = n.querySelector("tbody > tr, tr");
3865
+ x && ((i = x.querySelectorAll("th, td")[f]) == null ? void 0 : i.tagName.toLowerCase()) === "th" && (y = !0);
3786
3866
  }
3787
- if (!b && !w && !h && !l) {
3867
+ if (!b && !y && !h && !c) {
3788
3868
  a.push({
3789
3869
  ruleId: "td-has-header",
3790
- selector: d(m),
3791
- html: c(m),
3870
+ selector: m(d),
3871
+ html: u(d),
3792
3872
  impact: "serious",
3793
3873
  message: "Data cell has no associated header. Add th elements with scope, or headers attribute."
3794
3874
  });
@@ -3798,7 +3878,7 @@ const ai = {
3798
3878
  }
3799
3879
  return a;
3800
3880
  }
3801
- }, si = {
3881
+ }, li = {
3802
3882
  id: "scope-attr-valid",
3803
3883
  wcag: ["1.3.1"],
3804
3884
  level: "A",
@@ -3813,15 +3893,15 @@ const ai = {
3813
3893
  const r = (i = n.getAttribute("scope")) == null ? void 0 : i.toLowerCase();
3814
3894
  r && !e.has(r) && a.push({
3815
3895
  ruleId: "scope-attr-valid",
3816
- selector: d(n),
3817
- html: c(n),
3896
+ selector: m(n),
3897
+ html: u(n),
3818
3898
  impact: "moderate",
3819
3899
  message: `Invalid scope value "${r}". Use row, col, rowgroup, or colgroup.`
3820
3900
  });
3821
3901
  }
3822
3902
  return a;
3823
3903
  }
3824
- }, li = {
3904
+ }, ci = {
3825
3905
  id: "empty-table-header",
3826
3906
  wcag: [],
3827
3907
  level: "A",
@@ -3836,15 +3916,15 @@ const ai = {
3836
3916
  const i = e.closest("table");
3837
3917
  (i == null ? void 0 : i.getAttribute("role")) === "presentation" || (i == null ? void 0 : i.getAttribute("role")) === "none" || v(e) || a.push({
3838
3918
  ruleId: "empty-table-header",
3839
- selector: d(e),
3840
- html: c(e),
3919
+ selector: m(e),
3920
+ html: u(e),
3841
3921
  impact: "minor",
3842
3922
  message: "Table header cell is empty. Add text or use aria-label."
3843
3923
  });
3844
3924
  }
3845
3925
  return a;
3846
3926
  }
3847
- }, D = ["aria-labelledby", "aria-describedby", "aria-controls", "aria-owns", "aria-flowto"], ci = {
3927
+ }, D = ["aria-labelledby", "aria-describedby", "aria-controls", "aria-owns", "aria-flowto"], ui = {
3848
3928
  id: "duplicate-id-aria",
3849
3929
  wcag: ["4.1.2"],
3850
3930
  level: "A",
@@ -3868,31 +3948,31 @@ const ai = {
3868
3948
  for (const [n, r] of i) {
3869
3949
  if (r <= 1) continue;
3870
3950
  const o = t.querySelectorAll(`#${CSS.escape(n)}`), s = t.querySelector(
3871
- D.map((l) => `[${l}~="${CSS.escape(n)}"]`).join(", ")
3872
- ), u = t.querySelector(`label[for="${CSS.escape(n)}"]`);
3951
+ D.map((c) => `[${c}~="${CSS.escape(n)}"]`).join(", ")
3952
+ ), l = t.querySelector(`label[for="${CSS.escape(n)}"]`);
3873
3953
  let h;
3874
3954
  if (s) {
3875
- const l = D.find(
3876
- (m) => {
3955
+ const c = D.find(
3956
+ (d) => {
3877
3957
  var g;
3878
- return (g = s.getAttribute(m)) == null ? void 0 : g.split(/\s+/).includes(n);
3958
+ return (g = s.getAttribute(d)) == null ? void 0 : g.split(/\s+/).includes(n);
3879
3959
  }
3880
3960
  );
3881
- l && (h = l);
3882
- } else u && (h = "label[for]");
3961
+ c && (h = c);
3962
+ } else l && (h = "label[for]");
3883
3963
  a.push({
3884
3964
  ruleId: "duplicate-id-aria",
3885
- selector: d(o[1]),
3886
- html: c(o[1]),
3965
+ selector: m(o[1]),
3966
+ html: u(o[1]),
3887
3967
  impact: "critical",
3888
3968
  message: `Duplicate ID "${n}" referenced by ${h ?? "an accessibility attribute"}.`,
3889
- context: `First element: ${c(o[0])}${h ? `
3969
+ context: `First element: ${u(o[0])}${h ? `
3890
3970
  Referenced by: ${h}` : ""}`
3891
3971
  });
3892
3972
  }
3893
3973
  return a;
3894
3974
  }
3895
- }, ui = {
3975
+ }, di = {
3896
3976
  id: "video-caption",
3897
3977
  wcag: ["1.2.2"],
3898
3978
  level: "A",
@@ -3905,15 +3985,15 @@ Referenced by: ${h}` : ""}`
3905
3985
  if (p(e) || e.hasAttribute("muted") || e.hasAttribute("autoplay")) continue;
3906
3986
  e.querySelector('track[kind="captions"], track[kind="subtitles"]') || a.push({
3907
3987
  ruleId: "video-caption",
3908
- selector: d(e),
3909
- html: c(e),
3988
+ selector: m(e),
3989
+ html: u(e),
3910
3990
  impact: "critical",
3911
3991
  message: "Video element has no captions track."
3912
3992
  });
3913
3993
  }
3914
3994
  return a;
3915
3995
  }
3916
- }, di = {
3996
+ }, mi = {
3917
3997
  id: "audio-caption",
3918
3998
  wcag: ["1.2.1"],
3919
3999
  level: "A",
@@ -3927,15 +4007,15 @@ Referenced by: ${h}` : ""}`
3927
4007
  const n = e.parentElement;
3928
4008
  n && n.querySelector('a[href*="transcript"], a[href*="text"]') || a.push({
3929
4009
  ruleId: "audio-caption",
3930
- selector: d(e),
3931
- html: c(e),
4010
+ selector: m(e),
4011
+ html: u(e),
3932
4012
  impact: "critical",
3933
4013
  message: "Audio element has no transcript or text alternative. Add a transcript or track element."
3934
4014
  });
3935
4015
  }
3936
4016
  return a;
3937
4017
  }
3938
- }, mi = /* @__PURE__ */ new Set([
4018
+ }, hi = /* @__PURE__ */ new Set([
3939
4019
  "SCRIPT",
3940
4020
  "STYLE",
3941
4021
  "NOSCRIPT",
@@ -3954,10 +4034,10 @@ Referenced by: ${h}` : ""}`
3954
4034
  function ce([t, a, e]) {
3955
4035
  return "#" + [t, a, e].map((i) => i.toString(16).padStart(2, "0")).join("");
3956
4036
  }
3957
- function hi(t) {
4037
+ function pi(t) {
3958
4038
  return t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement || t instanceof HTMLSelectElement || t instanceof HTMLButtonElement ? t.disabled : !!(t.closest("fieldset[disabled]") || t.getAttribute("aria-disabled") === "true");
3959
4039
  }
3960
- function pi(t, a) {
4040
+ function gi(t, a) {
3961
4041
  if (t.tagName !== "LABEL") return !1;
3962
4042
  const e = t, i = e.htmlFor;
3963
4043
  if (i) {
@@ -3969,7 +4049,7 @@ function pi(t, a) {
3969
4049
  const r = e.id;
3970
4050
  return !!(r && a.querySelector(`[aria-labelledby~="${r}"][aria-disabled="true"]`));
3971
4051
  }
3972
- function gi(t) {
4052
+ function bi(t) {
3973
4053
  const a = t.clip;
3974
4054
  if (a && a.startsWith("rect(")) {
3975
4055
  const i = a.match(/[\d.]+/g);
@@ -3983,17 +4063,17 @@ function gi(t) {
3983
4063
  }
3984
4064
  return !1;
3985
4065
  }
3986
- function bi(t) {
4066
+ function fi(t) {
3987
4067
  if (p(t)) return !0;
3988
4068
  let a = t;
3989
4069
  for (; a; ) {
3990
4070
  const e = A(a);
3991
- if (e.display === "none" || e.visibility === "hidden" || gi(e)) return !0;
4071
+ if (e.display === "none" || e.visibility === "hidden" || bi(e)) return !0;
3992
4072
  a = a.parentElement;
3993
4073
  }
3994
4074
  return !1;
3995
4075
  }
3996
- const fi = {
4076
+ const vi = {
3997
4077
  id: "color-contrast",
3998
4078
  wcag: ["1.4.3"],
3999
4079
  level: "AA",
@@ -4008,140 +4088,140 @@ const fi = {
4008
4088
  for (; r = i.nextNode(); ) {
4009
4089
  if (!r.textContent || !r.textContent.trim()) continue;
4010
4090
  const o = r.parentElement;
4011
- if (!o || n.has(o) || (n.add(o), mi.has(o.tagName)) || hi(o) || pi(o, t) || bi(o)) continue;
4091
+ if (!o || n.has(o) || (n.add(o), hi.has(o.tagName)) || pi(o) || gi(o, t) || fi(o)) continue;
4012
4092
  const s = A(o);
4013
4093
  if (parseFloat(s.opacity) === 0) continue;
4014
- const u = s.textShadow;
4015
- if (u && u !== "none" && u !== "initial") continue;
4094
+ const l = s.textShadow;
4095
+ if (l && l !== "none" && l !== "initial") continue;
4016
4096
  const h = R(s.color);
4017
4097
  if (!h) continue;
4018
- const l = s.color.match(/rgba\(.+?,\s*([\d.]+)\s*\)/) || s.color.match(/rgba?\(.+?\/\s*([\d.]+%?)\s*\)/);
4019
- if (l && (l[1].endsWith("%") ? parseFloat(l[1]) / 100 : parseFloat(l[1])) === 0 || We(o)) continue;
4020
- const m = He(o);
4021
- if (!m) continue;
4022
- const g = q(h[0], h[1], h[2]), b = q(m[0], m[1], m[2]), f = de(g, b), w = Fe(o) ? 3 : 4.5;
4023
- if (f < w) {
4024
- const I = Math.round(f * 100) / 100, S = ce(h), M = ce(m);
4098
+ const c = s.color.match(/rgba\(.+?,\s*([\d.]+)\s*\)/) || s.color.match(/rgba?\(.+?\/\s*([\d.]+%?)\s*\)/);
4099
+ if (c && (c[1].endsWith("%") ? parseFloat(c[1]) / 100 : parseFloat(c[1])) === 0 || Fe(o)) continue;
4100
+ const d = De(o);
4101
+ if (!d) continue;
4102
+ const g = q(h[0], h[1], h[2]), b = q(d[0], d[1], d[2]), f = de(g, b), y = Pe(o) ? 3 : 4.5;
4103
+ if (f < y) {
4104
+ const I = Math.round(f * 100) / 100, x = ce(h), $ = ce(d);
4025
4105
  a.push({
4026
4106
  ruleId: "color-contrast",
4027
- selector: d(o),
4028
- html: c(o),
4107
+ selector: m(o),
4108
+ html: u(o),
4029
4109
  impact: "serious",
4030
- message: `Insufficient color contrast ratio of ${I}:1 (required ${w}:1).`,
4031
- context: `foreground: ${S} rgb(${h.join(", ")}), background: ${M} rgb(${m.join(", ")}), ratio: ${I}:1, required: ${w}:1`
4110
+ message: `Insufficient color contrast ratio of ${I}:1 (required ${y}:1).`,
4111
+ context: `foreground: ${x} rgb(${h.join(", ")}), background: ${$} rgb(${d.join(", ")}), ratio: ${I}:1, required: ${y}:1`
4032
4112
  });
4033
4113
  }
4034
4114
  }
4035
4115
  return a;
4036
4116
  }
4037
- }, he = [
4117
+ }, pe = [
4038
4118
  // Document Structure
4039
- Yt,
4040
4119
  Kt,
4041
- Qt,
4042
4120
  Jt,
4121
+ Qt,
4043
4122
  Zt,
4044
- ta,
4123
+ ea,
4045
4124
  aa,
4046
- na,
4047
- oa,
4125
+ ia,
4126
+ ra,
4127
+ sa,
4048
4128
  // Images
4049
4129
  je,
4050
- Ve,
4051
4130
  ze,
4052
4131
  Ue,
4053
- Xe,
4132
+ Ge,
4054
4133
  Ye,
4134
+ Ke,
4055
4135
  Qe,
4056
- Je,
4057
- at,
4136
+ Ze,
4137
+ it,
4058
4138
  // Forms
4059
- st,
4060
4139
  lt,
4061
4140
  ct,
4062
4141
  ut,
4063
- ft,
4142
+ dt,
4064
4143
  vt,
4144
+ yt,
4065
4145
  wt,
4066
4146
  // Keyboard
4067
- At,
4068
4147
  xt,
4069
- qt,
4148
+ kt,
4070
4149
  Rt,
4071
4150
  Nt,
4151
+ $t,
4072
4152
  // Structure
4073
4153
  Mt,
4074
- ea,
4075
- sa,
4076
- $t,
4154
+ ta,
4155
+ la,
4077
4156
  Ht,
4078
4157
  Dt,
4079
4158
  Ot,
4080
- Wt,
4081
4159
  Bt,
4082
- _t,
4083
4160
  Ft,
4084
- jt,
4161
+ Wt,
4162
+ _t,
4085
4163
  Pt,
4086
- zt,
4164
+ jt,
4165
+ Vt,
4087
4166
  Ut,
4088
- Xt,
4167
+ Gt,
4168
+ Yt,
4089
4169
  // ARIA
4090
- la,
4091
4170
  ca,
4092
4171
  ua,
4093
- ma,
4094
- fa,
4095
- ya,
4172
+ da,
4173
+ ha,
4174
+ va,
4175
+ Aa,
4096
4176
  Sa,
4097
- xa,
4177
+ ka,
4098
4178
  Ea,
4099
- Ta,
4100
- La,
4101
4179
  Ca,
4180
+ La,
4102
4181
  qa,
4103
4182
  Ra,
4104
4183
  Na,
4105
- Ma,
4106
4184
  $a,
4185
+ Ma,
4107
4186
  Ha,
4108
4187
  Da,
4109
- Ba,
4110
- pa,
4188
+ Oa,
4189
+ Wa,
4190
+ ga,
4111
4191
  _a,
4112
4192
  // Links
4113
4193
  ja,
4114
- Pa,
4115
- Ka,
4194
+ Va,
4195
+ Ja,
4116
4196
  // Language
4117
4197
  Qa,
4118
- ti,
4119
4198
  ai,
4120
4199
  ii,
4121
- // Tables
4122
4200
  ni,
4201
+ // Tables
4123
4202
  ri,
4124
4203
  oi,
4125
4204
  si,
4126
4205
  li,
4127
- // Parsing
4128
4206
  ci,
4129
- // Media
4207
+ // Parsing
4130
4208
  ui,
4209
+ // Media
4131
4210
  di,
4211
+ mi,
4132
4212
  // Color
4133
- fi
4213
+ vi
4134
4214
  ];
4135
- let U = [], pe = /* @__PURE__ */ new Set();
4136
- function Ai(t) {
4137
- t.additionalRules && (U = t.additionalRules), t.disabledRules && (pe = new Set(t.disabledRules));
4215
+ let U = [], ge = /* @__PURE__ */ new Set();
4216
+ function xi(t) {
4217
+ t.additionalRules && (U = t.additionalRules), t.disabledRules && (ge = new Set(t.disabledRules));
4138
4218
  }
4139
- function ge() {
4140
- return he.filter((a) => !pe.has(a.id)).concat(U);
4219
+ function be() {
4220
+ return pe.filter((a) => !ge.has(a.id)).concat(U);
4141
4221
  }
4142
4222
  function Si(t) {
4143
- be();
4144
- const a = ge(), e = [];
4223
+ fe();
4224
+ const a = be(), e = [];
4145
4225
  let i = 0;
4146
4226
  return {
4147
4227
  processChunk(n) {
@@ -4160,13 +4240,13 @@ function Si(t) {
4160
4240
  }
4161
4241
  };
4162
4242
  }
4163
- function be() {
4164
- Se(), fe(), ve(), $e(), Me(), ke();
4243
+ function fe() {
4244
+ Se(), ve(), ye(), He(), Me(), Ie();
4165
4245
  }
4166
- function xi(t) {
4246
+ function ki(t) {
4167
4247
  var i;
4168
- be();
4169
- const a = ge(), e = [];
4248
+ fe();
4249
+ const a = be(), e = [];
4170
4250
  for (const n of a)
4171
4251
  try {
4172
4252
  e.push(...n.run(t));
@@ -4179,32 +4259,32 @@ function xi(t) {
4179
4259
  ruleCount: a.length
4180
4260
  };
4181
4261
  }
4182
- const vi = new Map(he.map((t) => [t.id, t]));
4183
- function ki(t) {
4184
- const a = vi.get(t);
4262
+ const yi = new Map(pe.map((t) => [t.id, t]));
4263
+ function Ii(t) {
4264
+ const a = yi.get(t);
4185
4265
  return a || U.find((e) => e.id === t);
4186
4266
  }
4187
4267
  export {
4188
- be as clearAllCaches,
4268
+ fe as clearAllCaches,
4189
4269
  Me as clearAriaAttrAuditCache,
4190
4270
  Se as clearAriaHiddenCache,
4191
- $e as clearColorCaches,
4192
- fe as clearComputedRoleCache,
4271
+ He as clearColorCaches,
4272
+ ve as clearComputedRoleCache,
4193
4273
  k as compileDeclarativeRule,
4194
- Ai as configureRules,
4274
+ xi as configureRules,
4195
4275
  Si as createChunkedAudit,
4196
4276
  v as getAccessibleName,
4197
- y as getAccessibleTextContent,
4198
- ge as getActiveRules,
4199
- C as getComputedRole,
4200
- c as getHtmlSnippet,
4277
+ w as getAccessibleTextContent,
4278
+ be as getActiveRules,
4279
+ L as getComputedRole,
4280
+ u as getHtmlSnippet,
4201
4281
  ue as getImplicitRole,
4202
- ki as getRuleById,
4203
- d as getSelector,
4282
+ Ii as getRuleById,
4283
+ m as getSelector,
4204
4284
  p as isAriaHidden,
4205
- Ae as isValidRole,
4285
+ xe as isValidRole,
4206
4286
  wi as querySelectorShadowAware,
4207
- he as rules,
4208
- xi as runAudit,
4209
- yi as validateDeclarativeRule
4287
+ pe as rules,
4288
+ ki as runAudit,
4289
+ Ai as validateDeclarativeRule
4210
4290
  };