@accesslint/core 0.8.2 → 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -133,7 +133,7 @@ function $e(e) {
133
133
  return null;
134
134
  }
135
135
  }
136
- function U(e) {
136
+ function W(e) {
137
137
  var n;
138
138
  const a = K.get(e);
139
139
  if (a !== void 0) return a;
@@ -310,7 +310,7 @@ const ut = /* @__PURE__ */ new Set([
310
310
  "aria-relevant",
311
311
  "aria-roledescription"
312
312
  ]);
313
- function q(e) {
313
+ function E(e) {
314
314
  let a = e;
315
315
  for (; a; ) {
316
316
  if (He(a)) return !0;
@@ -493,7 +493,7 @@ function p(e) {
493
493
  const n = t.map((o, s) => (s === 0 ? "" : o.delimiter) + o.selector).join("");
494
494
  return te.set(e, n), n;
495
495
  }
496
- function Un(e) {
496
+ function Wn(e) {
497
497
  const a = [], t = [];
498
498
  let i = e;
499
499
  for (; i; ) {
@@ -909,7 +909,7 @@ function C(e) {
909
909
  );
910
910
  return r ? [parseInt(r[1]), parseInt(r[2]), parseInt(r[3])] : null;
911
911
  }
912
- function W(e) {
912
+ function U(e) {
913
913
  const a = e.match(/rgba\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*,\s*([\d.]+)\s*\)/);
914
914
  if (a) return parseFloat(a[1]);
915
915
  const t = e.match(/rgba?\([^)]+\/\s*([\d.]+%?)\s*\)/);
@@ -957,7 +957,7 @@ function St(e) {
957
957
  t = t.parentElement;
958
958
  continue;
959
959
  }
960
- const s = W(o);
960
+ const s = U(o);
961
961
  if (s < 0.01) {
962
962
  t = t.parentElement;
963
963
  continue;
@@ -1002,15 +1002,15 @@ function It(e, a = [255, 255, 255]) {
1002
1002
  }
1003
1003
  return t;
1004
1004
  }
1005
- const qt = /* @__PURE__ */ new Set(["IMG", "PICTURE", "VIDEO", "SVG"]);
1006
- function Et(e) {
1005
+ const Et = /* @__PURE__ */ new Set(["IMG", "PICTURE", "VIDEO", "SVG"]);
1006
+ function qt(e) {
1007
1007
  const a = ne.get(e);
1008
1008
  if (a !== void 0) return a;
1009
1009
  const t = Rt(e);
1010
1010
  return ne.set(e, t), t;
1011
1011
  }
1012
1012
  function Lt(e) {
1013
- return qt.has(e.tagName) ? !0 : !!e.querySelector("img, picture, video, svg");
1013
+ return Et.has(e.tagName) ? !0 : !!e.querySelector("img, picture, video, svg");
1014
1014
  }
1015
1015
  function Rt(e) {
1016
1016
  let a = e, t = !1;
@@ -1087,7 +1087,7 @@ function $t(e) {
1087
1087
  const r = getComputedStyle(a, n), o = r.content;
1088
1088
  if (!o || o === "none" || o === "normal" || o === '""') continue;
1089
1089
  const s = r.backgroundColor;
1090
- if (s && !we(s) && W(s) >= 0.1) return !0;
1090
+ if (s && !we(s) && U(s) >= 0.1) return !0;
1091
1091
  const l = r.backgroundImage;
1092
1092
  if (l && l !== "none" && l !== "initial") return !0;
1093
1093
  const d = r.position;
@@ -1098,13 +1098,13 @@ function $t(e) {
1098
1098
  } catch {
1099
1099
  }
1100
1100
  const i = x(a).backgroundColor;
1101
- if (i && !we(i) && W(i) >= 1) break;
1101
+ if (i && !we(i) && U(i) >= 1) break;
1102
1102
  a = a.parentElement;
1103
1103
  }
1104
1104
  return !1;
1105
1105
  }
1106
1106
  const G = /* @__PURE__ */ new Map();
1107
- function Wn(e, a) {
1107
+ function Un(e, a) {
1108
1108
  G.set(e, a), re.delete(e);
1109
1109
  }
1110
1110
  function Ht(e, a) {
@@ -1193,7 +1193,7 @@ const jt = {
1193
1193
  fixability: "contextual",
1194
1194
  browserHint: "Screenshot the image to describe its visual content for alt text.",
1195
1195
  description: `Images must have alternate text. Add an alt attribute to <img> elements. Decorative images may use an empty alt attribute (alt=""), role='none', or role='presentation'.`,
1196
- guidance: "Every image needs an alt attribute. For informative images, describe the content or function concisely. For decorative images (backgrounds, spacers, purely visual flourishes), use alt='' to hide them from screen readers. Never omit alt entirely—screen readers may read the filename instead.",
1196
+ guidance: "Every image needs an alt attribute. For informative images, describe the content or function concisely. For decorative images (backgrounds, spacers, purely visual flourishes), use alt='' to hide them from screen readers. Never omit alt entirely—screen readers may read the filename instead. When an image is inside a link or button that already has text, use alt='' if the image is decorative in that context, or write alt text that complements (not duplicates) the existing text.",
1197
1197
  run(e) {
1198
1198
  const a = [];
1199
1199
  for (const t of e.querySelectorAll("img")) {
@@ -1229,14 +1229,14 @@ const jt = {
1229
1229
  return a;
1230
1230
  }
1231
1231
  };
1232
- function Ut(e) {
1232
+ function Wt(e) {
1233
1233
  var i;
1234
1234
  const a = Pe(e);
1235
1235
  if (a) return a;
1236
1236
  const t = e.querySelector("title");
1237
1237
  return (i = t == null ? void 0 : t.textContent) != null && i.trim() ? t.textContent.trim() : "";
1238
1238
  }
1239
- const Wt = {
1239
+ const Ut = {
1240
1240
  id: "text-alternatives/svg-img-alt",
1241
1241
  category: "text-alternatives",
1242
1242
  actRuleIds: ["7d6734"],
@@ -1250,7 +1250,7 @@ const Wt = {
1250
1250
  const a = [], t = 'svg[role="img"], [role="graphics-document"], [role="graphics-symbol"]';
1251
1251
  for (const i of e.querySelectorAll(t)) {
1252
1252
  if (h(i)) continue;
1253
- if (!Ut(i)) {
1253
+ if (!Wt(i)) {
1254
1254
  const r = i.getAttribute("role");
1255
1255
  a.push({
1256
1256
  ruleId: "text-alternatives/svg-img-alt",
@@ -1441,7 +1441,7 @@ const Wt = {
1441
1441
  run(e) {
1442
1442
  const a = [];
1443
1443
  for (const t of e.querySelectorAll("video")) {
1444
- if (h(t) || q(t) || t.hasAttribute("muted") || t.hasAttribute("autoplay")) continue;
1444
+ if (h(t) || E(t) || t.hasAttribute("muted") || t.hasAttribute("autoplay")) continue;
1445
1445
  t.querySelector('track[kind="captions"], track[kind="subtitles"]') || a.push({
1446
1446
  ruleId: "time-based-media/video-captions",
1447
1447
  selector: p(t),
@@ -1465,7 +1465,7 @@ const Wt = {
1465
1465
  run(e) {
1466
1466
  const a = [];
1467
1467
  for (const t of e.querySelectorAll("audio")) {
1468
- if (h(t) || q(t) || t.querySelector('track[kind="captions"], track[kind="descriptions"]') || t.hasAttribute("aria-describedby")) continue;
1468
+ if (h(t) || E(t) || t.querySelector('track[kind="captions"], track[kind="descriptions"]') || t.hasAttribute("aria-describedby")) continue;
1469
1469
  const n = t.parentElement;
1470
1470
  n && n.querySelector('a[href*="transcript"], a[href*="text"]') || a.push({
1471
1471
  ruleId: "time-based-media/audio-transcript",
@@ -1564,7 +1564,7 @@ const na = {
1564
1564
  run(e) {
1565
1565
  const a = [];
1566
1566
  for (const t of e.querySelectorAll("[autocomplete]")) {
1567
- if (h(t) || q(t) || t.disabled || t.getAttribute("aria-disabled") === "true") continue;
1567
+ if (h(t) || E(t) || t.disabled || t.getAttribute("aria-disabled") === "true") continue;
1568
1568
  const i = t.getAttribute("autocomplete").trim();
1569
1569
  i && (ia(i) || a.push({
1570
1570
  ruleId: "adaptable/autocomplete-valid",
@@ -2007,14 +2007,14 @@ function fa(e, a) {
2007
2007
  for (const s of e.querySelectorAll("*")) {
2008
2008
  if (h(s)) continue;
2009
2009
  r = !0;
2010
- const l = U(s);
2010
+ const l = W(s);
2011
2011
  l && n.add(l);
2012
2012
  }
2013
2013
  for (const s of t) {
2014
2014
  const l = i.getElementById(s);
2015
2015
  if (l && !h(l)) {
2016
2016
  r = !0;
2017
- const d = U(l);
2017
+ const d = W(l);
2018
2018
  d && n.add(d);
2019
2019
  }
2020
2020
  }
@@ -2091,7 +2091,7 @@ const va = {
2091
2091
  const r = Se[n];
2092
2092
  let o = i.parentElement, s = !1;
2093
2093
  for (; o && o !== e.documentElement; ) {
2094
- const l = U(o);
2094
+ const l = W(o);
2095
2095
  if (l && r.includes(l)) {
2096
2096
  s = !0;
2097
2097
  break;
@@ -2294,7 +2294,7 @@ const ka = {
2294
2294
  }
2295
2295
  return a;
2296
2296
  }
2297
- }, qa = {
2297
+ }, Ea = {
2298
2298
  id: "distinguishable/meta-viewport",
2299
2299
  category: "distinguishable",
2300
2300
  actRuleIds: ["b4f0c3"],
@@ -2340,7 +2340,7 @@ const ka = {
2340
2340
  function ce(e) {
2341
2341
  return e.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
2342
2342
  }
2343
- function Ue(e, a) {
2343
+ function We(e, a) {
2344
2344
  const t = e.getAttribute("style");
2345
2345
  if (!t) return null;
2346
2346
  const i = new RegExp(
@@ -2387,7 +2387,7 @@ function Ue(e, a) {
2387
2387
  }
2388
2388
  return null;
2389
2389
  }
2390
- function We(e, a, t, i) {
2390
+ function Ue(e, a, t, i) {
2391
2391
  function n(r) {
2392
2392
  var o;
2393
2393
  if (r !== e) {
@@ -2409,7 +2409,7 @@ function We(e, a, t, i) {
2409
2409
  }
2410
2410
  return n(e);
2411
2411
  }
2412
- function Ea(e) {
2412
+ function qa(e) {
2413
2413
  var a;
2414
2414
  for (const t of e.childNodes)
2415
2415
  if (t.nodeType === 3 && ((a = t.textContent) != null && a.trim()))
@@ -2431,7 +2431,7 @@ function Ve(e) {
2431
2431
  return !1;
2432
2432
  }
2433
2433
  function de(e, a) {
2434
- if (Ea(e)) return !0;
2434
+ if (qa(e)) return !0;
2435
2435
  for (const t of e.children) {
2436
2436
  const i = t.getAttribute("style") || "";
2437
2437
  if (!new RegExp(
@@ -2446,10 +2446,10 @@ function Be(e, a, t, i) {
2446
2446
  const n = [];
2447
2447
  for (const r of e.querySelectorAll("[style]")) {
2448
2448
  if (h(r) || !Oe(r) || Ve(r) || !de(r, t)) continue;
2449
- const o = Ue(r, t);
2449
+ const o = We(r, t);
2450
2450
  if (!o) continue;
2451
2451
  let s = !1;
2452
- if (o.em !== null ? s = o.em < i : o.px !== null && (s = We(r, t, o.px, i)), s) {
2452
+ if (o.em !== null ? s = o.em < i : o.px !== null && (s = Ue(r, t, o.px, i)), s) {
2453
2453
  const l = o.em !== null ? `${o.em}${t === "line-height" ? "" : "em"}` : `${o.px}px`;
2454
2454
  n.push({
2455
2455
  ruleId: a,
@@ -2503,10 +2503,10 @@ const Ra = {
2503
2503
  const r = parseFloat(x(t).lineHeight);
2504
2504
  if (r > 0 && t.scrollHeight <= r * 1.5) continue;
2505
2505
  }
2506
- const i = Ue(t, "line-height");
2506
+ const i = We(t, "line-height");
2507
2507
  if (!i) continue;
2508
2508
  let n = !1;
2509
- if (i.em !== null ? n = i.em < 1.5 : i.px !== null && (n = We(t, "line-height", i.px, 1.5)), n) {
2509
+ if (i.em !== null ? n = i.em < 1.5 : i.px !== null && (n = Ue(t, "line-height", i.px, 1.5)), n) {
2510
2510
  const r = i.em !== null ? `${i.em}` : `${i.px}px`;
2511
2511
  a.push({
2512
2512
  ruleId: "distinguishable/line-height",
@@ -2567,7 +2567,7 @@ function $a(e) {
2567
2567
  const o = i.match(new RegExp("\\p{L}{3,}", "gu"));
2568
2568
  return !n || !o || o.length < 2 ? null : { block: a, textColor: n };
2569
2569
  }
2570
- function qe(e, a) {
2570
+ function Ee(e, a) {
2571
2571
  const t = e.textDecorationLine || e.textDecoration || "";
2572
2572
  return (t.includes("underline") || t.includes("line-through")) && t !== a;
2573
2573
  }
@@ -2599,13 +2599,13 @@ const Da = {
2599
2599
  const n = $a(t);
2600
2600
  if (!n) continue;
2601
2601
  const r = x(n.block), o = r.textDecorationLine || r.textDecoration || "";
2602
- if (qe(i, o) || (parseFloat(i.borderBottomWidth) || 0) > 0 && i.borderBottomStyle !== "none" && i.borderBottomStyle !== "hidden" || Math.abs(V(i.fontWeight) - V(r.fontWeight)) >= 300 || i.fontStyle !== r.fontStyle) continue;
2602
+ if (Ee(i, o) || (parseFloat(i.borderBottomWidth) || 0) > 0 && i.borderBottomStyle !== "none" && i.borderBottomStyle !== "hidden" || Math.abs(V(i.fontWeight) - V(r.fontWeight)) >= 300 || i.fontStyle !== r.fontStyle) continue;
2603
2603
  const l = parseFloat(i.fontSize) || 16, d = parseFloat(r.fontSize) || 16;
2604
2604
  if (d > 0 && l / d >= 1.2) continue;
2605
2605
  let c = !1;
2606
2606
  for (const y of t.querySelectorAll("*")) {
2607
2607
  const A = x(y);
2608
- if (qe(A, o) || Math.abs(V(A.fontWeight) - V(r.fontWeight)) >= 300) {
2608
+ if (Ee(A, o) || Math.abs(V(A.fontWeight) - V(r.fontWeight)) >= 300) {
2609
2609
  c = !0;
2610
2610
  break;
2611
2611
  }
@@ -2671,7 +2671,7 @@ function za(e) {
2671
2671
  function ja(e) {
2672
2672
  return e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement || e instanceof HTMLSelectElement || e instanceof HTMLButtonElement ? e.disabled : !!(e.closest("fieldset[disabled]") || e.getAttribute("aria-disabled") === "true");
2673
2673
  }
2674
- function Ua(e, a) {
2674
+ function Wa(e, a) {
2675
2675
  if (e.tagName !== "LABEL") return !1;
2676
2676
  const t = e, i = t.htmlFor;
2677
2677
  if (i) {
@@ -2683,7 +2683,7 @@ function Ua(e, a) {
2683
2683
  const r = t.id;
2684
2684
  return !!(r && a.querySelector(`[aria-labelledby~="${r}"][aria-disabled="true"]`));
2685
2685
  }
2686
- function Wa(e) {
2686
+ function Ua(e) {
2687
2687
  return e.closest("select") !== null;
2688
2688
  }
2689
2689
  function Oa(e) {
@@ -2708,10 +2708,10 @@ function _a(e) {
2708
2708
  const a = parseFloat(e);
2709
2709
  return isNaN(a) ? NaN : e.trim().endsWith("%") ? a / 100 : a;
2710
2710
  }
2711
- const Ee = /([a-z-]+)\(([^)]*)\)/g;
2711
+ const qe = /([a-z-]+)\(([^)]*)\)/g;
2712
2712
  function Le(e) {
2713
2713
  let a, t = !1;
2714
- for (Ee.lastIndex = 0; a = Ee.exec(e); ) {
2714
+ for (qe.lastIndex = 0; a = qe.exec(e); ) {
2715
2715
  t = !0;
2716
2716
  const i = Ba[a[1]];
2717
2717
  if (i === void 0 || _a(a[2]) !== i) return !1;
@@ -2742,7 +2742,7 @@ function Ya(e) {
2742
2742
  a = a.parentElement;
2743
2743
  continue;
2744
2744
  }
2745
- if (W(n) < 0.01) {
2745
+ if (U(n) < 0.01) {
2746
2746
  a = a.parentElement;
2747
2747
  continue;
2748
2748
  }
@@ -2787,7 +2787,7 @@ function _e(e, a, t) {
2787
2787
  const l = s.parentElement;
2788
2788
  if (!l || o.has(l) || (o.add(l), Pa.has(l.tagName))) continue;
2789
2789
  const d = l.tagName;
2790
- if (d === "BODY" || d === "HTML" || Wa(l) || ja(l) || Ua(l, e) || Va(l) || za(l)) continue;
2790
+ if (d === "BODY" || d === "HTML" || Ua(l) || ja(l) || Wa(l, e) || Va(l) || za(l)) continue;
2791
2791
  const c = x(l);
2792
2792
  if (parseFloat(c.opacity) === 0) continue;
2793
2793
  const u = Mt(l);
@@ -2797,15 +2797,15 @@ function _e(e, a, t) {
2797
2797
  if (b && b !== "none" && b !== "initial" && (g = Tt(b), !g) || Ga(l) || $t(l)) continue;
2798
2798
  const f = C(c.color);
2799
2799
  if (!f) continue;
2800
- const v = W(c.color);
2801
- if (v === 0 || Et(l)) continue;
2800
+ const v = U(c.color);
2801
+ if (v === 0 || qt(l)) continue;
2802
2802
  const y = t === "AAA" ? xe(l) ? 4.5 : 7 : xe(l) ? 3 : 4.5;
2803
2803
  let A = ye(l);
2804
2804
  if (!A) {
2805
2805
  if (g) continue;
2806
- const E = Ya(l);
2807
- if (E) {
2808
- const O = E.gradientEl.parentElement ? ye(E.gradientEl.parentElement) : null, H = Xa(
2806
+ const q = Ya(l);
2807
+ if (q) {
2808
+ const O = q.gradientEl.parentElement ? ye(q.gradientEl.parentElement) : null, H = Xa(
2809
2809
  l,
2810
2810
  f,
2811
2811
  v,
@@ -2813,7 +2813,7 @@ function _e(e, a, t) {
2813
2813
  y,
2814
2814
  a,
2815
2815
  t,
2816
- E.bgImage,
2816
+ q.bgImage,
2817
2817
  O ?? [255, 255, 255]
2818
2818
  );
2819
2819
  H && i.push(H);
@@ -2824,14 +2824,14 @@ function _e(e, a, t) {
2824
2824
  v < 1 && (S = R(f, A, v)), u < 1 && (S = R(S, A, u));
2825
2825
  const nt = I(S[0], S[1], S[2]), rt = I(A[0], A[1], A[2]), be = g ? Nt(S, A, g) : $(nt, rt);
2826
2826
  if (be < y) {
2827
- const E = Math.round(be * 100) / 100, O = _(S), H = _(A);
2827
+ const q = Math.round(be * 100) / 100, O = _(S), H = _(A);
2828
2828
  i.push({
2829
2829
  ruleId: a,
2830
2830
  selector: p(l),
2831
2831
  html: m(l),
2832
2832
  impact: "serious",
2833
- message: `Insufficient${t === "AAA" ? " enhanced" : ""} color contrast ratio of ${E}:1 (required ${y}:1).`,
2834
- context: `foreground: ${O} rgb(${S.join(", ")}), background: ${H} rgb(${A.join(", ")}), ratio: ${E}:1, required: ${y}:1`,
2833
+ message: `Insufficient${t === "AAA" ? " enhanced" : ""} color contrast ratio of ${q}:1 (required ${y}:1).`,
2834
+ context: `foreground: ${O} rgb(${S.join(", ")}), background: ${H} rgb(${A.join(", ")}), ratio: ${q}:1, required: ${y}:1`,
2835
2835
  fix: { type: "suggest", suggestion: `Change the text color or background color so the contrast ratio meets ${y}:1. Current foreground is ${O}, background is ${H}.` }
2836
2836
  });
2837
2837
  }
@@ -3484,7 +3484,7 @@ const bi = {
3484
3484
  return a;
3485
3485
  }
3486
3486
  };
3487
- function qi(e) {
3487
+ function Ei(e) {
3488
3488
  var n, r;
3489
3489
  const a = [], t = e.getAttribute("href");
3490
3490
  t && a.push(`href: ${t}`);
@@ -3501,7 +3501,7 @@ function qi(e) {
3501
3501
  return a.length > 0 ? a.join(`
3502
3502
  `) : void 0;
3503
3503
  }
3504
- const Ei = {
3504
+ const qi = {
3505
3505
  id: "navigable/link-name",
3506
3506
  category: "navigable",
3507
3507
  actRuleIds: ["c487ae"],
@@ -3514,14 +3514,14 @@ const Ei = {
3514
3514
  run(e) {
3515
3515
  const a = [];
3516
3516
  for (const t of e.querySelectorAll('a[href], area[href], [role="link"]')) {
3517
- if (h(t) || q(t) || t.getRootNode() instanceof ShadowRoot) continue;
3517
+ if (h(t) || E(t) || t.getRootNode() instanceof ShadowRoot) continue;
3518
3518
  w(t) || a.push({
3519
3519
  ruleId: "navigable/link-name",
3520
3520
  selector: p(t),
3521
3521
  html: m(t),
3522
3522
  impact: "serious",
3523
3523
  message: "Link has no discernible text.",
3524
- context: qi(t),
3524
+ context: Ei(t),
3525
3525
  fix: { type: "add-text-content" }
3526
3526
  });
3527
3527
  }
@@ -3761,13 +3761,13 @@ const Ei = {
3761
3761
  }
3762
3762
  }, ji = new Set(
3763
3763
  "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(" ")
3764
- ), Ui = new Set(
3764
+ ), Wi = new Set(
3765
3765
  "aar abk afr aka amh ara arg asm ava ave aym aze bak bam bel ben bih bis bod bos bre bul cat ces cha che chu chv cor cos cre cym dan deu div dzo ell eng epo est eus ewe fao fas fij fin fra fry ful gla gle glg glv grn guj hat hau hbs heb her hin hmo hrv hun hye ibo iii iku ile ina ind ipk isl ita jav jpn kal kan kas kat kau kaz khm kik kin kir kom kon kor kua kur lao lat lav lim lin lit ltz lub lug mah mal mar mkd mlg mlt mon mri msa mya nau nav nbl nde ndo nep nld nno nob nor nya oci oji ori orm oss pan pli pol por pus que roh ron run rus sag san sin slk slv sme smo sna snd som sot spa sqi srd srp ssw sun swa swe tah tam tat tel tgk tgl tha tir ton tsn tso tuk tur twi uig ukr urd uzb ven vie vol wln wol xho yid yor zha zho zul".split(" ")
3766
- ), Wi = /^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i;
3766
+ ), Ui = /^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i;
3767
3767
  function Ke(e) {
3768
- if (!Wi.test(e)) return !1;
3768
+ if (!Ui.test(e)) return !1;
3769
3769
  const a = e.split("-")[0].toLowerCase();
3770
- return a.length === 2 ? ji.has(a) : a.length === 3 ? !Ui.has(a) : !1;
3770
+ return a.length === 2 ? ji.has(a) : a.length === 3 ? !Wi.has(a) : !1;
3771
3771
  }
3772
3772
  function Ce(e) {
3773
3773
  var i;
@@ -4034,7 +4034,7 @@ const en = {
4034
4034
  var i;
4035
4035
  const a = [], t = e.querySelectorAll(`${Qe}, ${Ji}`);
4036
4036
  for (const n of t) {
4037
- if (h(n) || q(n)) continue;
4037
+ if (h(n) || E(n)) continue;
4038
4038
  const r = (i = n.getAttribute("role")) == null ? void 0 : i.trim().toLowerCase();
4039
4039
  if (r === "presentation" || r === "none") continue;
4040
4040
  if (!Zi(n)) {
@@ -4107,7 +4107,7 @@ const en = {
4107
4107
  for (const n of e.querySelectorAll(
4108
4108
  'input[type="submit"], input[type="button"], input[type="reset"]'
4109
4109
  )) {
4110
- if (h(n) || q(n)) continue;
4110
+ if (h(n) || E(n)) continue;
4111
4111
  const r = (t = n.getAttribute("value")) == null ? void 0 : t.trim(), o = (i = n.getAttribute("type")) == null ? void 0 : i.toLowerCase(), s = (o === "submit" || o === "reset") && !n.hasAttribute("value");
4112
4112
  !r && !s && !w(n) && a.push({
4113
4113
  ruleId: "labels-and-names/input-button-name",
@@ -4221,7 +4221,7 @@ const nn = {
4221
4221
  var t;
4222
4222
  const a = [];
4223
4223
  for (const i of e.querySelectorAll('[role="button"], [role="link"], [role="menuitem"]')) {
4224
- if (h(i) || q(i) || se(i)) continue;
4224
+ if (h(i) || E(i) || se(i)) continue;
4225
4225
  const n = i.getAttribute("role");
4226
4226
  if ((i.tagName.toLowerCase() === "button" || i.tagName.toLowerCase() === "a") && n !== "menuitem") continue;
4227
4227
  if (!w(i)) {
@@ -4254,7 +4254,7 @@ function M(e) {
4254
4254
  var i;
4255
4255
  const t = [];
4256
4256
  for (const n of a.querySelectorAll(e.selector)) {
4257
- if (h(n) || e.checkComputedHidden && q(n) || e.checkShadowDOM && se(n)) continue;
4257
+ if (h(n) || e.checkComputedHidden && E(n) || e.checkShadowDOM && se(n)) continue;
4258
4258
  if (e.roleSet) {
4259
4259
  const o = (i = n.getAttribute("role")) == null ? void 0 : i.trim().toLowerCase();
4260
4260
  if (!o || !e.roleSet.has(o)) continue;
@@ -4366,7 +4366,7 @@ const hn = {
4366
4366
  run(e) {
4367
4367
  const a = [];
4368
4368
  for (const t of e.querySelectorAll('button, [role="button"]')) {
4369
- if (h(t) || q(t)) continue;
4369
+ if (h(t) || E(t)) continue;
4370
4370
  const i = t.getAttribute("role");
4371
4371
  if ((i === "none" || i === "presentation") && !(t.matches('button:not([disabled]), [tabindex]:not([tabindex="-1"])') || t.tagName.toLowerCase() === "button" && !t.disabled) || se(t)) continue;
4372
4372
  w(t) || a.push({
@@ -4642,30 +4642,45 @@ Referenced by: ${d}` : ""}`,
4642
4642
  description: "ARIA attributes must be allowed for the element's role.",
4643
4643
  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.",
4644
4644
  run(e) {
4645
- const a = [];
4646
- for (const t of e.querySelectorAll("[role], [aria-*]")) {
4647
- if (h(t)) continue;
4648
- const i = U(t);
4649
- if (!i) continue;
4650
- const n = Sn[i];
4651
- if (n)
4652
- for (const r of t.attributes) {
4653
- if (!r.name.startsWith("aria-") || ut.has(r.name) || n.has(r.name)) continue;
4654
- const o = n.size > 0 ? [...n].join(", ") : "none (only global ARIA attributes)";
4645
+ const a = [], t = new Set(e.querySelectorAll("[role]")), i = e.createTreeWalker(
4646
+ e.body || e.documentElement,
4647
+ 1
4648
+ /* NodeFilter.SHOW_ELEMENT */
4649
+ );
4650
+ let n = i.currentNode;
4651
+ for (; n; ) {
4652
+ if (n instanceof Element) {
4653
+ for (const r of n.attributes)
4654
+ if (r.name.startsWith("aria-")) {
4655
+ t.add(n);
4656
+ break;
4657
+ }
4658
+ }
4659
+ n = i.nextNode();
4660
+ }
4661
+ for (const r of t) {
4662
+ if (h(r)) continue;
4663
+ const o = W(r);
4664
+ if (!o) continue;
4665
+ const s = Sn[o];
4666
+ if (s)
4667
+ for (const l of r.attributes) {
4668
+ if (!l.name.startsWith("aria-") || ut.has(l.name) || s.has(l.name)) continue;
4669
+ const d = s.size > 0 ? [...s].join(", ") : "none (only global ARIA attributes)";
4655
4670
  a.push({
4656
4671
  ruleId: "aria/aria-allowed-attr",
4657
- selector: p(t),
4658
- html: m(t),
4672
+ selector: p(r),
4673
+ html: m(r),
4659
4674
  impact: "critical",
4660
- message: `ARIA attribute "${r.name}" is not allowed on role "${i}".`,
4661
- context: `Attribute: ${r.name}="${r.value}", role: ${i}, allowed role-specific attributes: ${o}`,
4662
- fix: { type: "remove-attribute", attribute: r.name }
4675
+ message: `ARIA attribute "${l.name}" is not allowed on role "${o}".`,
4676
+ context: `Attribute: ${l.name}="${l.value}", role: ${o}, allowed role-specific attributes: ${d}`,
4677
+ fix: { type: "remove-attribute", attribute: l.name }
4663
4678
  });
4664
4679
  }
4665
4680
  }
4666
4681
  return a;
4667
4682
  }
4668
- }, qn = /* @__PURE__ */ new Set([
4683
+ }, En = /* @__PURE__ */ new Set([
4669
4684
  "base",
4670
4685
  "col",
4671
4686
  "colgroup",
@@ -4787,10 +4802,10 @@ Referenced by: ${d}` : ""}`,
4787
4802
  video: /* @__PURE__ */ new Set(["application"]),
4788
4803
  wbr: /* @__PURE__ */ new Set(["none", "presentation"])
4789
4804
  };
4790
- function En(e) {
4805
+ function qn(e) {
4791
4806
  var t;
4792
4807
  const a = e.tagName.toLowerCase();
4793
- if (qn.has(a))
4808
+ if (En.has(a))
4794
4809
  return "none";
4795
4810
  if (a === "a" && e.hasAttribute("href"))
4796
4811
  return D["a[href]"];
@@ -4819,7 +4834,7 @@ const Ln = {
4819
4834
  if (!n) continue;
4820
4835
  const r = $e(i);
4821
4836
  if (r && n === r) continue;
4822
- const o = En(i);
4837
+ const o = qn(i);
4823
4838
  o === "none" ? a.push({
4824
4839
  ruleId: "aria/aria-allowed-role",
4825
4840
  selector: p(i),
@@ -5031,7 +5046,7 @@ const Pn = {
5031
5046
  const a = [];
5032
5047
  for (const t of e.querySelectorAll("*")) {
5033
5048
  if (h(t)) continue;
5034
- const i = U(t);
5049
+ const i = W(t);
5035
5050
  if (!(!i || !Fn.has(i))) {
5036
5051
  for (const n of t.querySelectorAll(j))
5037
5052
  if (n !== t && !n.disabled) {
@@ -5051,7 +5066,7 @@ const Pn = {
5051
5066
  }, Ze = [
5052
5067
  // Text Alternatives
5053
5068
  jt,
5054
- Wt,
5069
+ Ut,
5055
5070
  Ot,
5056
5071
  Vt,
5057
5072
  _t,
@@ -5076,7 +5091,7 @@ const Pn = {
5076
5091
  Sa,
5077
5092
  Ia,
5078
5093
  // Distinguishable
5079
- qa,
5094
+ Ea,
5080
5095
  Ra,
5081
5096
  Ca,
5082
5097
  Ta,
@@ -5103,7 +5118,7 @@ const Pn = {
5103
5118
  ki,
5104
5119
  Si,
5105
5120
  Ii,
5106
- Ei,
5121
+ qi,
5107
5122
  Li,
5108
5123
  // Landmarks
5109
5124
  Ri,
@@ -5244,7 +5259,7 @@ const Xn = {
5244
5259
  "enough-time/meta-refresh": { description: "Meta refresh must not redirect or refresh automatically.", guidance: "Automatic page refreshes or redirects can disorient users, especially those using screen readers or with cognitive disabilities. They may lose their place or not have time to read content. If a redirect is needed, use a server-side redirect (HTTP 301/302) instead. For timed refreshes, provide user controls.", messages: { "Page redirects after {0} seconds without warning. Use server-side redirect.": "Page redirects after {0} seconds without warning. Use server-side redirect.", "Page auto-refreshes after {0} seconds. Provide user control over refresh.": "Page auto-refreshes after {0} seconds. Provide user control over refresh." } },
5245
5260
  "enough-time/blink": { description: "The <blink> element must not be used.", guidance: "Blinking content can cause seizures in users with photosensitive epilepsy and is distracting for users with attention disorders. The <blink> element is deprecated and should never be used. If you need to draw attention to content, use less intrusive methods like color, borders, or icons.", messages: { "The <blink> element causes accessibility issues. Remove it entirely.": "The <blink> element causes accessibility issues. Remove it entirely." } },
5246
5261
  "enough-time/marquee": { description: "The <marquee> element must not be used.", guidance: "Scrolling or moving content is difficult for many users to read, especially those with cognitive or visual disabilities. The <marquee> element is deprecated. Replace scrolling text with static content. If content must scroll, provide pause/stop controls and ensure it stops after 5 seconds.", messages: { "The <marquee> element causes accessibility issues. Replace with static content.": "The <marquee> element causes accessibility issues. Replace with static content." } },
5247
- "text-alternatives/img-alt": { description: `Images must have alternate text. Add an alt attribute to <img> elements. Decorative images may use an empty alt attribute (alt=""), role='none', or role='presentation'.`, guidance: "Every image needs an alt attribute. For informative images, describe the content or function concisely. For decorative images (backgrounds, spacers, purely visual flourishes), use alt='' to hide them from screen readers. Never omit alt entirely—screen readers may read the filename instead.", messages: { 'Image has whitespace-only alt text. Use alt="" for decorative images or provide descriptive text.': 'Image has whitespace-only alt text. Use alt="" for decorative images or provide descriptive text.', "Image element missing alt attribute.": "Image element missing alt attribute.", 'Element with role="img" has no accessible name. Add aria-label or aria-labelledby.': 'Element with role="img" has no accessible name. Add aria-label or aria-labelledby.' } },
5262
+ "text-alternatives/img-alt": { description: `Images must have alternate text. Add an alt attribute to <img> elements. Decorative images may use an empty alt attribute (alt=""), role='none', or role='presentation'.`, guidance: "Every image needs an alt attribute. For informative images, describe the content or function concisely. For decorative images (backgrounds, spacers, purely visual flourishes), use alt='' to hide them from screen readers. Never omit alt entirely—screen readers may read the filename instead. When an image is inside a link or button that already has text, use alt='' if the image is decorative in that context, or write alt text that complements (not duplicates) the existing text.", messages: { 'Image has whitespace-only alt text. Use alt="" for decorative images or provide descriptive text.': 'Image has whitespace-only alt text. Use alt="" for decorative images or provide descriptive text.', "Image element missing alt attribute.": "Image element missing alt attribute.", 'Element with role="img" has no accessible name. Add aria-label or aria-labelledby.': 'Element with role="img" has no accessible name. Add aria-label or aria-labelledby.' } },
5248
5263
  "text-alternatives/svg-img-alt": { description: "SVG elements with an img, graphics-document, or graphics-symbol role must have an accessible name via a <title> element, aria-label, or aria-labelledby.", guidance: "Inline SVGs with role='img' need accessible names. Add a <title> element as the first child of the SVG (screen readers will announce it), or use aria-label on the SVG element. For complex SVGs, use aria-labelledby referencing both a <title> and <desc> element. Decorative SVGs should use aria-hidden='true' instead.", messages: { "{0} with role='{1}' has no accessible name.": "{0} with role='{1}' has no accessible name." } },
5249
5264
  "text-alternatives/input-image-alt": { description: 'Image inputs (<input type="image">) must have alternate text describing the button action.', guidance: "Image buttons (<input type='image'>) act as submit buttons with a custom image. Add alt text via alt, aria-label, or aria-labelledby that describes the action (e.g. alt='Search' or alt='Submit order'), not the image itself. Without it, screen readers announce only 'image' or the filename, giving no clue what the button does.", messages: { "Image input missing alt text.": "Image input missing alt text." } },
5250
5265
  "text-alternatives/image-redundant-alt": { 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.", 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.", messages: { 'Alt text "{0}" duplicates surrounding {1} text.': 'Alt text "{0}" duplicates surrounding {1} text.' } },
@@ -5434,7 +5449,7 @@ export {
5434
5449
  w as getAccessibleName,
5435
5450
  k as getAccessibleTextContent,
5436
5451
  pe as getActiveRules,
5437
- U as getComputedRole,
5452
+ W as getComputedRole,
5438
5453
  m as getHtmlSnippet,
5439
5454
  $e as getImplicitRole,
5440
5455
  Yn as getRuleById,
@@ -5443,8 +5458,8 @@ export {
5443
5458
  dt as isValidRole,
5444
5459
  Xn as localeEn,
5445
5460
  Jn as localeEs,
5446
- Un as querySelectorShadowAware,
5447
- Wn as registerLocale,
5461
+ Wn as querySelectorShadowAware,
5462
+ Un as registerLocale,
5448
5463
  Ze as rules,
5449
5464
  _n as runAudit,
5450
5465
  ze as translateViolations,
@@ -1 +1 @@
1
- {"version":3,"file":"aria-allowed-attr.d.ts","sourceRoot":"","sources":["../../../src/rules/aria/aria-allowed-attr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAgFrC,eAAO,MAAM,eAAe,EAAE,IA4C7B,CAAC"}
1
+ {"version":3,"file":"aria-allowed-attr.d.ts","sourceRoot":"","sources":["../../../src/rules/aria/aria-allowed-attr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAgFrC,eAAO,MAAM,eAAe,EAAE,IA0D7B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@accesslint/core",
3
- "version": "0.8.2",
3
+ "version": "0.8.3",
4
4
  "description": "Pure accessibility rule engine — WCAG audit with zero browser dependencies",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -21,7 +21,7 @@
21
21
  "build": "vite build --config vite.lib.config.ts && vite build --config vite.iife.config.ts && tsc -p tsconfig.lib.json",
22
22
  "test": "vitest run",
23
23
  "test:coverage": "vitest run --coverage",
24
- "bench": "vitest bench",
24
+
25
25
  "test:memory": "vitest run --config vite.memory.config.ts",
26
26
  "act:download": "scripts/download-act-fixtures.sh && npx tsx src/act/download-fixtures.ts",
27
27
  "test:act": "vitest run src/act/act.test.ts",
@@ -39,7 +39,7 @@
39
39
  "access": "public"
40
40
  },
41
41
  "devDependencies": {
42
- "@codspeed/vitest-plugin": "^5.1.0",
42
+
43
43
  "@playwright/test": "^1.58.2",
44
44
  "@vitest/coverage-v8": "^3.2.4",
45
45
  "happy-dom": "^20.4.0",