@accesslint/core 0.3.5 → 0.3.7
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.cjs +5 -5
- package/dist/index.iife.js +5 -5
- package/dist/index.js +661 -586
- package/dist/rules/aria/aria-allowed-attr.d.ts.map +1 -1
- package/dist/rules/aria/aria-hidden-rules.d.ts.map +1 -1
- package/dist/rules/forms/label.d.ts.map +1 -1
- package/dist/rules/images/image-redundant-alt.d.ts.map +1 -1
- package/dist/rules/language/html-has-lang.d.ts.map +1 -1
- package/dist/rules/links/link-rules.d.ts.map +1 -1
- package/dist/rules/structure/document-rules.d.ts.map +1 -1
- package/dist/rules/structure/frame-rules.d.ts.map +1 -1
- package/dist/rules/structure/heading-rules.d.ts.map +1 -1
- package/dist/rules/structure/meta-rules.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -117,69 +117,69 @@ function ue(t) {
|
|
|
117
117
|
return null;
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
|
-
function
|
|
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
|
|
128
|
-
function
|
|
129
|
-
|
|
127
|
+
let F = /* @__PURE__ */ new WeakMap();
|
|
128
|
+
function ye() {
|
|
129
|
+
F = /* @__PURE__ */ new WeakMap();
|
|
130
130
|
}
|
|
131
131
|
function v(t) {
|
|
132
|
-
const a =
|
|
132
|
+
const a = F.get(t);
|
|
133
133
|
if (a !== void 0) return a;
|
|
134
|
-
const e =
|
|
135
|
-
return
|
|
134
|
+
const e = we(t);
|
|
135
|
+
return F.set(t, e), e;
|
|
136
136
|
}
|
|
137
|
-
function
|
|
138
|
-
var r, o, s,
|
|
137
|
+
function we(t) {
|
|
138
|
+
var r, o, s, l, h;
|
|
139
139
|
const a = t.getAttribute("aria-labelledby");
|
|
140
140
|
if (a) {
|
|
141
|
-
const
|
|
142
|
-
const g = t.ownerDocument.getElementById(
|
|
143
|
-
return g ?
|
|
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 (
|
|
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 ?
|
|
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
|
|
155
|
-
if (
|
|
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
|
|
161
|
-
if (
|
|
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
|
|
166
|
-
if (
|
|
167
|
-
const
|
|
168
|
-
if (
|
|
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
|
|
173
|
-
if (
|
|
174
|
-
const
|
|
175
|
-
if (
|
|
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
|
|
180
|
-
if (
|
|
179
|
+
const c = w(t).trim();
|
|
180
|
+
if (c) return c;
|
|
181
181
|
}
|
|
182
|
-
return t instanceof HTMLImageElement || t instanceof HTMLAreaElement ? ((
|
|
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
184
|
const Ae = /* @__PURE__ */ new Set([
|
|
185
185
|
"alert",
|
|
@@ -265,12 +265,12 @@ const Ae = /* @__PURE__ */ new Set([
|
|
|
265
265
|
"treegrid",
|
|
266
266
|
"treeitem"
|
|
267
267
|
]);
|
|
268
|
-
function
|
|
268
|
+
function xe(t) {
|
|
269
269
|
const a = t.trim().toLowerCase().replace(/[\u201C\u201D\u2018\u2019\u00AB\u00BB]/g, "");
|
|
270
270
|
return Ae.has(a);
|
|
271
271
|
}
|
|
272
272
|
let B = /* @__PURE__ */ new WeakMap();
|
|
273
|
-
function
|
|
273
|
+
function Se() {
|
|
274
274
|
B = /* @__PURE__ */ new WeakMap();
|
|
275
275
|
}
|
|
276
276
|
function p(t) {
|
|
@@ -282,50 +282,50 @@ function p(t) {
|
|
|
282
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
|
|
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
|
|
293
|
-
if (!ke(
|
|
294
|
-
const h = (e =
|
|
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
|
|
297
|
-
if (
|
|
298
|
-
const
|
|
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 =
|
|
300
|
+
return ((f = (b = l.ownerDocument.getElementById(g)) == null ? void 0 : b.textContent) == null ? void 0 : f.trim()) ?? "";
|
|
301
301
|
}).filter(Boolean);
|
|
302
|
-
if (
|
|
303
|
-
a +=
|
|
302
|
+
if (d.length) {
|
|
303
|
+
a += d.join(" ");
|
|
304
304
|
continue;
|
|
305
305
|
}
|
|
306
306
|
}
|
|
307
|
-
a += ((i =
|
|
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
|
|
310
|
-
if (
|
|
311
|
-
a +=
|
|
309
|
+
const c = (r = l.getAttribute("aria-label")) == null ? void 0 : r.trim();
|
|
310
|
+
if (c)
|
|
311
|
+
a += c;
|
|
312
312
|
else {
|
|
313
|
-
const
|
|
314
|
-
|
|
313
|
+
const d = l.querySelector("title");
|
|
314
|
+
d && (a += d.textContent ?? "");
|
|
315
315
|
}
|
|
316
|
-
} else (o =
|
|
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
|
|
321
|
+
let W = /* @__PURE__ */ new WeakMap();
|
|
322
322
|
function Ie() {
|
|
323
|
-
|
|
323
|
+
W = /* @__PURE__ */ new WeakMap();
|
|
324
324
|
}
|
|
325
|
-
function
|
|
325
|
+
function Te(t) {
|
|
326
326
|
return t.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
327
327
|
}
|
|
328
|
-
const
|
|
328
|
+
const Ee = [
|
|
329
329
|
"data-testid",
|
|
330
330
|
"data-test-id",
|
|
331
331
|
"data-cy",
|
|
@@ -335,12 +335,12 @@ const Te = [
|
|
|
335
335
|
"for",
|
|
336
336
|
"aria-label"
|
|
337
337
|
];
|
|
338
|
-
function
|
|
338
|
+
function Ce(t) {
|
|
339
339
|
const a = t.tagName.toLowerCase();
|
|
340
|
-
for (const i of
|
|
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}="${
|
|
343
|
+
return `${a}[${i}="${Te(n)}"]`;
|
|
344
344
|
}
|
|
345
345
|
const e = t.parentElement;
|
|
346
346
|
if (e) {
|
|
@@ -352,7 +352,7 @@ function Le(t) {
|
|
|
352
352
|
}
|
|
353
353
|
return a;
|
|
354
354
|
}
|
|
355
|
-
function
|
|
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(
|
|
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,30 +373,30 @@ function $(t) {
|
|
|
373
373
|
}
|
|
374
374
|
return i.join(" > ");
|
|
375
375
|
}
|
|
376
|
-
function
|
|
376
|
+
function m(t) {
|
|
377
377
|
var r;
|
|
378
|
-
const a =
|
|
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:
|
|
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:
|
|
389
|
+
e.unshift({ selector: M(i), delimiter: " >>>iframe> " }), i = s;
|
|
390
390
|
else {
|
|
391
|
-
e.unshift({ selector:
|
|
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
|
|
397
|
+
return W.set(t, n), n;
|
|
398
398
|
}
|
|
399
|
-
function
|
|
399
|
+
function wi(t) {
|
|
400
400
|
const a = [], e = [];
|
|
401
401
|
let i = t;
|
|
402
402
|
for (; i; ) {
|
|
@@ -429,11 +429,11 @@ function yi(t) {
|
|
|
429
429
|
}
|
|
430
430
|
return null;
|
|
431
431
|
}
|
|
432
|
-
function
|
|
432
|
+
function u(t) {
|
|
433
433
|
const a = t.outerHTML;
|
|
434
434
|
return a.length > 200 ? a.slice(0, 200) + "..." : a;
|
|
435
435
|
}
|
|
436
|
-
const
|
|
436
|
+
const Le = /* @__PURE__ */ new Set([
|
|
437
437
|
"aria-activedescendant",
|
|
438
438
|
"aria-atomic",
|
|
439
439
|
"aria-autocomplete",
|
|
@@ -568,7 +568,7 @@ const Ce = /* @__PURE__ */ new Set([
|
|
|
568
568
|
u: !0,
|
|
569
569
|
var: !0,
|
|
570
570
|
wbr: !0
|
|
571
|
-
},
|
|
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 Ce = /* @__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
|
|
605
|
-
function
|
|
606
|
-
|
|
604
|
+
let E = null, C = null;
|
|
605
|
+
function Me() {
|
|
606
|
+
E = null, C = null;
|
|
607
607
|
}
|
|
608
608
|
function z(t) {
|
|
609
609
|
var n;
|
|
610
|
-
if (
|
|
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
|
|
615
|
-
if (
|
|
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,
|
|
621
|
-
const h = () => (s === void 0 && (s =
|
|
622
|
-
for (const
|
|
623
|
-
if (
|
|
624
|
-
const
|
|
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:
|
|
628
|
-
html:
|
|
627
|
+
selector: d.selector,
|
|
628
|
+
html: d.html,
|
|
629
629
|
impact: "critical",
|
|
630
|
-
message: `Invalid ARIA attribute "${
|
|
630
|
+
message: `Invalid ARIA attribute "${c.name}".`
|
|
631
631
|
});
|
|
632
632
|
break;
|
|
633
633
|
}
|
|
634
|
-
for (const
|
|
635
|
-
if (!
|
|
636
|
-
const
|
|
637
|
-
if (!(
|
|
638
|
-
if (G.has(
|
|
639
|
-
if (
|
|
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: `${
|
|
646
|
+
message: `${c.name} must be "true" or "false", got "${d}".`
|
|
647
647
|
});
|
|
648
648
|
}
|
|
649
|
-
} else if (X.has(
|
|
650
|
-
if (
|
|
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: `${
|
|
657
|
+
message: `${c.name} must be "true", "false", or "mixed", got "${d}".`
|
|
658
658
|
});
|
|
659
659
|
}
|
|
660
|
-
} else if (qe.has(
|
|
661
|
-
if (
|
|
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: `${
|
|
668
|
+
message: `${c.name} must be an integer, got "${d}".`
|
|
669
669
|
});
|
|
670
670
|
}
|
|
671
|
-
} else if (Re.has(
|
|
672
|
-
if (
|
|
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: `${
|
|
679
|
+
message: `${c.name} must be a number, got "${d}".`
|
|
680
680
|
});
|
|
681
681
|
}
|
|
682
|
-
} else if (Y[
|
|
683
|
-
const g =
|
|
682
|
+
} else if (Y[c.name]) {
|
|
683
|
+
const g = d.split(/\s+/);
|
|
684
684
|
for (const b of g)
|
|
685
|
-
if (!Y[
|
|
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 "${
|
|
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
|
|
701
|
-
if (!
|
|
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 <${
|
|
710
|
+
message: `aria-label and aria-labelledby are prohibited on <${d}> elements.`
|
|
711
711
|
});
|
|
712
712
|
}
|
|
713
|
-
} else if (
|
|
714
|
-
if (K.has(
|
|
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
|
|
717
|
+
const y = h();
|
|
718
718
|
i.push({
|
|
719
719
|
ruleId: "aria-prohibited-attr",
|
|
720
|
-
selector:
|
|
721
|
-
html:
|
|
720
|
+
selector: y.selector,
|
|
721
|
+
html: y.html,
|
|
722
722
|
impact: "serious",
|
|
723
|
-
message: `aria-label and aria-labelledby are prohibited on role "${
|
|
723
|
+
message: `aria-label and aria-labelledby are prohibited on role "${c}".`
|
|
724
724
|
});
|
|
725
725
|
}
|
|
726
726
|
}
|
|
727
|
-
const g =
|
|
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(
|
|
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,18 +736,18 @@ 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 "${
|
|
739
|
+
message: `Attribute "${b.name}" is prohibited on role "${c}".`
|
|
740
740
|
});
|
|
741
741
|
}
|
|
742
742
|
}
|
|
743
743
|
}
|
|
744
744
|
}
|
|
745
745
|
}
|
|
746
|
-
return
|
|
746
|
+
return E = new WeakRef(t), C = { validAttr: a, validAttrValue: e, prohibitedAttr: i }, C;
|
|
747
747
|
}
|
|
748
|
-
let _ = /* @__PURE__ */ new WeakMap(),
|
|
748
|
+
let _ = /* @__PURE__ */ new WeakMap(), P = /* @__PURE__ */ new WeakMap(), j = /* @__PURE__ */ new WeakMap();
|
|
749
749
|
function He() {
|
|
750
|
-
_ = /* @__PURE__ */ new WeakMap(),
|
|
750
|
+
_ = /* @__PURE__ */ new WeakMap(), P = /* @__PURE__ */ new WeakMap(), j = /* @__PURE__ */ new WeakMap();
|
|
751
751
|
}
|
|
752
752
|
function A(t) {
|
|
753
753
|
let a = _.get(t);
|
|
@@ -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
|
|
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 (
|
|
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)];
|
|
@@ -804,10 +804,10 @@ function R(t) {
|
|
|
804
804
|
return r ? [parseInt(r[1]), parseInt(r[2]), parseInt(r[3])] : null;
|
|
805
805
|
}
|
|
806
806
|
function De(t) {
|
|
807
|
-
const a =
|
|
807
|
+
const a = P.get(t);
|
|
808
808
|
if (a !== void 0) return a;
|
|
809
809
|
const e = Oe(t);
|
|
810
|
-
return
|
|
810
|
+
return P.set(t, e), e;
|
|
811
811
|
}
|
|
812
812
|
function Oe(t) {
|
|
813
813
|
let a = t;
|
|
@@ -828,20 +828,20 @@ function Oe(t) {
|
|
|
828
828
|
}
|
|
829
829
|
return [255, 255, 255];
|
|
830
830
|
}
|
|
831
|
-
const
|
|
831
|
+
const Fe = /* @__PURE__ */ new Set(["IMG", "PICTURE", "VIDEO", "SVG"]);
|
|
832
832
|
function Be(t) {
|
|
833
|
-
const a =
|
|
833
|
+
const a = j.get(t);
|
|
834
834
|
if (a !== void 0) return a;
|
|
835
|
-
const e =
|
|
836
|
-
return
|
|
835
|
+
const e = We(t);
|
|
836
|
+
return j.set(t, e), e;
|
|
837
837
|
}
|
|
838
|
-
function
|
|
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)) &&
|
|
844
|
+
if (!(n === t || n.contains(t)) && Fe.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
|
|
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 || "",
|
|
878
|
-
|
|
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
|
|
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;
|
|
@@ -888,7 +888,7 @@ function J(t) {
|
|
|
888
888
|
}
|
|
889
889
|
return !1;
|
|
890
890
|
}
|
|
891
|
-
const
|
|
891
|
+
const je = {
|
|
892
892
|
id: "img-alt",
|
|
893
893
|
wcag: ["1.1.1"],
|
|
894
894
|
level: "A",
|
|
@@ -898,7 +898,7 @@ const Pe = {
|
|
|
898
898
|
run(t) {
|
|
899
899
|
const a = [];
|
|
900
900
|
for (const e of t.querySelectorAll("img")) {
|
|
901
|
-
if (p(e) ||
|
|
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 Pe = {
|
|
|
908
908
|
if (n !== null && n.trim() === "" && n !== "") {
|
|
909
909
|
a.push({
|
|
910
910
|
ruleId: "img-alt",
|
|
911
|
-
selector:
|
|
912
|
-
html:
|
|
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 Pe = {
|
|
|
918
918
|
}
|
|
919
919
|
!e.hasAttribute("alt") && !v(e) && a.push({
|
|
920
920
|
ruleId: "img-alt",
|
|
921
|
-
selector:
|
|
922
|
-
html:
|
|
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) ||
|
|
929
|
+
p(e) || Q(e) || v(e) || a.push({
|
|
930
930
|
ruleId: "img-alt",
|
|
931
|
-
selector:
|
|
932
|
-
html:
|
|
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)
|
|
@@ -941,11 +941,11 @@ function Ve(t) {
|
|
|
941
941
|
var r, o, s;
|
|
942
942
|
const a = t.getAttribute("aria-labelledby");
|
|
943
943
|
if (a) {
|
|
944
|
-
const
|
|
945
|
-
var
|
|
946
|
-
return ((
|
|
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 (
|
|
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;
|
|
@@ -969,8 +969,8 @@ const ze = {
|
|
|
969
969
|
const r = i.getAttribute("role");
|
|
970
970
|
a.push({
|
|
971
971
|
ruleId: "svg-img-alt",
|
|
972
|
-
selector:
|
|
973
|
-
html:
|
|
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
|
});
|
|
@@ -990,8 +990,8 @@ const ze = {
|
|
|
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:
|
|
994
|
-
html:
|
|
993
|
+
selector: m(e),
|
|
994
|
+
html: u(e),
|
|
995
995
|
impact: "critical",
|
|
996
996
|
message: "Image input missing alt text."
|
|
997
997
|
});
|
|
@@ -1004,7 +1004,7 @@ const ze = {
|
|
|
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: "
|
|
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,13 +1014,17 @@ const ze = {
|
|
|
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
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
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;
|
|
@@ -1032,17 +1036,20 @@ const ze = {
|
|
|
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: "
|
|
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
|
-
|
|
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:
|
|
1043
|
-
html:
|
|
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;
|
|
@@ -1060,8 +1067,8 @@ const ze = {
|
|
|
1060
1067
|
if (p(e)) continue;
|
|
1061
1068
|
v(e) || a.push({
|
|
1062
1069
|
ruleId: "area-alt",
|
|
1063
|
-
selector:
|
|
1064
|
-
html:
|
|
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 ze = {
|
|
|
1069
1076
|
return a;
|
|
1070
1077
|
}
|
|
1071
1078
|
};
|
|
1072
|
-
function
|
|
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
|
|
1078
|
-
return ((h = (
|
|
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
|
}
|
|
@@ -1084,7 +1091,7 @@ function Qe(t) {
|
|
|
1084
1091
|
const i = (r = t.getAttribute("title")) == null ? void 0 : r.trim();
|
|
1085
1092
|
return i || "";
|
|
1086
1093
|
}
|
|
1087
|
-
const
|
|
1094
|
+
const Qe = {
|
|
1088
1095
|
id: "object-alt",
|
|
1089
1096
|
wcag: ["1.1.1"],
|
|
1090
1097
|
level: "A",
|
|
@@ -1104,7 +1111,7 @@ const Je = {
|
|
|
1104
1111
|
}
|
|
1105
1112
|
n = n.parentElement;
|
|
1106
1113
|
}
|
|
1107
|
-
if (r || i.getAttribute("role") === "presentation" || i.getAttribute("role") === "none" ||
|
|
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,8 +1119,8 @@ const Je = {
|
|
|
1112
1119
|
}
|
|
1113
1120
|
a.push({
|
|
1114
1121
|
ruleId: "object-alt",
|
|
1115
|
-
selector:
|
|
1116
|
-
html:
|
|
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
|
});
|
|
@@ -1133,8 +1140,8 @@ const Je = {
|
|
|
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:
|
|
1137
|
-
html:
|
|
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
|
});
|
|
@@ -1193,7 +1200,7 @@ function et(t) {
|
|
|
1193
1200
|
return `Unknown check type: ${String(t.type)}`;
|
|
1194
1201
|
}
|
|
1195
1202
|
}
|
|
1196
|
-
function
|
|
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:
|
|
1222
|
-
html:
|
|
1228
|
+
selector: m(n),
|
|
1229
|
+
html: u(n),
|
|
1223
1230
|
impact: t.impact,
|
|
1224
|
-
message:
|
|
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
|
|
1234
|
-
|
|
1240
|
+
const l = s.getAttribute(n);
|
|
1241
|
+
l !== null && tt(l, r, o) && i.push({
|
|
1235
1242
|
ruleId: t.id,
|
|
1236
|
-
selector:
|
|
1237
|
-
html:
|
|
1243
|
+
selector: m(s),
|
|
1244
|
+
html: u(s),
|
|
1238
1245
|
impact: t.impact,
|
|
1239
|
-
message:
|
|
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:
|
|
1251
|
-
html:
|
|
1257
|
+
selector: m(r),
|
|
1258
|
+
html: u(r),
|
|
1252
1259
|
impact: t.impact,
|
|
1253
|
-
message:
|
|
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
|
|
1267
|
+
let l;
|
|
1261
1268
|
try {
|
|
1262
|
-
|
|
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
|
|
1269
|
-
if (
|
|
1270
|
-
const
|
|
1271
|
-
s && !
|
|
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:
|
|
1274
|
-
html:
|
|
1280
|
+
selector: m(h),
|
|
1281
|
+
html: u(h),
|
|
1275
1282
|
impact: t.impact,
|
|
1276
|
-
message:
|
|
1283
|
+
message: S(t.message, h, t.check),
|
|
1277
1284
|
element: h
|
|
1278
|
-
}) : !s &&
|
|
1285
|
+
}) : !s && d && i.push({
|
|
1279
1286
|
ruleId: t.id,
|
|
1280
|
-
selector:
|
|
1281
|
-
html:
|
|
1287
|
+
selector: m(h),
|
|
1288
|
+
html: u(h),
|
|
1282
1289
|
impact: t.impact,
|
|
1283
|
-
message:
|
|
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:
|
|
1295
|
-
html:
|
|
1301
|
+
selector: m(r),
|
|
1302
|
+
html: u(r),
|
|
1296
1303
|
impact: t.impact,
|
|
1297
|
-
message:
|
|
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:
|
|
1313
|
-
html:
|
|
1319
|
+
selector: m(o),
|
|
1320
|
+
html: u(o),
|
|
1314
1321
|
impact: t.impact,
|
|
1315
|
-
message:
|
|
1322
|
+
message: S(t.message, o, t.check),
|
|
1316
1323
|
element: o
|
|
1317
1324
|
});
|
|
1318
1325
|
break;
|
|
@@ -1381,39 +1388,39 @@ const at = {
|
|
|
1381
1388
|
"textbox"
|
|
1382
1389
|
]);
|
|
1383
1390
|
function st(t) {
|
|
1384
|
-
var o, s,
|
|
1391
|
+
var o, s, l, h;
|
|
1385
1392
|
const a = (o = t.getAttribute("role")) == null ? void 0 : o.trim().toLowerCase();
|
|
1386
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
|
|
1391
|
-
const g = t.ownerDocument.getElementById(
|
|
1392
|
-
return g ?
|
|
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 (
|
|
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
|
|
1401
|
-
if (
|
|
1402
|
-
const g =
|
|
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
|
|
1407
|
-
if (
|
|
1408
|
-
const
|
|
1409
|
-
if (
|
|
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 = (
|
|
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
|
|
1416
|
-
if (
|
|
1422
|
+
const c = (h = t.getAttribute("placeholder")) == null ? void 0 : h.trim();
|
|
1423
|
+
if (c) return c;
|
|
1417
1424
|
}
|
|
1418
1425
|
return "";
|
|
1419
1426
|
}
|
|
@@ -1423,7 +1430,7 @@ const lt = {
|
|
|
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:
|
|
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
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}`);
|
|
@@ -1431,13 +1438,23 @@ const lt = {
|
|
|
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
|
-
st(r)
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
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
|
}
|
|
@@ -1465,8 +1482,8 @@ const lt = {
|
|
|
1465
1482
|
const s = n.length + r;
|
|
1466
1483
|
s > 1 && a.push({
|
|
1467
1484
|
ruleId: "form-field-multiple-labels",
|
|
1468
|
-
selector:
|
|
1469
|
-
html:
|
|
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
|
});
|
|
@@ -1485,8 +1502,8 @@ const lt = {
|
|
|
1485
1502
|
for (const e of t.querySelectorAll("select"))
|
|
1486
1503
|
p(e) || v(e) || a.push({
|
|
1487
1504
|
ruleId: "select-name",
|
|
1488
|
-
selector:
|
|
1489
|
-
html:
|
|
1505
|
+
selector: m(e),
|
|
1506
|
+
html: u(e),
|
|
1490
1507
|
impact: "critical",
|
|
1491
1508
|
message: "Select element has no accessible name."
|
|
1492
1509
|
});
|
|
@@ -1509,8 +1526,8 @@ const lt = {
|
|
|
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:
|
|
1513
|
-
html:
|
|
1529
|
+
selector: m(n),
|
|
1530
|
+
html: u(n),
|
|
1514
1531
|
impact: "critical",
|
|
1515
1532
|
message: "Input button has no discernible text."
|
|
1516
1533
|
});
|
|
@@ -1606,8 +1623,8 @@ const vt = {
|
|
|
1606
1623
|
const i = e.getAttribute("autocomplete").trim();
|
|
1607
1624
|
i && (ft(i) || a.push({
|
|
1608
1625
|
ruleId: "autocomplete-valid",
|
|
1609
|
-
selector:
|
|
1610
|
-
html:
|
|
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
|
|
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 wt = {
|
|
|
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:
|
|
1662
|
-
html:
|
|
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,15 +1693,15 @@ const wt = {
|
|
|
1676
1693
|
}
|
|
1677
1694
|
o.trim() && (ee(i, o) || a.push({
|
|
1678
1695
|
ruleId: "label-content-name-mismatch",
|
|
1679
|
-
selector:
|
|
1680
|
-
html:
|
|
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
|
}));
|
|
1684
1701
|
}
|
|
1685
1702
|
return a;
|
|
1686
1703
|
}
|
|
1687
|
-
},
|
|
1704
|
+
}, wt = {
|
|
1688
1705
|
id: "label-title-only",
|
|
1689
1706
|
wcag: [],
|
|
1690
1707
|
level: "A",
|
|
@@ -1699,18 +1716,18 @@ const wt = {
|
|
|
1699
1716
|
);
|
|
1700
1717
|
for (const s of e) {
|
|
1701
1718
|
if (p(s)) continue;
|
|
1702
|
-
const
|
|
1703
|
-
let
|
|
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() && (
|
|
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() && (
|
|
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:
|
|
1713
|
-
html:
|
|
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
|
});
|
|
@@ -1729,7 +1746,7 @@ const wt = {
|
|
|
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
|
-
},
|
|
1749
|
+
}, xt = k(At), St = /* @__PURE__ */ new Set([
|
|
1733
1750
|
"div",
|
|
1734
1751
|
"span",
|
|
1735
1752
|
"p",
|
|
@@ -1768,11 +1785,11 @@ const wt = {
|
|
|
1768
1785
|
const a = [];
|
|
1769
1786
|
for (const e of t.querySelectorAll('[tabindex="0"]')) {
|
|
1770
1787
|
const i = e.tagName.toLowerCase();
|
|
1771
|
-
if (!
|
|
1788
|
+
if (!St.has(i)) continue;
|
|
1772
1789
|
e.getAttribute("role") || a.push({
|
|
1773
1790
|
ruleId: "focus-order-semantics",
|
|
1774
|
-
selector:
|
|
1775
|
-
html:
|
|
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
|
});
|
|
@@ -1788,7 +1805,7 @@ const wt = {
|
|
|
1788
1805
|
"select",
|
|
1789
1806
|
"textarea",
|
|
1790
1807
|
"video"
|
|
1791
|
-
]),
|
|
1808
|
+
]), Tt = /* @__PURE__ */ new Set([
|
|
1792
1809
|
"button",
|
|
1793
1810
|
"checkbox",
|
|
1794
1811
|
"combobox",
|
|
@@ -1812,7 +1829,7 @@ const wt = {
|
|
|
1812
1829
|
"tabpanel",
|
|
1813
1830
|
"textbox",
|
|
1814
1831
|
"treeitem"
|
|
1815
|
-
]),
|
|
1832
|
+
]), Et = {
|
|
1816
1833
|
grid: /* @__PURE__ */ new Set(["gridcell", "row", "columnheader", "rowheader"]),
|
|
1817
1834
|
listbox: /* @__PURE__ */ new Set(["option"]),
|
|
1818
1835
|
menu: /* @__PURE__ */ new Set(["menuitem", "menuitemcheckbox", "menuitemradio"]),
|
|
@@ -1822,18 +1839,18 @@ const wt = {
|
|
|
1822
1839
|
tree: /* @__PURE__ */ new Set(["treeitem"]),
|
|
1823
1840
|
treegrid: /* @__PURE__ */ new Set(["gridcell", "row", "columnheader", "rowheader", "treeitem"])
|
|
1824
1841
|
};
|
|
1825
|
-
function
|
|
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
|
-
return !e || !i ? !1 : ((o =
|
|
1845
|
+
return !e || !i ? !1 : ((o = Et[e]) == null ? void 0 : o.has(i)) ?? !1;
|
|
1829
1846
|
}
|
|
1830
|
-
function
|
|
1847
|
+
function Lt(t) {
|
|
1831
1848
|
var n;
|
|
1832
1849
|
const a = t.tagName.toLowerCase();
|
|
1833
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 &&
|
|
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
|
}
|
|
@@ -1856,13 +1873,13 @@ const Rt = {
|
|
|
1856
1873
|
for (; o; ) {
|
|
1857
1874
|
for (; r.length > 0 && !r[r.length - 1].contains(o); )
|
|
1858
1875
|
r.pop();
|
|
1859
|
-
if (!p(o) &&
|
|
1876
|
+
if (!p(o) && Lt(o)) {
|
|
1860
1877
|
if (r.length > 0) {
|
|
1861
1878
|
const s = r[r.length - 1];
|
|
1862
|
-
|
|
1879
|
+
Ct(s, o) || a.push({
|
|
1863
1880
|
ruleId: "nested-interactive",
|
|
1864
|
-
selector:
|
|
1865
|
-
html:
|
|
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
|
});
|
|
@@ -1889,23 +1906,23 @@ const Rt = {
|
|
|
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
|
|
1893
|
-
if (!
|
|
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
|
|
1896
|
-
|
|
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:
|
|
1901
|
-
html:
|
|
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
|
-
},
|
|
1925
|
+
}, $t = {
|
|
1909
1926
|
id: "accesskeys",
|
|
1910
1927
|
wcag: [],
|
|
1911
1928
|
level: "A",
|
|
@@ -1928,14 +1945,14 @@ const Rt = {
|
|
|
1928
1945
|
for (const o of r.slice(1))
|
|
1929
1946
|
a.push({
|
|
1930
1947
|
ruleId: "accesskeys",
|
|
1931
|
-
selector:
|
|
1932
|
-
html:
|
|
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
|
});
|
|
1936
1953
|
return a;
|
|
1937
1954
|
}
|
|
1938
|
-
},
|
|
1955
|
+
}, Mt = {
|
|
1939
1956
|
id: "heading-order",
|
|
1940
1957
|
wcag: [],
|
|
1941
1958
|
level: "A",
|
|
@@ -1951,11 +1968,11 @@ const Rt = {
|
|
|
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:
|
|
1955
|
-
html:
|
|
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: ${
|
|
1975
|
+
context: n ? `Previous heading: ${u(n)}` : void 0
|
|
1959
1976
|
}), i = o, n = r;
|
|
1960
1977
|
}
|
|
1961
1978
|
return a;
|
|
@@ -1978,8 +1995,8 @@ const Rt = {
|
|
|
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:
|
|
1982
|
-
html:
|
|
1998
|
+
selector: m(e),
|
|
1999
|
+
html: u(e),
|
|
1983
2000
|
impact: "moderate",
|
|
1984
2001
|
message: "Page has multiple main landmarks."
|
|
1985
2002
|
})) : [];
|
|
@@ -1997,8 +2014,8 @@ const Rt = {
|
|
|
1997
2014
|
return i.length > 1 && i.slice(1).forEach(
|
|
1998
2015
|
(n) => a.push({
|
|
1999
2016
|
ruleId: "landmark-no-duplicate-banner",
|
|
2000
|
-
selector:
|
|
2001
|
-
html:
|
|
2017
|
+
selector: m(n),
|
|
2018
|
+
html: u(n),
|
|
2002
2019
|
impact: "moderate",
|
|
2003
2020
|
message: "Page has multiple banner landmarks."
|
|
2004
2021
|
})
|
|
@@ -2017,14 +2034,14 @@ const Rt = {
|
|
|
2017
2034
|
return i.length > 1 && i.slice(1).forEach(
|
|
2018
2035
|
(n) => a.push({
|
|
2019
2036
|
ruleId: "landmark-no-duplicate-contentinfo",
|
|
2020
|
-
selector:
|
|
2021
|
-
html:
|
|
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
|
-
},
|
|
2044
|
+
}, Ft = {
|
|
2028
2045
|
id: "landmark-no-duplicate-main",
|
|
2029
2046
|
wcag: [],
|
|
2030
2047
|
level: "A",
|
|
@@ -2037,8 +2054,8 @@ const Rt = {
|
|
|
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:
|
|
2041
|
-
html:
|
|
2057
|
+
selector: m(i),
|
|
2058
|
+
html: u(i),
|
|
2042
2059
|
impact: "moderate",
|
|
2043
2060
|
message: "Page has multiple main landmarks."
|
|
2044
2061
|
})
|
|
@@ -2057,14 +2074,14 @@ const Rt = {
|
|
|
2057
2074
|
for (const i of e)
|
|
2058
2075
|
i.closest(N) && a.push({
|
|
2059
2076
|
ruleId: "landmark-banner-is-top-level",
|
|
2060
|
-
selector:
|
|
2061
|
-
html:
|
|
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
|
-
},
|
|
2084
|
+
}, Wt = {
|
|
2068
2085
|
id: "landmark-contentinfo-is-top-level",
|
|
2069
2086
|
wcag: [],
|
|
2070
2087
|
level: "A",
|
|
@@ -2077,8 +2094,8 @@ const Rt = {
|
|
|
2077
2094
|
for (const i of e)
|
|
2078
2095
|
i.closest(N) && a.push({
|
|
2079
2096
|
ruleId: "landmark-contentinfo-is-top-level",
|
|
2080
|
-
selector:
|
|
2081
|
-
html:
|
|
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 Rt = {
|
|
|
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:
|
|
2102
|
-
html:
|
|
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
|
-
},
|
|
2126
|
+
}, Pt = {
|
|
2110
2127
|
id: "landmark-complementary-is-top-level",
|
|
2111
2128
|
wcag: [],
|
|
2112
2129
|
level: "A",
|
|
@@ -2120,15 +2137,15 @@ const Rt = {
|
|
|
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:
|
|
2124
|
-
html:
|
|
2140
|
+
selector: m(i),
|
|
2141
|
+
html: u(i),
|
|
2125
2142
|
impact: "moderate",
|
|
2126
2143
|
message: "Complementary landmark should be top-level."
|
|
2127
2144
|
});
|
|
2128
2145
|
}
|
|
2129
2146
|
return a;
|
|
2130
2147
|
}
|
|
2131
|
-
},
|
|
2148
|
+
}, jt = {
|
|
2132
2149
|
id: "landmark-unique",
|
|
2133
2150
|
wcag: [],
|
|
2134
2151
|
level: "A",
|
|
@@ -2148,16 +2165,16 @@ const Rt = {
|
|
|
2148
2165
|
if (r.length <= 1) continue;
|
|
2149
2166
|
const o = /* @__PURE__ */ new Map();
|
|
2150
2167
|
for (const s of r) {
|
|
2151
|
-
const
|
|
2152
|
-
h.push(s), o.set(
|
|
2168
|
+
const l = v(s).toLowerCase() || "", h = o.get(l) || [];
|
|
2169
|
+
h.push(s), o.set(l, h);
|
|
2153
2170
|
}
|
|
2154
|
-
for (const [s,
|
|
2155
|
-
if (
|
|
2156
|
-
for (const h of
|
|
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:
|
|
2160
|
-
html:
|
|
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
|
});
|
|
@@ -2181,8 +2198,8 @@ const Rt = {
|
|
|
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:
|
|
2185
|
-
html:
|
|
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
|
}));
|
|
@@ -2212,8 +2229,8 @@ const Rt = {
|
|
|
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:
|
|
2216
|
-
html:
|
|
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
|
});
|
|
@@ -2236,65 +2253,89 @@ const Rt = {
|
|
|
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
|
|
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
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
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
|
-
},
|
|
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:
|
|
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
|
|
2265
|
-
if (
|
|
2266
|
-
const
|
|
2267
|
-
if (t.getElementById(
|
|
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
|
-
|
|
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 = {
|
|
2279
2311
|
id: "page-has-heading-one",
|
|
2280
2312
|
wcag: [],
|
|
2281
2313
|
level: "A",
|
|
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
|
|
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
|
|
2291
|
-
if (v(
|
|
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
2341
|
};
|
|
@@ -2310,18 +2351,22 @@ const Zt = {
|
|
|
2310
2351
|
level: "A",
|
|
2311
2352
|
description: "Frames must have an accessible name.",
|
|
2312
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'.",
|
|
2313
|
-
prompt: "
|
|
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.",
|
|
2314
2355
|
run(t) {
|
|
2315
2356
|
const a = [];
|
|
2316
2357
|
for (const e of t.querySelectorAll("iframe, frame")) {
|
|
2317
2358
|
if (p(e) || me(e)) continue;
|
|
2318
|
-
v(e)
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
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
|
+
}
|
|
2325
2370
|
}
|
|
2326
2371
|
return a;
|
|
2327
2372
|
}
|
|
@@ -2349,8 +2394,8 @@ const Zt = {
|
|
|
2349
2394
|
for (const o of r.slice(1))
|
|
2350
2395
|
a.push({
|
|
2351
2396
|
ruleId: "frame-title-unique",
|
|
2352
|
-
selector:
|
|
2353
|
-
html:
|
|
2397
|
+
selector: m(o),
|
|
2398
|
+
html: u(o),
|
|
2354
2399
|
impact: "moderate",
|
|
2355
2400
|
message: "Frame title is not unique. Use a distinct title for each frame."
|
|
2356
2401
|
});
|
|
@@ -2363,17 +2408,27 @@ const Zt = {
|
|
|
2363
2408
|
tags: ["best-practice"],
|
|
2364
2409
|
description: "Headings must have discernible text.",
|
|
2365
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.",
|
|
2366
|
-
prompt: "
|
|
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.",
|
|
2367
2412
|
run(t) {
|
|
2413
|
+
var i;
|
|
2368
2414
|
const a = [], e = t.querySelectorAll('h1, h2, h3, h4, h5, h6, [role="heading"]');
|
|
2369
|
-
for (const
|
|
2370
|
-
p(
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
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
|
+
}
|
|
2377
2432
|
return a;
|
|
2378
2433
|
}
|
|
2379
2434
|
}, aa = {
|
|
@@ -2382,28 +2437,29 @@ const Zt = {
|
|
|
2382
2437
|
level: "AA",
|
|
2383
2438
|
description: "Viewport meta tag must not disable user scaling.",
|
|
2384
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.",
|
|
2385
|
-
prompt: "
|
|
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.",
|
|
2386
2441
|
run(t) {
|
|
2387
|
-
var r;
|
|
2388
2442
|
const a = [], e = t.querySelector('meta[name="viewport"]');
|
|
2389
2443
|
if (!e) return [];
|
|
2390
|
-
const i =
|
|
2391
|
-
(/user-scalable\s*=\s*no/i.test(
|
|
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({
|
|
2392
2446
|
ruleId: "meta-viewport",
|
|
2393
|
-
selector:
|
|
2394
|
-
html:
|
|
2447
|
+
selector: m(e),
|
|
2448
|
+
html: u(e),
|
|
2395
2449
|
impact: "critical",
|
|
2396
|
-
message: "Viewport disables user scaling. Remove user-scalable=no."
|
|
2450
|
+
message: "Viewport disables user scaling. Remove user-scalable=no.",
|
|
2451
|
+
context: `content: "${i}"`
|
|
2397
2452
|
});
|
|
2398
|
-
const
|
|
2399
|
-
if (
|
|
2400
|
-
const 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);
|
|
2401
2456
|
s < 2 && a.push({
|
|
2402
2457
|
ruleId: "meta-viewport",
|
|
2403
|
-
selector:
|
|
2404
|
-
html:
|
|
2458
|
+
selector: m(e),
|
|
2459
|
+
html: u(e),
|
|
2405
2460
|
impact: "critical",
|
|
2406
|
-
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}"`
|
|
2407
2463
|
});
|
|
2408
2464
|
}
|
|
2409
2465
|
return a;
|
|
@@ -2423,16 +2479,16 @@ const Zt = {
|
|
|
2423
2479
|
if (/^\d+\s*[;,]\s*url\s*=/i.test(e) || /^\d+\s*[;,]\s*['"]?\s*https?:/i.test(e))
|
|
2424
2480
|
return n > 0 && n <= 72e3 ? [{
|
|
2425
2481
|
ruleId: "meta-refresh",
|
|
2426
|
-
selector:
|
|
2427
|
-
html:
|
|
2482
|
+
selector: m(a),
|
|
2483
|
+
html: u(a),
|
|
2428
2484
|
impact: "critical",
|
|
2429
2485
|
message: `Page redirects after ${n} seconds without warning. Use server-side redirect.`
|
|
2430
2486
|
}] : [];
|
|
2431
2487
|
if (n > 0 && n <= 72e3)
|
|
2432
2488
|
return [{
|
|
2433
2489
|
ruleId: "meta-refresh",
|
|
2434
|
-
selector:
|
|
2435
|
-
html:
|
|
2490
|
+
selector: m(a),
|
|
2491
|
+
html: u(a),
|
|
2436
2492
|
impact: "critical",
|
|
2437
2493
|
message: `Page auto-refreshes after ${n} seconds. Provide user control over refresh.`
|
|
2438
2494
|
}];
|
|
@@ -2474,13 +2530,13 @@ const Zt = {
|
|
|
2474
2530
|
const a = [];
|
|
2475
2531
|
for (const n of t.querySelectorAll("p")) {
|
|
2476
2532
|
if (p(n)) continue;
|
|
2477
|
-
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),
|
|
2478
|
-
if ((o && s || o && h) &&
|
|
2479
|
-
const
|
|
2480
|
-
|
|
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({
|
|
2481
2537
|
ruleId: "p-as-heading",
|
|
2482
|
-
selector:
|
|
2483
|
-
html:
|
|
2538
|
+
selector: m(n),
|
|
2539
|
+
html: u(n),
|
|
2484
2540
|
impact: "serious",
|
|
2485
2541
|
message: "Paragraph appears to be styled as a heading. Use an h1-h6 element instead."
|
|
2486
2542
|
});
|
|
@@ -2499,10 +2555,10 @@ const Zt = {
|
|
|
2499
2555
|
const a = [];
|
|
2500
2556
|
for (const e of t.querySelectorAll("[role]")) {
|
|
2501
2557
|
const r = e.getAttribute("role").replace(/[\u201C\u201D\u2018\u2019\u00AB\u00BB]/g, "").split(/\s+/).filter(Boolean);
|
|
2502
|
-
!r.some((s) =>
|
|
2558
|
+
!r.some((s) => xe(s)) && r.length > 0 && a.push({
|
|
2503
2559
|
ruleId: "aria-roles",
|
|
2504
|
-
selector:
|
|
2505
|
-
html:
|
|
2560
|
+
selector: m(e),
|
|
2561
|
+
html: u(e),
|
|
2506
2562
|
impact: "critical",
|
|
2507
2563
|
message: `Invalid ARIA role "${r[0]}".`
|
|
2508
2564
|
});
|
|
@@ -2565,8 +2621,8 @@ const Zt = {
|
|
|
2565
2621
|
if (!e.hasAttribute(r)) {
|
|
2566
2622
|
a.push({
|
|
2567
2623
|
ruleId: "aria-required-attr",
|
|
2568
|
-
selector:
|
|
2569
|
-
html:
|
|
2624
|
+
selector: m(e),
|
|
2625
|
+
html: u(e),
|
|
2570
2626
|
impact: "critical",
|
|
2571
2627
|
message: `Role "${i}" requires attribute "${r}".`
|
|
2572
2628
|
});
|
|
@@ -2584,13 +2640,13 @@ function pa(t) {
|
|
|
2584
2640
|
e && typeof e == "string" && e.trim() && a.push(`Classes: ${e.trim().slice(0, 100)}`);
|
|
2585
2641
|
const i = t.closest("form");
|
|
2586
2642
|
if (i) {
|
|
2587
|
-
const
|
|
2588
|
-
|
|
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)}`);
|
|
2589
2645
|
}
|
|
2590
2646
|
const n = t.parentElement;
|
|
2591
2647
|
if (n) {
|
|
2592
|
-
const
|
|
2593
|
-
(s =
|
|
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)}`);
|
|
2594
2650
|
}
|
|
2595
2651
|
return a.length > 0 ? a.join(`
|
|
2596
2652
|
`) : void 0;
|
|
@@ -2610,8 +2666,8 @@ const ga = {
|
|
|
2610
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;
|
|
2611
2667
|
v(e) || a.push({
|
|
2612
2668
|
ruleId: "button-name",
|
|
2613
|
-
selector:
|
|
2614
|
-
html:
|
|
2669
|
+
selector: m(e),
|
|
2670
|
+
html: u(e),
|
|
2615
2671
|
impact: "critical",
|
|
2616
2672
|
message: "Button has no discernible text.",
|
|
2617
2673
|
context: pa(e)
|
|
@@ -2721,27 +2777,31 @@ const ga = {
|
|
|
2721
2777
|
level: "A",
|
|
2722
2778
|
description: "ARIA attributes must be allowed for the element's role.",
|
|
2723
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.",
|
|
2724
|
-
prompt: "
|
|
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.",
|
|
2725
2781
|
run(t) {
|
|
2726
2782
|
const a = [];
|
|
2727
2783
|
for (const e of t.querySelectorAll("[role], [aria-*]")) {
|
|
2728
2784
|
if (p(e)) continue;
|
|
2729
|
-
const i =
|
|
2785
|
+
const i = L(e);
|
|
2730
2786
|
if (!i) continue;
|
|
2731
2787
|
const n = ba[i];
|
|
2732
2788
|
if (n)
|
|
2733
|
-
for (const r of e.attributes)
|
|
2734
|
-
r.name.startsWith("aria-")
|
|
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({
|
|
2735
2793
|
ruleId: "aria-allowed-attr",
|
|
2736
|
-
selector:
|
|
2737
|
-
html:
|
|
2794
|
+
selector: m(e),
|
|
2795
|
+
html: u(e),
|
|
2738
2796
|
impact: "critical",
|
|
2739
|
-
message: `ARIA attribute "${r.name}" is not allowed on role "${i}"
|
|
2740
|
-
|
|
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
|
+
}
|
|
2741
2801
|
}
|
|
2742
2802
|
return a;
|
|
2743
2803
|
}
|
|
2744
|
-
},
|
|
2804
|
+
}, ya = /* @__PURE__ */ new Set([
|
|
2745
2805
|
"base",
|
|
2746
2806
|
"col",
|
|
2747
2807
|
"colgroup",
|
|
@@ -2756,7 +2816,7 @@ const ga = {
|
|
|
2756
2816
|
"template",
|
|
2757
2817
|
"title",
|
|
2758
2818
|
"track"
|
|
2759
|
-
]),
|
|
2819
|
+
]), T = {
|
|
2760
2820
|
a: /* @__PURE__ */ new Set(["button", "checkbox", "menuitem", "menuitemcheckbox", "menuitemradio", "option", "radio", "switch", "tab", "treeitem", "link"]),
|
|
2761
2821
|
"a[href]": /* @__PURE__ */ new Set(["button", "checkbox", "menuitem", "menuitemcheckbox", "menuitemradio", "option", "radio", "switch", "tab", "treeitem"]),
|
|
2762
2822
|
abbr: "any",
|
|
@@ -2863,20 +2923,20 @@ const ga = {
|
|
|
2863
2923
|
video: /* @__PURE__ */ new Set(["application"]),
|
|
2864
2924
|
wbr: /* @__PURE__ */ new Set(["none", "presentation"])
|
|
2865
2925
|
};
|
|
2866
|
-
function
|
|
2926
|
+
function wa(t) {
|
|
2867
2927
|
var e;
|
|
2868
2928
|
const a = t.tagName.toLowerCase();
|
|
2869
|
-
if (
|
|
2929
|
+
if (ya.has(a))
|
|
2870
2930
|
return "none";
|
|
2871
2931
|
if (a === "a" && t.hasAttribute("href"))
|
|
2872
|
-
return
|
|
2932
|
+
return T["a[href]"];
|
|
2873
2933
|
if (a === "img" && t.getAttribute("alt") === "")
|
|
2874
|
-
return
|
|
2934
|
+
return T["img[alt='']"];
|
|
2875
2935
|
if (a === "input") {
|
|
2876
2936
|
const n = `input[type=${((e = t.getAttribute("type")) == null ? void 0 : e.toLowerCase()) || "text"}]`;
|
|
2877
|
-
return n in
|
|
2937
|
+
return n in T ? T[n] : "none";
|
|
2878
2938
|
}
|
|
2879
|
-
return
|
|
2939
|
+
return T[a] || "any";
|
|
2880
2940
|
}
|
|
2881
2941
|
const Aa = {
|
|
2882
2942
|
id: "aria-allowed-role",
|
|
@@ -2894,17 +2954,17 @@ const Aa = {
|
|
|
2894
2954
|
if (!n) continue;
|
|
2895
2955
|
const r = ue(i);
|
|
2896
2956
|
if (r && n === r) continue;
|
|
2897
|
-
const o =
|
|
2957
|
+
const o = wa(i);
|
|
2898
2958
|
o === "none" ? a.push({
|
|
2899
2959
|
ruleId: "aria-allowed-role",
|
|
2900
|
-
selector:
|
|
2901
|
-
html:
|
|
2960
|
+
selector: m(i),
|
|
2961
|
+
html: u(i),
|
|
2902
2962
|
impact: "minor",
|
|
2903
2963
|
message: `Element <${i.tagName.toLowerCase()}> should not have an explicit role.`
|
|
2904
2964
|
}) : o !== "any" && !o.has(n) && a.push({
|
|
2905
2965
|
ruleId: "aria-allowed-role",
|
|
2906
|
-
selector:
|
|
2907
|
-
html:
|
|
2966
|
+
selector: m(i),
|
|
2967
|
+
html: u(i),
|
|
2908
2968
|
impact: "minor",
|
|
2909
2969
|
message: `Role "${n}" is not allowed on element <${i.tagName.toLowerCase()}>.`
|
|
2910
2970
|
});
|
|
@@ -2942,25 +3002,25 @@ const Aa = {
|
|
|
2942
3002
|
tab: ["tablist"],
|
|
2943
3003
|
treeitem: ["tree", "group"]
|
|
2944
3004
|
};
|
|
2945
|
-
function
|
|
3005
|
+
function xa(t, a) {
|
|
2946
3006
|
var r;
|
|
2947
3007
|
const e = ((r = t.getAttribute("aria-owns")) == null ? void 0 : r.split(/\s+/)) || [], i = t.ownerDocument, n = /* @__PURE__ */ new Set();
|
|
2948
3008
|
for (const o of t.querySelectorAll("*")) {
|
|
2949
|
-
const s =
|
|
3009
|
+
const s = L(o);
|
|
2950
3010
|
s && !p(o) && n.add(s);
|
|
2951
3011
|
}
|
|
2952
3012
|
for (const o of e) {
|
|
2953
3013
|
const s = i.getElementById(o);
|
|
2954
3014
|
if (s) {
|
|
2955
|
-
const
|
|
2956
|
-
|
|
3015
|
+
const l = L(s);
|
|
3016
|
+
l && !p(s) && n.add(l);
|
|
2957
3017
|
}
|
|
2958
3018
|
}
|
|
2959
3019
|
for (const o of a)
|
|
2960
|
-
if (!o.some((
|
|
3020
|
+
if (!o.some((l) => n.has(l))) return !1;
|
|
2961
3021
|
return !0;
|
|
2962
3022
|
}
|
|
2963
|
-
const
|
|
3023
|
+
const Sa = {
|
|
2964
3024
|
id: "aria-required-children",
|
|
2965
3025
|
wcag: ["4.1.2"],
|
|
2966
3026
|
level: "A",
|
|
@@ -2975,12 +3035,12 @@ const xa = {
|
|
|
2975
3035
|
const n = (e = i.getAttribute("role")) == null ? void 0 : e.trim().toLowerCase();
|
|
2976
3036
|
if (!n || !(n in ae)) continue;
|
|
2977
3037
|
const r = ae[n];
|
|
2978
|
-
if (!
|
|
3038
|
+
if (!xa(i, r)) {
|
|
2979
3039
|
const o = r.map((s) => s.join(" or ")).join(", ");
|
|
2980
3040
|
a.push({
|
|
2981
3041
|
ruleId: "aria-required-children",
|
|
2982
|
-
selector:
|
|
2983
|
-
html:
|
|
3042
|
+
selector: m(i),
|
|
3043
|
+
html: u(i),
|
|
2984
3044
|
impact: "critical",
|
|
2985
3045
|
message: `Role "${n}" requires children with role: ${o}.`
|
|
2986
3046
|
});
|
|
@@ -3005,8 +3065,8 @@ const xa = {
|
|
|
3005
3065
|
const r = ie[n];
|
|
3006
3066
|
let o = i.parentElement, s = !1;
|
|
3007
3067
|
for (; o && o !== t.documentElement; ) {
|
|
3008
|
-
const
|
|
3009
|
-
if (
|
|
3068
|
+
const l = L(o);
|
|
3069
|
+
if (l && r.includes(l)) {
|
|
3010
3070
|
s = !0;
|
|
3011
3071
|
break;
|
|
3012
3072
|
}
|
|
@@ -3014,8 +3074,8 @@ const xa = {
|
|
|
3014
3074
|
}
|
|
3015
3075
|
s || a.push({
|
|
3016
3076
|
ruleId: "aria-required-parent",
|
|
3017
|
-
selector:
|
|
3018
|
-
html:
|
|
3077
|
+
selector: m(i),
|
|
3078
|
+
html: u(i),
|
|
3019
3079
|
impact: "critical",
|
|
3020
3080
|
message: `Role "${n}" must be contained within: ${r.join(", ")}.`
|
|
3021
3081
|
});
|
|
@@ -3051,7 +3111,7 @@ function Ia(t) {
|
|
|
3051
3111
|
}
|
|
3052
3112
|
return !0;
|
|
3053
3113
|
}
|
|
3054
|
-
const
|
|
3114
|
+
const Ta = {
|
|
3055
3115
|
id: "aria-hidden-body",
|
|
3056
3116
|
selector: 'body[aria-hidden="true"]',
|
|
3057
3117
|
check: { type: "selector-exists" },
|
|
@@ -3063,13 +3123,13 @@ const Ea = {
|
|
|
3063
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.",
|
|
3064
3124
|
prompt: "Instruct to remove aria-hidden='true' from the body element.",
|
|
3065
3125
|
skipAriaHidden: !1
|
|
3066
|
-
},
|
|
3126
|
+
}, Ea = k(Ta), Ca = {
|
|
3067
3127
|
id: "aria-hidden-focus",
|
|
3068
3128
|
wcag: ["4.1.2"],
|
|
3069
3129
|
level: "A",
|
|
3070
3130
|
description: "Elements with aria-hidden='true' must not contain focusable elements.",
|
|
3071
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.",
|
|
3072
|
-
prompt: "
|
|
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.",
|
|
3073
3133
|
run(t) {
|
|
3074
3134
|
const a = [];
|
|
3075
3135
|
for (const e of t.querySelectorAll('[aria-hidden="true"]')) {
|
|
@@ -3078,19 +3138,25 @@ const Ea = {
|
|
|
3078
3138
|
e.matches(ne) && i.push(e);
|
|
3079
3139
|
for (const n of i)
|
|
3080
3140
|
if (n instanceof HTMLElement) {
|
|
3081
|
-
|
|
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"]');
|
|
3082
3147
|
a.push({
|
|
3083
3148
|
ruleId: "aria-hidden-focus",
|
|
3084
|
-
selector:
|
|
3085
|
-
html:
|
|
3149
|
+
selector: m(n),
|
|
3150
|
+
html: u(n),
|
|
3086
3151
|
impact: "serious",
|
|
3087
|
-
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"}`
|
|
3088
3154
|
});
|
|
3089
3155
|
}
|
|
3090
3156
|
}
|
|
3091
3157
|
return a;
|
|
3092
3158
|
}
|
|
3093
|
-
},
|
|
3159
|
+
}, La = {
|
|
3094
3160
|
id: "aria-command-name",
|
|
3095
3161
|
wcag: ["4.1.2"],
|
|
3096
3162
|
level: "A",
|
|
@@ -3107,8 +3173,8 @@ const Ea = {
|
|
|
3107
3173
|
if ((e = r == null ? void 0 : r.getAttribute("alt")) != null && e.trim()) continue;
|
|
3108
3174
|
a.push({
|
|
3109
3175
|
ruleId: "aria-command-name",
|
|
3110
|
-
selector:
|
|
3111
|
-
html:
|
|
3176
|
+
selector: m(i),
|
|
3177
|
+
html: u(i),
|
|
3112
3178
|
impact: "serious",
|
|
3113
3179
|
message: "ARIA command has no accessible name."
|
|
3114
3180
|
});
|
|
@@ -3129,8 +3195,8 @@ const Ea = {
|
|
|
3129
3195
|
if (p(i) || i.matches("input, select, textarea")) continue;
|
|
3130
3196
|
v(i) || a.push({
|
|
3131
3197
|
ruleId: "aria-input-field-name",
|
|
3132
|
-
selector:
|
|
3133
|
-
html:
|
|
3198
|
+
selector: m(i),
|
|
3199
|
+
html: u(i),
|
|
3134
3200
|
impact: "serious",
|
|
3135
3201
|
message: "ARIA input field has no accessible name."
|
|
3136
3202
|
});
|
|
@@ -3150,8 +3216,8 @@ const Ea = {
|
|
|
3150
3216
|
if (p(i) || i.matches('input[type="checkbox"], input[type="radio"]')) continue;
|
|
3151
3217
|
v(i) || a.push({
|
|
3152
3218
|
ruleId: "aria-toggle-field-name",
|
|
3153
|
-
selector:
|
|
3154
|
-
html:
|
|
3219
|
+
selector: m(i),
|
|
3220
|
+
html: u(i),
|
|
3155
3221
|
impact: "serious",
|
|
3156
3222
|
message: "ARIA toggle field has no accessible name."
|
|
3157
3223
|
});
|
|
@@ -3171,15 +3237,15 @@ const Ea = {
|
|
|
3171
3237
|
if (p(e)) continue;
|
|
3172
3238
|
v(e) || a.push({
|
|
3173
3239
|
ruleId: "aria-meter-name",
|
|
3174
|
-
selector:
|
|
3175
|
-
html:
|
|
3240
|
+
selector: m(e),
|
|
3241
|
+
html: u(e),
|
|
3176
3242
|
impact: "serious",
|
|
3177
3243
|
message: "Meter has no accessible name."
|
|
3178
3244
|
});
|
|
3179
3245
|
}
|
|
3180
3246
|
return a;
|
|
3181
3247
|
}
|
|
3182
|
-
},
|
|
3248
|
+
}, $a = {
|
|
3183
3249
|
id: "aria-progressbar-name",
|
|
3184
3250
|
wcag: ["4.1.2"],
|
|
3185
3251
|
level: "A",
|
|
@@ -3192,15 +3258,15 @@ const Ea = {
|
|
|
3192
3258
|
if (p(e)) continue;
|
|
3193
3259
|
v(e) || a.push({
|
|
3194
3260
|
ruleId: "aria-progressbar-name",
|
|
3195
|
-
selector:
|
|
3196
|
-
html:
|
|
3261
|
+
selector: m(e),
|
|
3262
|
+
html: u(e),
|
|
3197
3263
|
impact: "serious",
|
|
3198
3264
|
message: "Progressbar has no accessible name."
|
|
3199
3265
|
});
|
|
3200
3266
|
}
|
|
3201
3267
|
return a;
|
|
3202
3268
|
}
|
|
3203
|
-
},
|
|
3269
|
+
}, Ma = {
|
|
3204
3270
|
id: "aria-dialog-name",
|
|
3205
3271
|
wcag: ["4.1.2"],
|
|
3206
3272
|
level: "A",
|
|
@@ -3213,8 +3279,8 @@ const Ea = {
|
|
|
3213
3279
|
if (p(e)) continue;
|
|
3214
3280
|
v(e) || a.push({
|
|
3215
3281
|
ruleId: "aria-dialog-name",
|
|
3216
|
-
selector:
|
|
3217
|
-
html:
|
|
3282
|
+
selector: m(e),
|
|
3283
|
+
html: u(e),
|
|
3218
3284
|
impact: "serious",
|
|
3219
3285
|
message: "Dialog has no accessible name."
|
|
3220
3286
|
});
|
|
@@ -3234,8 +3300,8 @@ const Ea = {
|
|
|
3234
3300
|
if (p(e)) continue;
|
|
3235
3301
|
v(e) || a.push({
|
|
3236
3302
|
ruleId: "aria-tooltip-name",
|
|
3237
|
-
selector:
|
|
3238
|
-
html:
|
|
3303
|
+
selector: m(e),
|
|
3304
|
+
html: u(e),
|
|
3239
3305
|
impact: "serious",
|
|
3240
3306
|
message: "Tooltip has no accessible name."
|
|
3241
3307
|
});
|
|
@@ -3255,8 +3321,8 @@ const Ea = {
|
|
|
3255
3321
|
if (p(e)) continue;
|
|
3256
3322
|
v(e) || a.push({
|
|
3257
3323
|
ruleId: "aria-treeitem-name",
|
|
3258
|
-
selector:
|
|
3259
|
-
html:
|
|
3324
|
+
selector: m(e),
|
|
3325
|
+
html: u(e),
|
|
3260
3326
|
impact: "serious",
|
|
3261
3327
|
message: "Treeitem has no accessible name."
|
|
3262
3328
|
});
|
|
@@ -3273,7 +3339,7 @@ const Ea = {
|
|
|
3273
3339
|
run(t) {
|
|
3274
3340
|
return z(t).prohibitedAttr;
|
|
3275
3341
|
}
|
|
3276
|
-
},
|
|
3342
|
+
}, Fa = [
|
|
3277
3343
|
"a[href]",
|
|
3278
3344
|
"button:not([disabled])",
|
|
3279
3345
|
'input:not([disabled]):not([type="hidden"])',
|
|
@@ -3297,7 +3363,7 @@ const Ea = {
|
|
|
3297
3363
|
];
|
|
3298
3364
|
function re(t) {
|
|
3299
3365
|
const a = [];
|
|
3300
|
-
t.matches(
|
|
3366
|
+
t.matches(Fa) && a.push("element is focusable");
|
|
3301
3367
|
for (const e of Ba)
|
|
3302
3368
|
if (t.hasAttribute(e)) {
|
|
3303
3369
|
a.push(`has ${e}`);
|
|
@@ -3305,7 +3371,7 @@ function re(t) {
|
|
|
3305
3371
|
}
|
|
3306
3372
|
return (t.hasAttribute("aria-label") || t.hasAttribute("aria-labelledby")) && a.push("has accessible name"), a;
|
|
3307
3373
|
}
|
|
3308
|
-
const
|
|
3374
|
+
const Wa = {
|
|
3309
3375
|
id: "presentation-role-conflict",
|
|
3310
3376
|
wcag: ["4.1.2"],
|
|
3311
3377
|
level: "A",
|
|
@@ -3319,8 +3385,8 @@ const Fa = {
|
|
|
3319
3385
|
const i = re(e);
|
|
3320
3386
|
i.length > 0 && a.push({
|
|
3321
3387
|
ruleId: "presentation-role-conflict",
|
|
3322
|
-
selector:
|
|
3323
|
-
html:
|
|
3388
|
+
selector: m(e),
|
|
3389
|
+
html: u(e),
|
|
3324
3390
|
impact: "serious",
|
|
3325
3391
|
message: `Presentation role conflicts with: ${i.join(", ")}. The role will be ignored.`
|
|
3326
3392
|
});
|
|
@@ -3330,8 +3396,8 @@ const Fa = {
|
|
|
3330
3396
|
const i = re(e);
|
|
3331
3397
|
i.length > 0 && a.push({
|
|
3332
3398
|
ruleId: "presentation-role-conflict",
|
|
3333
|
-
selector:
|
|
3334
|
-
html:
|
|
3399
|
+
selector: m(e),
|
|
3400
|
+
html: u(e),
|
|
3335
3401
|
impact: "serious",
|
|
3336
3402
|
message: `Element with implicit presentation role (alt="") conflicts with: ${i.join(", ")}. The decorative role will be ignored.`
|
|
3337
3403
|
});
|
|
@@ -3351,8 +3417,8 @@ const Fa = {
|
|
|
3351
3417
|
if (p(e)) continue;
|
|
3352
3418
|
v(e) || a.push({
|
|
3353
3419
|
ruleId: "summary-name",
|
|
3354
|
-
selector:
|
|
3355
|
-
html:
|
|
3420
|
+
selector: m(e),
|
|
3421
|
+
html: u(e),
|
|
3356
3422
|
impact: "serious",
|
|
3357
3423
|
message: "<summary> element has no accessible name. Add descriptive text."
|
|
3358
3424
|
});
|
|
@@ -3360,7 +3426,7 @@ const Fa = {
|
|
|
3360
3426
|
return a;
|
|
3361
3427
|
}
|
|
3362
3428
|
};
|
|
3363
|
-
function
|
|
3429
|
+
function Pa(t) {
|
|
3364
3430
|
var n, r;
|
|
3365
3431
|
const a = [], e = t.getAttribute("href");
|
|
3366
3432
|
e && a.push(`href: ${e}`);
|
|
@@ -3377,7 +3443,7 @@ function ja(t) {
|
|
|
3377
3443
|
return a.length > 0 ? a.join(`
|
|
3378
3444
|
`) : void 0;
|
|
3379
3445
|
}
|
|
3380
|
-
const
|
|
3446
|
+
const ja = {
|
|
3381
3447
|
id: "link-name",
|
|
3382
3448
|
wcag: ["2.4.4", "4.1.2"],
|
|
3383
3449
|
level: "A",
|
|
@@ -3390,11 +3456,11 @@ const Pa = {
|
|
|
3390
3456
|
if (p(e)) continue;
|
|
3391
3457
|
v(e) || a.push({
|
|
3392
3458
|
ruleId: "link-name",
|
|
3393
|
-
selector:
|
|
3394
|
-
html:
|
|
3459
|
+
selector: m(e),
|
|
3460
|
+
html: u(e),
|
|
3395
3461
|
impact: "serious",
|
|
3396
3462
|
message: "Link has no discernible text.",
|
|
3397
|
-
context:
|
|
3463
|
+
context: Pa(e)
|
|
3398
3464
|
});
|
|
3399
3465
|
}
|
|
3400
3466
|
return a;
|
|
@@ -3412,13 +3478,13 @@ const Pa = {
|
|
|
3412
3478
|
for (const i of e) {
|
|
3413
3479
|
const n = i.getAttribute("href");
|
|
3414
3480
|
if (!n || n === "#") continue;
|
|
3415
|
-
const r =
|
|
3481
|
+
const r = w(i).toLowerCase();
|
|
3416
3482
|
if (!(r.includes("skip") || r.includes("jump") || r.includes("main content") || r.includes("navigation"))) continue;
|
|
3417
3483
|
const s = n.slice(1);
|
|
3418
3484
|
t.getElementById(s) || a.push({
|
|
3419
3485
|
ruleId: "skip-link",
|
|
3420
|
-
selector:
|
|
3421
|
-
html:
|
|
3486
|
+
selector: m(i),
|
|
3487
|
+
html: u(i),
|
|
3422
3488
|
impact: "moderate",
|
|
3423
3489
|
message: `Skip link points to "#${s}" which does not exist on the page.`
|
|
3424
3490
|
});
|
|
@@ -3454,20 +3520,20 @@ function Xa(t) {
|
|
|
3454
3520
|
t,
|
|
3455
3521
|
NodeFilter.SHOW_TEXT
|
|
3456
3522
|
);
|
|
3457
|
-
let e;
|
|
3458
|
-
for (;
|
|
3459
|
-
if (!
|
|
3460
|
-
let
|
|
3461
|
-
for (;
|
|
3462
|
-
if (
|
|
3463
|
-
|
|
3523
|
+
let e = "", i;
|
|
3524
|
+
for (; i = a.nextNode(); ) {
|
|
3525
|
+
if (!i.data.trim()) continue;
|
|
3526
|
+
let n = i.parentElement, r = !1;
|
|
3527
|
+
for (; n && n !== t; ) {
|
|
3528
|
+
if (n.tagName === "A") {
|
|
3529
|
+
r = !0;
|
|
3464
3530
|
break;
|
|
3465
3531
|
}
|
|
3466
|
-
|
|
3532
|
+
n = n.parentElement;
|
|
3467
3533
|
}
|
|
3468
|
-
|
|
3534
|
+
r || (e += i.data);
|
|
3469
3535
|
}
|
|
3470
|
-
return
|
|
3536
|
+
return /[a-zA-Z\u00C0-\u024F]{2,}/.test(e);
|
|
3471
3537
|
}
|
|
3472
3538
|
function Ya(t, a) {
|
|
3473
3539
|
const e = t.ownerDocument.createTreeWalker(
|
|
@@ -3500,14 +3566,14 @@ function Ka(t, a) {
|
|
|
3500
3566
|
const o = parseFloat(t.outlineWidth) || 0, s = t.outlineStyle || "";
|
|
3501
3567
|
if (o > 0 && s !== "none")
|
|
3502
3568
|
return !0;
|
|
3503
|
-
const
|
|
3504
|
-
if (
|
|
3569
|
+
const l = t.backgroundImage || "";
|
|
3570
|
+
if (l && l !== "none" && l !== "initial")
|
|
3505
3571
|
return !0;
|
|
3506
|
-
const h = oe(t.fontWeight),
|
|
3507
|
-
if (Math.abs(h -
|
|
3572
|
+
const h = oe(t.fontWeight), c = oe(a.fontWeight);
|
|
3573
|
+
if (Math.abs(h - c) >= 300 || t.fontStyle !== a.fontStyle)
|
|
3508
3574
|
return !0;
|
|
3509
|
-
const
|
|
3510
|
-
return g > 0 &&
|
|
3575
|
+
const d = parseFloat(t.fontSize) || 16, g = parseFloat(a.fontSize) || 16;
|
|
3576
|
+
return g > 0 && d / g >= 1.2;
|
|
3511
3577
|
}
|
|
3512
3578
|
function oe(t) {
|
|
3513
3579
|
return t === "bold" ? 700 : t === "normal" ? 400 : parseInt(t) || 400;
|
|
@@ -3515,7 +3581,7 @@ function oe(t) {
|
|
|
3515
3581
|
function se(t, a, e) {
|
|
3516
3582
|
return "#" + [t, a, e].map((i) => i.toString(16).padStart(2, "0")).join("");
|
|
3517
3583
|
}
|
|
3518
|
-
const
|
|
3584
|
+
const Ja = {
|
|
3519
3585
|
id: "link-in-text-block",
|
|
3520
3586
|
wcag: ["1.4.1"],
|
|
3521
3587
|
level: "A",
|
|
@@ -3525,22 +3591,22 @@ const Qa = {
|
|
|
3525
3591
|
run(t) {
|
|
3526
3592
|
const a = [];
|
|
3527
3593
|
for (const e of t.querySelectorAll("a[href]")) {
|
|
3528
|
-
if (p(e) || !
|
|
3594
|
+
if (p(e) || !w(e).trim() || e.closest('nav, header, footer, [role="navigation"], [role="banner"], [role="contentinfo"]')) continue;
|
|
3529
3595
|
const i = A(e), n = i.display || "inline";
|
|
3530
3596
|
if (!Ua.has(n)) continue;
|
|
3531
3597
|
const r = Ga(e);
|
|
3532
3598
|
if (!r) continue;
|
|
3533
3599
|
const o = A(r);
|
|
3534
3600
|
if (Ka(i, o)) continue;
|
|
3535
|
-
const s = R(i.color),
|
|
3536
|
-
if (!s || !
|
|
3537
|
-
const h = q(...s),
|
|
3538
|
-
if (
|
|
3539
|
-
const g = se(...s), b = se(...
|
|
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`;
|
|
3540
3606
|
a.push({
|
|
3541
3607
|
ruleId: "link-in-text-block",
|
|
3542
|
-
selector:
|
|
3543
|
-
html:
|
|
3608
|
+
selector: m(e),
|
|
3609
|
+
html: u(e),
|
|
3544
3610
|
impact: "serious",
|
|
3545
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.",
|
|
3546
3612
|
context: f
|
|
@@ -3548,30 +3614,39 @@ const Qa = {
|
|
|
3548
3614
|
}
|
|
3549
3615
|
return a;
|
|
3550
3616
|
}
|
|
3551
|
-
},
|
|
3617
|
+
}, Qa = {
|
|
3552
3618
|
id: "html-has-lang",
|
|
3553
3619
|
wcag: ["3.1.1"],
|
|
3554
3620
|
level: "A",
|
|
3555
3621
|
description: "The <html> element must have a lang attribute.",
|
|
3556
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).",
|
|
3557
|
-
prompt:
|
|
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="...">.`,
|
|
3558
3624
|
run(t) {
|
|
3559
|
-
var e;
|
|
3625
|
+
var e, i;
|
|
3560
3626
|
const a = t.documentElement;
|
|
3561
3627
|
if (a.tagName.toLowerCase() !== "html") return [];
|
|
3562
3628
|
if (!t.doctype && t.body) {
|
|
3563
|
-
const
|
|
3564
|
-
if (
|
|
3565
|
-
(
|
|
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"
|
|
3566
3632
|
)) return [];
|
|
3567
3633
|
}
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
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 [];
|
|
3575
3650
|
}
|
|
3576
3651
|
}, Za = new Set(
|
|
3577
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(" ")
|
|
@@ -3596,7 +3671,7 @@ const ai = {
|
|
|
3596
3671
|
return a && !he(a) ? [{
|
|
3597
3672
|
ruleId: "html-lang-valid",
|
|
3598
3673
|
selector: "html",
|
|
3599
|
-
html:
|
|
3674
|
+
html: u(t.documentElement),
|
|
3600
3675
|
impact: "serious",
|
|
3601
3676
|
message: `Invalid lang attribute value "${a}".`
|
|
3602
3677
|
}] : [];
|
|
@@ -3649,8 +3724,8 @@ const ii = {
|
|
|
3649
3724
|
if (i && !n) {
|
|
3650
3725
|
le(e) && a.push({
|
|
3651
3726
|
ruleId: "valid-lang",
|
|
3652
|
-
selector:
|
|
3653
|
-
html:
|
|
3727
|
+
selector: m(e),
|
|
3728
|
+
html: u(e),
|
|
3654
3729
|
impact: "serious",
|
|
3655
3730
|
message: "Empty lang attribute value."
|
|
3656
3731
|
});
|
|
@@ -3658,8 +3733,8 @@ const ii = {
|
|
|
3658
3733
|
}
|
|
3659
3734
|
n && le(e) && (he(n) || a.push({
|
|
3660
3735
|
ruleId: "valid-lang",
|
|
3661
|
-
selector:
|
|
3662
|
-
html:
|
|
3736
|
+
selector: m(e),
|
|
3737
|
+
html: u(e),
|
|
3663
3738
|
impact: "serious",
|
|
3664
3739
|
message: `Invalid lang attribute value "${n}".`
|
|
3665
3740
|
}));
|
|
@@ -3682,7 +3757,7 @@ const ii = {
|
|
|
3682
3757
|
return [{
|
|
3683
3758
|
ruleId: "html-xml-lang-mismatch",
|
|
3684
3759
|
selector: "html",
|
|
3685
|
-
html:
|
|
3760
|
+
html: u(a),
|
|
3686
3761
|
impact: "moderate",
|
|
3687
3762
|
message: `lang="${e}" and xml:lang="${i}" do not match.`
|
|
3688
3763
|
}];
|
|
@@ -3707,8 +3782,8 @@ const ii = {
|
|
|
3707
3782
|
if (o === n) {
|
|
3708
3783
|
a.push({
|
|
3709
3784
|
ruleId: "td-headers-attr",
|
|
3710
|
-
selector:
|
|
3711
|
-
html:
|
|
3785
|
+
selector: m(e),
|
|
3786
|
+
html: u(e),
|
|
3712
3787
|
impact: "serious",
|
|
3713
3788
|
message: `Headers attribute references the cell itself ("${o}").`
|
|
3714
3789
|
});
|
|
@@ -3717,8 +3792,8 @@ const ii = {
|
|
|
3717
3792
|
if (!i.querySelector(`th#${CSS.escape(o)}, td#${CSS.escape(o)}`)) {
|
|
3718
3793
|
a.push({
|
|
3719
3794
|
ruleId: "td-headers-attr",
|
|
3720
|
-
selector:
|
|
3721
|
-
html:
|
|
3795
|
+
selector: m(e),
|
|
3796
|
+
html: u(e),
|
|
3722
3797
|
impact: "serious",
|
|
3723
3798
|
message: `Headers attribute references non-existent ID "${o}".`
|
|
3724
3799
|
});
|
|
@@ -3742,8 +3817,8 @@ const ii = {
|
|
|
3742
3817
|
const i = e.querySelectorAll("th"), n = e.querySelectorAll("td");
|
|
3743
3818
|
i.length > 0 && n.length === 0 && a.push({
|
|
3744
3819
|
ruleId: "th-has-data-cells",
|
|
3745
|
-
selector:
|
|
3746
|
-
html:
|
|
3820
|
+
selector: m(e),
|
|
3821
|
+
html: u(e),
|
|
3747
3822
|
impact: "serious",
|
|
3748
3823
|
message: "Table has header cells but no data cells."
|
|
3749
3824
|
});
|
|
@@ -3764,36 +3839,36 @@ const ii = {
|
|
|
3764
3839
|
if (p(n) || n.getAttribute("role") === "presentation" || n.getAttribute("role") === "none") continue;
|
|
3765
3840
|
const r = n.querySelectorAll("tr"), o = r.length;
|
|
3766
3841
|
let s = 0;
|
|
3767
|
-
for (const
|
|
3768
|
-
const g =
|
|
3842
|
+
for (const d of r) {
|
|
3843
|
+
const g = d.querySelectorAll("td, th");
|
|
3769
3844
|
let b = 0;
|
|
3770
3845
|
for (const f of g)
|
|
3771
3846
|
b += parseInt(f.getAttribute("colspan") || "1", 10);
|
|
3772
3847
|
s = Math.max(s, b);
|
|
3773
3848
|
}
|
|
3774
3849
|
if (o <= 3 && s <= 3) continue;
|
|
3775
|
-
const
|
|
3776
|
-
if (
|
|
3777
|
-
for (const
|
|
3778
|
-
if (p(
|
|
3779
|
-
const g =
|
|
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");
|
|
3780
3855
|
if (!g) continue;
|
|
3781
|
-
const b = g.querySelector("th") !== null, f = Array.from(g.children).indexOf(
|
|
3782
|
-
let
|
|
3856
|
+
const b = g.querySelector("th") !== null, f = Array.from(g.children).indexOf(d);
|
|
3857
|
+
let y = !1;
|
|
3783
3858
|
const I = n.querySelector("thead");
|
|
3784
3859
|
if (I) {
|
|
3785
|
-
const
|
|
3786
|
-
|
|
3860
|
+
const x = I.querySelector("tr");
|
|
3861
|
+
x && ((e = x.querySelectorAll("th, td")[f]) == null ? void 0 : e.tagName.toLowerCase()) === "th" && (y = !0);
|
|
3787
3862
|
}
|
|
3788
|
-
if (!
|
|
3789
|
-
const
|
|
3790
|
-
|
|
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);
|
|
3791
3866
|
}
|
|
3792
|
-
if (!b && !
|
|
3867
|
+
if (!b && !y && !h && !c) {
|
|
3793
3868
|
a.push({
|
|
3794
3869
|
ruleId: "td-has-header",
|
|
3795
|
-
selector:
|
|
3796
|
-
html:
|
|
3870
|
+
selector: m(d),
|
|
3871
|
+
html: u(d),
|
|
3797
3872
|
impact: "serious",
|
|
3798
3873
|
message: "Data cell has no associated header. Add th elements with scope, or headers attribute."
|
|
3799
3874
|
});
|
|
@@ -3818,8 +3893,8 @@ const ii = {
|
|
|
3818
3893
|
const r = (i = n.getAttribute("scope")) == null ? void 0 : i.toLowerCase();
|
|
3819
3894
|
r && !e.has(r) && a.push({
|
|
3820
3895
|
ruleId: "scope-attr-valid",
|
|
3821
|
-
selector:
|
|
3822
|
-
html:
|
|
3896
|
+
selector: m(n),
|
|
3897
|
+
html: u(n),
|
|
3823
3898
|
impact: "moderate",
|
|
3824
3899
|
message: `Invalid scope value "${r}". Use row, col, rowgroup, or colgroup.`
|
|
3825
3900
|
});
|
|
@@ -3841,8 +3916,8 @@ const ii = {
|
|
|
3841
3916
|
const i = e.closest("table");
|
|
3842
3917
|
(i == null ? void 0 : i.getAttribute("role")) === "presentation" || (i == null ? void 0 : i.getAttribute("role")) === "none" || v(e) || a.push({
|
|
3843
3918
|
ruleId: "empty-table-header",
|
|
3844
|
-
selector:
|
|
3845
|
-
html:
|
|
3919
|
+
selector: m(e),
|
|
3920
|
+
html: u(e),
|
|
3846
3921
|
impact: "minor",
|
|
3847
3922
|
message: "Table header cell is empty. Add text or use aria-label."
|
|
3848
3923
|
});
|
|
@@ -3873,25 +3948,25 @@ const ii = {
|
|
|
3873
3948
|
for (const [n, r] of i) {
|
|
3874
3949
|
if (r <= 1) continue;
|
|
3875
3950
|
const o = t.querySelectorAll(`#${CSS.escape(n)}`), s = t.querySelector(
|
|
3876
|
-
D.map((
|
|
3877
|
-
),
|
|
3951
|
+
D.map((c) => `[${c}~="${CSS.escape(n)}"]`).join(", ")
|
|
3952
|
+
), l = t.querySelector(`label[for="${CSS.escape(n)}"]`);
|
|
3878
3953
|
let h;
|
|
3879
3954
|
if (s) {
|
|
3880
|
-
const
|
|
3881
|
-
(
|
|
3955
|
+
const c = D.find(
|
|
3956
|
+
(d) => {
|
|
3882
3957
|
var g;
|
|
3883
|
-
return (g = s.getAttribute(
|
|
3958
|
+
return (g = s.getAttribute(d)) == null ? void 0 : g.split(/\s+/).includes(n);
|
|
3884
3959
|
}
|
|
3885
3960
|
);
|
|
3886
|
-
|
|
3887
|
-
} else
|
|
3961
|
+
c && (h = c);
|
|
3962
|
+
} else l && (h = "label[for]");
|
|
3888
3963
|
a.push({
|
|
3889
3964
|
ruleId: "duplicate-id-aria",
|
|
3890
|
-
selector:
|
|
3891
|
-
html:
|
|
3965
|
+
selector: m(o[1]),
|
|
3966
|
+
html: u(o[1]),
|
|
3892
3967
|
impact: "critical",
|
|
3893
3968
|
message: `Duplicate ID "${n}" referenced by ${h ?? "an accessibility attribute"}.`,
|
|
3894
|
-
context: `First element: ${
|
|
3969
|
+
context: `First element: ${u(o[0])}${h ? `
|
|
3895
3970
|
Referenced by: ${h}` : ""}`
|
|
3896
3971
|
});
|
|
3897
3972
|
}
|
|
@@ -3910,8 +3985,8 @@ Referenced by: ${h}` : ""}`
|
|
|
3910
3985
|
if (p(e) || e.hasAttribute("muted") || e.hasAttribute("autoplay")) continue;
|
|
3911
3986
|
e.querySelector('track[kind="captions"], track[kind="subtitles"]') || a.push({
|
|
3912
3987
|
ruleId: "video-caption",
|
|
3913
|
-
selector:
|
|
3914
|
-
html:
|
|
3988
|
+
selector: m(e),
|
|
3989
|
+
html: u(e),
|
|
3915
3990
|
impact: "critical",
|
|
3916
3991
|
message: "Video element has no captions track."
|
|
3917
3992
|
});
|
|
@@ -3932,8 +4007,8 @@ Referenced by: ${h}` : ""}`
|
|
|
3932
4007
|
const n = e.parentElement;
|
|
3933
4008
|
n && n.querySelector('a[href*="transcript"], a[href*="text"]') || a.push({
|
|
3934
4009
|
ruleId: "audio-caption",
|
|
3935
|
-
selector:
|
|
3936
|
-
html:
|
|
4010
|
+
selector: m(e),
|
|
4011
|
+
html: u(e),
|
|
3937
4012
|
impact: "critical",
|
|
3938
4013
|
message: "Audio element has no transcript or text alternative. Add a transcript or track element."
|
|
3939
4014
|
});
|
|
@@ -4016,24 +4091,24 @@ const vi = {
|
|
|
4016
4091
|
if (!o || n.has(o) || (n.add(o), hi.has(o.tagName)) || pi(o) || gi(o, t) || fi(o)) continue;
|
|
4017
4092
|
const s = A(o);
|
|
4018
4093
|
if (parseFloat(s.opacity) === 0) continue;
|
|
4019
|
-
const
|
|
4020
|
-
if (
|
|
4094
|
+
const l = s.textShadow;
|
|
4095
|
+
if (l && l !== "none" && l !== "initial") continue;
|
|
4021
4096
|
const h = R(s.color);
|
|
4022
4097
|
if (!h) continue;
|
|
4023
|
-
const
|
|
4024
|
-
if (
|
|
4025
|
-
const
|
|
4026
|
-
if (!
|
|
4027
|
-
const g = q(h[0], h[1], h[2]), b = q(
|
|
4028
|
-
if (f <
|
|
4029
|
-
const I = Math.round(f * 100) / 100,
|
|
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 || Be(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);
|
|
4030
4105
|
a.push({
|
|
4031
4106
|
ruleId: "color-contrast",
|
|
4032
|
-
selector:
|
|
4033
|
-
html:
|
|
4107
|
+
selector: m(o),
|
|
4108
|
+
html: u(o),
|
|
4034
4109
|
impact: "serious",
|
|
4035
|
-
message: `Insufficient color contrast ratio of ${I}:1 (required ${
|
|
4036
|
-
context: `foreground: ${
|
|
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`
|
|
4037
4112
|
});
|
|
4038
4113
|
}
|
|
4039
4114
|
}
|
|
@@ -4042,8 +4117,8 @@ const vi = {
|
|
|
4042
4117
|
}, pe = [
|
|
4043
4118
|
// Document Structure
|
|
4044
4119
|
Kt,
|
|
4045
|
-
Qt,
|
|
4046
4120
|
Jt,
|
|
4121
|
+
Qt,
|
|
4047
4122
|
Zt,
|
|
4048
4123
|
ea,
|
|
4049
4124
|
aa,
|
|
@@ -4051,13 +4126,13 @@ const vi = {
|
|
|
4051
4126
|
ra,
|
|
4052
4127
|
sa,
|
|
4053
4128
|
// Images
|
|
4054
|
-
|
|
4129
|
+
je,
|
|
4055
4130
|
ze,
|
|
4056
4131
|
Ue,
|
|
4057
4132
|
Ge,
|
|
4058
4133
|
Ye,
|
|
4059
4134
|
Ke,
|
|
4060
|
-
|
|
4135
|
+
Qe,
|
|
4061
4136
|
Ze,
|
|
4062
4137
|
it,
|
|
4063
4138
|
// Forms
|
|
@@ -4066,27 +4141,27 @@ const vi = {
|
|
|
4066
4141
|
ut,
|
|
4067
4142
|
dt,
|
|
4068
4143
|
vt,
|
|
4069
|
-
wt,
|
|
4070
4144
|
yt,
|
|
4145
|
+
wt,
|
|
4071
4146
|
// Keyboard
|
|
4072
|
-
|
|
4147
|
+
xt,
|
|
4073
4148
|
kt,
|
|
4074
4149
|
Rt,
|
|
4075
4150
|
Nt,
|
|
4076
|
-
Mt,
|
|
4077
|
-
// Structure
|
|
4078
4151
|
$t,
|
|
4152
|
+
// Structure
|
|
4153
|
+
Mt,
|
|
4079
4154
|
ta,
|
|
4080
4155
|
la,
|
|
4081
4156
|
Ht,
|
|
4082
4157
|
Dt,
|
|
4083
4158
|
Ot,
|
|
4084
|
-
Wt,
|
|
4085
|
-
Bt,
|
|
4086
4159
|
Ft,
|
|
4160
|
+
Bt,
|
|
4161
|
+
Wt,
|
|
4087
4162
|
_t,
|
|
4088
|
-
jt,
|
|
4089
4163
|
Pt,
|
|
4164
|
+
jt,
|
|
4090
4165
|
Vt,
|
|
4091
4166
|
Ut,
|
|
4092
4167
|
Gt,
|
|
@@ -4098,28 +4173,28 @@ const vi = {
|
|
|
4098
4173
|
ha,
|
|
4099
4174
|
va,
|
|
4100
4175
|
Aa,
|
|
4101
|
-
|
|
4176
|
+
Sa,
|
|
4102
4177
|
ka,
|
|
4103
|
-
|
|
4104
|
-
La,
|
|
4178
|
+
Ea,
|
|
4105
4179
|
Ca,
|
|
4180
|
+
La,
|
|
4106
4181
|
qa,
|
|
4107
4182
|
Ra,
|
|
4108
4183
|
Na,
|
|
4109
|
-
Ma,
|
|
4110
4184
|
$a,
|
|
4185
|
+
Ma,
|
|
4111
4186
|
Ha,
|
|
4112
4187
|
Da,
|
|
4113
4188
|
Oa,
|
|
4114
|
-
|
|
4189
|
+
Wa,
|
|
4115
4190
|
ga,
|
|
4116
4191
|
_a,
|
|
4117
4192
|
// Links
|
|
4118
|
-
|
|
4193
|
+
ja,
|
|
4119
4194
|
Va,
|
|
4120
|
-
Qa,
|
|
4121
|
-
// Language
|
|
4122
4195
|
Ja,
|
|
4196
|
+
// Language
|
|
4197
|
+
Qa,
|
|
4123
4198
|
ai,
|
|
4124
4199
|
ii,
|
|
4125
4200
|
ni,
|
|
@@ -4138,13 +4213,13 @@ const vi = {
|
|
|
4138
4213
|
vi
|
|
4139
4214
|
];
|
|
4140
4215
|
let U = [], ge = /* @__PURE__ */ new Set();
|
|
4141
|
-
function
|
|
4216
|
+
function xi(t) {
|
|
4142
4217
|
t.additionalRules && (U = t.additionalRules), t.disabledRules && (ge = new Set(t.disabledRules));
|
|
4143
4218
|
}
|
|
4144
4219
|
function be() {
|
|
4145
4220
|
return pe.filter((a) => !ge.has(a.id)).concat(U);
|
|
4146
4221
|
}
|
|
4147
|
-
function
|
|
4222
|
+
function Si(t) {
|
|
4148
4223
|
fe();
|
|
4149
4224
|
const a = be(), e = [];
|
|
4150
4225
|
let i = 0;
|
|
@@ -4166,7 +4241,7 @@ function xi(t) {
|
|
|
4166
4241
|
};
|
|
4167
4242
|
}
|
|
4168
4243
|
function fe() {
|
|
4169
|
-
|
|
4244
|
+
Se(), ve(), ye(), He(), Me(), Ie();
|
|
4170
4245
|
}
|
|
4171
4246
|
function ki(t) {
|
|
4172
4247
|
var i;
|
|
@@ -4184,31 +4259,31 @@ function ki(t) {
|
|
|
4184
4259
|
ruleCount: a.length
|
|
4185
4260
|
};
|
|
4186
4261
|
}
|
|
4187
|
-
const
|
|
4262
|
+
const yi = new Map(pe.map((t) => [t.id, t]));
|
|
4188
4263
|
function Ii(t) {
|
|
4189
|
-
const a =
|
|
4264
|
+
const a = yi.get(t);
|
|
4190
4265
|
return a || U.find((e) => e.id === t);
|
|
4191
4266
|
}
|
|
4192
4267
|
export {
|
|
4193
4268
|
fe as clearAllCaches,
|
|
4194
|
-
|
|
4195
|
-
|
|
4269
|
+
Me as clearAriaAttrAuditCache,
|
|
4270
|
+
Se as clearAriaHiddenCache,
|
|
4196
4271
|
He as clearColorCaches,
|
|
4197
4272
|
ve as clearComputedRoleCache,
|
|
4198
4273
|
k as compileDeclarativeRule,
|
|
4199
|
-
|
|
4200
|
-
|
|
4274
|
+
xi as configureRules,
|
|
4275
|
+
Si as createChunkedAudit,
|
|
4201
4276
|
v as getAccessibleName,
|
|
4202
|
-
|
|
4277
|
+
w as getAccessibleTextContent,
|
|
4203
4278
|
be as getActiveRules,
|
|
4204
|
-
|
|
4205
|
-
|
|
4279
|
+
L as getComputedRole,
|
|
4280
|
+
u as getHtmlSnippet,
|
|
4206
4281
|
ue as getImplicitRole,
|
|
4207
4282
|
Ii as getRuleById,
|
|
4208
|
-
|
|
4283
|
+
m as getSelector,
|
|
4209
4284
|
p as isAriaHidden,
|
|
4210
|
-
|
|
4211
|
-
|
|
4285
|
+
xe as isValidRole,
|
|
4286
|
+
wi as querySelectorShadowAware,
|
|
4212
4287
|
pe as rules,
|
|
4213
4288
|
ki as runAudit,
|
|
4214
4289
|
Ai as validateDeclarativeRule
|