test-a11y-js 0.5.5 → 0.6.2

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.
@@ -11075,9 +11075,9 @@ var require_CSSStyleRule = __commonJS({
11075
11075
  return text;
11076
11076
  },
11077
11077
  set: function(cssText) {
11078
- var rule7 = CSSOM.CSSStyleRule.parse(cssText);
11079
- this.style = rule7.style;
11080
- this.selectorText = rule7.selectorText;
11078
+ var rule14 = CSSOM.CSSStyleRule.parse(cssText);
11079
+ this.style = rule14.style;
11080
+ this.selectorText = rule14.selectorText;
11081
11081
  }
11082
11082
  });
11083
11083
  CSSOM.CSSStyleRule.parse = function(ruleText) {
@@ -11215,11 +11215,11 @@ var require_CSSStyleSheet = __commonJS({
11215
11215
  };
11216
11216
  CSSOM.CSSStyleSheet.prototype = new CSSOM.StyleSheet();
11217
11217
  CSSOM.CSSStyleSheet.prototype.constructor = CSSOM.CSSStyleSheet;
11218
- CSSOM.CSSStyleSheet.prototype.insertRule = function(rule7, index) {
11218
+ CSSOM.CSSStyleSheet.prototype.insertRule = function(rule14, index) {
11219
11219
  if (index < 0 || index > this.cssRules.length) {
11220
11220
  throw new RangeError("INDEX_SIZE_ERR");
11221
11221
  }
11222
- var cssRule = CSSOM.parse(rule7).cssRules[0];
11222
+ var cssRule = CSSOM.parse(rule14).cssRules[0];
11223
11223
  cssRule.parentStyleSheet = this;
11224
11224
  this.cssRules.splice(index, 0, cssRule);
11225
11225
  return index;
@@ -11414,11 +11414,11 @@ var require_CSSGroupingRule = __commonJS({
11414
11414
  };
11415
11415
  CSSOM.CSSGroupingRule.prototype = new CSSOM.CSSRule();
11416
11416
  CSSOM.CSSGroupingRule.prototype.constructor = CSSOM.CSSGroupingRule;
11417
- CSSOM.CSSGroupingRule.prototype.insertRule = function insertRule(rule7, index) {
11417
+ CSSOM.CSSGroupingRule.prototype.insertRule = function insertRule(rule14, index) {
11418
11418
  if (index < 0 || index > this.cssRules.length) {
11419
11419
  throw new RangeError("INDEX_SIZE_ERR");
11420
11420
  }
11421
- var cssRule = CSSOM.parse(rule7).cssRules[0];
11421
+ var cssRule = CSSOM.parse(rule14).cssRules[0];
11422
11422
  cssRule.parentRule = this;
11423
11423
  this.cssRules.splice(index, 0, cssRule);
11424
11424
  return index;
@@ -12618,9 +12618,9 @@ var require_clone = __commonJS({
12618
12618
  return cloned;
12619
12619
  }
12620
12620
  for (var i = 0, rulesLength = rules.length; i < rulesLength; i++) {
12621
- var rule7 = rules[i];
12622
- var ruleClone = cloned.cssRules[i] = new rule7.constructor();
12623
- var style = rule7.style;
12621
+ var rule14 = rules[i];
12622
+ var ruleClone = cloned.cssRules[i] = new rule14.constructor();
12623
+ var style = rule14.style;
12624
12624
  if (style) {
12625
12625
  var styleClone = ruleClone.style = new CSSOM.CSSStyleDeclaration();
12626
12626
  for (var j = 0, styleLength = style.length; j < styleLength; j++) {
@@ -12630,23 +12630,23 @@ var require_clone = __commonJS({
12630
12630
  }
12631
12631
  styleClone.length = style.length;
12632
12632
  }
12633
- if (rule7.hasOwnProperty("keyText")) {
12634
- ruleClone.keyText = rule7.keyText;
12633
+ if (rule14.hasOwnProperty("keyText")) {
12634
+ ruleClone.keyText = rule14.keyText;
12635
12635
  }
12636
- if (rule7.hasOwnProperty("selectorText")) {
12637
- ruleClone.selectorText = rule7.selectorText;
12636
+ if (rule14.hasOwnProperty("selectorText")) {
12637
+ ruleClone.selectorText = rule14.selectorText;
12638
12638
  }
12639
- if (rule7.hasOwnProperty("mediaText")) {
12640
- ruleClone.mediaText = rule7.mediaText;
12639
+ if (rule14.hasOwnProperty("mediaText")) {
12640
+ ruleClone.mediaText = rule14.mediaText;
12641
12641
  }
12642
- if (rule7.hasOwnProperty("conditionText")) {
12643
- ruleClone.conditionText = rule7.conditionText;
12642
+ if (rule14.hasOwnProperty("conditionText")) {
12643
+ ruleClone.conditionText = rule14.conditionText;
12644
12644
  }
12645
- if (rule7.hasOwnProperty("layerName")) {
12646
- ruleClone.layerName = rule7.layerName;
12645
+ if (rule14.hasOwnProperty("layerName")) {
12646
+ ruleClone.layerName = rule14.layerName;
12647
12647
  }
12648
- if (rule7.hasOwnProperty("cssRules")) {
12649
- ruleClone.cssRules = clone(rule7).cssRules;
12648
+ if (rule14.hasOwnProperty("cssRules")) {
12649
+ ruleClone.cssRules = clone(rule14).cssRules;
12650
12650
  }
12651
12651
  }
12652
12652
  return cloned;
@@ -28132,9 +28132,9 @@ var require_CSSStyleRule2 = __commonJS({
28132
28132
  return text;
28133
28133
  },
28134
28134
  set: function(cssText) {
28135
- var rule7 = CSSOM.CSSStyleRule.parse(cssText);
28136
- this.style = rule7.style;
28137
- this.selectorText = rule7.selectorText;
28135
+ var rule14 = CSSOM.CSSStyleRule.parse(cssText);
28136
+ this.style = rule14.style;
28137
+ this.selectorText = rule14.selectorText;
28138
28138
  }
28139
28139
  });
28140
28140
  CSSOM.CSSStyleRule.parse = function(ruleText) {
@@ -28272,11 +28272,11 @@ var require_CSSStyleSheet2 = __commonJS({
28272
28272
  };
28273
28273
  CSSOM.CSSStyleSheet.prototype = new CSSOM.StyleSheet();
28274
28274
  CSSOM.CSSStyleSheet.prototype.constructor = CSSOM.CSSStyleSheet;
28275
- CSSOM.CSSStyleSheet.prototype.insertRule = function(rule7, index) {
28275
+ CSSOM.CSSStyleSheet.prototype.insertRule = function(rule14, index) {
28276
28276
  if (index < 0 || index > this.cssRules.length) {
28277
28277
  throw new RangeError("INDEX_SIZE_ERR");
28278
28278
  }
28279
- var cssRule = CSSOM.parse(rule7).cssRules[0];
28279
+ var cssRule = CSSOM.parse(rule14).cssRules[0];
28280
28280
  cssRule.parentStyleSheet = this;
28281
28281
  this.cssRules.splice(index, 0, cssRule);
28282
28282
  return index;
@@ -28471,11 +28471,11 @@ var require_CSSGroupingRule2 = __commonJS({
28471
28471
  };
28472
28472
  CSSOM.CSSGroupingRule.prototype = new CSSOM.CSSRule();
28473
28473
  CSSOM.CSSGroupingRule.prototype.constructor = CSSOM.CSSGroupingRule;
28474
- CSSOM.CSSGroupingRule.prototype.insertRule = function insertRule(rule7, index) {
28474
+ CSSOM.CSSGroupingRule.prototype.insertRule = function insertRule(rule14, index) {
28475
28475
  if (index < 0 || index > this.cssRules.length) {
28476
28476
  throw new RangeError("INDEX_SIZE_ERR");
28477
28477
  }
28478
- var cssRule = CSSOM.parse(rule7).cssRules[0];
28478
+ var cssRule = CSSOM.parse(rule14).cssRules[0];
28479
28479
  cssRule.parentRule = this;
28480
28480
  this.cssRules.splice(index, 0, cssRule);
28481
28481
  return index;
@@ -29509,9 +29509,9 @@ var require_clone2 = __commonJS({
29509
29509
  return cloned;
29510
29510
  }
29511
29511
  for (var i = 0, rulesLength = rules.length; i < rulesLength; i++) {
29512
- var rule7 = rules[i];
29513
- var ruleClone = cloned.cssRules[i] = new rule7.constructor();
29514
- var style = rule7.style;
29512
+ var rule14 = rules[i];
29513
+ var ruleClone = cloned.cssRules[i] = new rule14.constructor();
29514
+ var style = rule14.style;
29515
29515
  if (style) {
29516
29516
  var styleClone = ruleClone.style = new CSSOM.CSSStyleDeclaration();
29517
29517
  for (var j = 0, styleLength = style.length; j < styleLength; j++) {
@@ -29521,20 +29521,20 @@ var require_clone2 = __commonJS({
29521
29521
  }
29522
29522
  styleClone.length = style.length;
29523
29523
  }
29524
- if (rule7.hasOwnProperty("keyText")) {
29525
- ruleClone.keyText = rule7.keyText;
29524
+ if (rule14.hasOwnProperty("keyText")) {
29525
+ ruleClone.keyText = rule14.keyText;
29526
29526
  }
29527
- if (rule7.hasOwnProperty("selectorText")) {
29528
- ruleClone.selectorText = rule7.selectorText;
29527
+ if (rule14.hasOwnProperty("selectorText")) {
29528
+ ruleClone.selectorText = rule14.selectorText;
29529
29529
  }
29530
- if (rule7.hasOwnProperty("mediaText")) {
29531
- ruleClone.mediaText = rule7.mediaText;
29530
+ if (rule14.hasOwnProperty("mediaText")) {
29531
+ ruleClone.mediaText = rule14.mediaText;
29532
29532
  }
29533
- if (rule7.hasOwnProperty("conditionText")) {
29534
- ruleClone.conditionText = rule7.conditionText;
29533
+ if (rule14.hasOwnProperty("conditionText")) {
29534
+ ruleClone.conditionText = rule14.conditionText;
29535
29535
  }
29536
- if (rule7.hasOwnProperty("cssRules")) {
29537
- ruleClone.cssRules = clone(rule7).cssRules;
29536
+ if (rule14.hasOwnProperty("cssRules")) {
29537
+ ruleClone.cssRules = clone(rule14).cssRules;
29538
29538
  }
29539
29539
  }
29540
29540
  return cloned;
@@ -64268,17 +64268,17 @@ var require_style_rules = __commonJS({
64268
64268
  };
64269
64269
  function forEachMatchingSheetRuleOfElement(elementImpl, handleRule) {
64270
64270
  function handleSheet(sheet) {
64271
- forEach.call(sheet.cssRules, (rule7) => {
64272
- if (rule7.media) {
64273
- if (indexOf.call(rule7.media, "screen") !== -1) {
64274
- forEach.call(rule7.cssRules, (innerRule) => {
64271
+ forEach.call(sheet.cssRules, (rule14) => {
64272
+ if (rule14.media) {
64273
+ if (indexOf.call(rule14.media, "screen") !== -1) {
64274
+ forEach.call(rule14.cssRules, (innerRule) => {
64275
64275
  if (matches2(innerRule, elementImpl)) {
64276
64276
  handleRule(innerRule);
64277
64277
  }
64278
64278
  });
64279
64279
  }
64280
- } else if (matches2(rule7, elementImpl)) {
64281
- handleRule(rule7);
64280
+ } else if (matches2(rule14, elementImpl)) {
64281
+ handleRule(rule14);
64282
64282
  }
64283
64283
  });
64284
64284
  }
@@ -64315,9 +64315,9 @@ var require_style_rules = __commonJS({
64315
64315
  );
64316
64316
  }
64317
64317
  }
64318
- forEachMatchingSheetRuleOfElement(elementImpl, (rule7) => {
64319
- forEach.call(rule7.style, (property) => {
64320
- handleProperty(rule7.style, property);
64318
+ forEachMatchingSheetRuleOfElement(elementImpl, (rule14) => {
64319
+ forEach.call(rule14.style, (property) => {
64320
+ handleProperty(rule14.style, property);
64321
64321
  });
64322
64322
  });
64323
64323
  forEach.call(elementImpl.style, (property) => {
@@ -64326,8 +64326,8 @@ var require_style_rules = __commonJS({
64326
64326
  styleCache.set(elementImpl, declaration);
64327
64327
  return declaration;
64328
64328
  };
64329
- function matches2(rule7, elementImpl) {
64330
- return matchesDontThrow(rule7.selectorText, elementImpl);
64329
+ function matches2(rule14, elementImpl) {
64330
+ return matchesDontThrow(rule14.selectorText, elementImpl);
64331
64331
  }
64332
64332
  function getCascadedPropertyValue(element, property) {
64333
64333
  return exports.getDeclarationForElement(element).getPropertyValue(property);
@@ -170729,6 +170729,13 @@ var recommended = {
170729
170729
  "test-a11y-js/button-label": "error",
170730
170730
  "test-a11y-js/form-label": "error",
170731
170731
  "test-a11y-js/iframe-title": "error",
170732
+ "test-a11y-js/fieldset-legend": "error",
170733
+ "test-a11y-js/table-structure": "error",
170734
+ "test-a11y-js/details-summary": "error",
170735
+ "test-a11y-js/video-captions": "error",
170736
+ "test-a11y-js/audio-captions": "error",
170737
+ "test-a11y-js/landmark-roles": "warn",
170738
+ "test-a11y-js/dialog-modal": "error",
170732
170739
  // Moderate/Minor violations - set to warn
170733
170740
  "test-a11y-js/link-text": "warn",
170734
170741
  "test-a11y-js/heading-order": "warn"
@@ -170743,7 +170750,14 @@ var strict = {
170743
170750
  "test-a11y-js/link-text": "error",
170744
170751
  "test-a11y-js/form-label": "error",
170745
170752
  "test-a11y-js/heading-order": "error",
170746
- "test-a11y-js/iframe-title": "error"
170753
+ "test-a11y-js/iframe-title": "error",
170754
+ "test-a11y-js/fieldset-legend": "error",
170755
+ "test-a11y-js/table-structure": "error",
170756
+ "test-a11y-js/details-summary": "error",
170757
+ "test-a11y-js/video-captions": "error",
170758
+ "test-a11y-js/audio-captions": "error",
170759
+ "test-a11y-js/landmark-roles": "error",
170760
+ "test-a11y-js/dialog-modal": "error"
170747
170761
  };
170748
170762
  var strict_default = strict;
170749
170763
 
@@ -170882,6 +170896,254 @@ var A11yChecker = class {
170882
170896
  }
170883
170897
  return violations;
170884
170898
  }
170899
+ static checkFieldsetLegend(element) {
170900
+ const violations = [];
170901
+ const fieldsets = element.getElementsByTagName("fieldset");
170902
+ for (const fieldset of Array.from(fieldsets)) {
170903
+ const legend = Array.from(fieldset.children).find(
170904
+ (child) => child.tagName.toLowerCase() === "legend"
170905
+ );
170906
+ if (!legend) {
170907
+ violations.push({
170908
+ id: "fieldset-legend",
170909
+ description: "fieldset must have a legend element as a direct child",
170910
+ element: fieldset,
170911
+ impact: "serious"
170912
+ });
170913
+ } else if (!legend.textContent?.trim()) {
170914
+ violations.push({
170915
+ id: "fieldset-legend-empty",
170916
+ description: "fieldset legend must have non-empty text content",
170917
+ element: fieldset,
170918
+ impact: "serious"
170919
+ });
170920
+ }
170921
+ }
170922
+ return violations;
170923
+ }
170924
+ static checkTableStructure(element) {
170925
+ const violations = [];
170926
+ const tables = element.getElementsByTagName("table");
170927
+ for (const table of Array.from(tables)) {
170928
+ const hasCaption = table.querySelector("caption");
170929
+ const hasAriaLabel = table.hasAttribute("aria-label");
170930
+ const hasAriaLabelledBy = table.hasAttribute("aria-labelledby");
170931
+ if (!hasCaption && !hasAriaLabel && !hasAriaLabelledBy) {
170932
+ violations.push({
170933
+ id: "table-caption",
170934
+ description: "Table must have a caption or aria-label/aria-labelledby",
170935
+ element: table,
170936
+ impact: "serious"
170937
+ });
170938
+ }
170939
+ const headerCells = table.querySelectorAll("th");
170940
+ const dataCells = table.querySelectorAll("td");
170941
+ if (dataCells.length > 0 && headerCells.length === 0) {
170942
+ violations.push({
170943
+ id: "table-headers",
170944
+ description: "Table must have header cells (th elements) when it has data cells",
170945
+ element: table,
170946
+ impact: "serious"
170947
+ });
170948
+ }
170949
+ for (const th of Array.from(headerCells)) {
170950
+ if (!th.hasAttribute("scope")) {
170951
+ violations.push({
170952
+ id: "table-header-scope",
170953
+ description: "Table header cells (th) should have a scope attribute",
170954
+ element: th,
170955
+ impact: "moderate"
170956
+ });
170957
+ }
170958
+ }
170959
+ }
170960
+ return violations;
170961
+ }
170962
+ static checkDetailsSummary(element) {
170963
+ const violations = [];
170964
+ const detailsElements = element.getElementsByTagName("details");
170965
+ for (const details of Array.from(detailsElements)) {
170966
+ const firstChild = details.firstElementChild;
170967
+ if (!firstChild || firstChild.tagName.toLowerCase() !== "summary") {
170968
+ violations.push({
170969
+ id: "details-summary",
170970
+ description: "details element must have a summary element as its first child",
170971
+ element: details,
170972
+ impact: "serious"
170973
+ });
170974
+ } else if (!firstChild.textContent?.trim()) {
170975
+ violations.push({
170976
+ id: "details-summary-empty",
170977
+ description: "details summary element must have non-empty text content",
170978
+ element: details,
170979
+ impact: "serious"
170980
+ });
170981
+ }
170982
+ }
170983
+ return violations;
170984
+ }
170985
+ static checkVideoCaptions(element) {
170986
+ const violations = [];
170987
+ const videos = element.getElementsByTagName("video");
170988
+ for (const video of Array.from(videos)) {
170989
+ const tracks = video.querySelectorAll("track");
170990
+ const captionTracks = Array.from(tracks).filter(
170991
+ (track) => track.getAttribute("kind")?.toLowerCase() === "captions"
170992
+ );
170993
+ if (captionTracks.length === 0) {
170994
+ violations.push({
170995
+ id: "video-captions",
170996
+ description: 'Video element must have at least one track element with kind="captions"',
170997
+ element: video,
170998
+ impact: "serious"
170999
+ });
171000
+ } else {
171001
+ for (const track of captionTracks) {
171002
+ if (!track.hasAttribute("srclang")) {
171003
+ violations.push({
171004
+ id: "video-track-srclang",
171005
+ description: "Video caption track must have a srclang attribute",
171006
+ element: track,
171007
+ impact: "serious"
171008
+ });
171009
+ }
171010
+ if (!track.hasAttribute("label")) {
171011
+ violations.push({
171012
+ id: "video-track-label",
171013
+ description: "Video caption track should have a label attribute",
171014
+ element: track,
171015
+ impact: "moderate"
171016
+ });
171017
+ }
171018
+ }
171019
+ }
171020
+ }
171021
+ return violations;
171022
+ }
171023
+ static checkAudioCaptions(element) {
171024
+ const violations = [];
171025
+ const audios = element.getElementsByTagName("audio");
171026
+ for (const audio of Array.from(audios)) {
171027
+ const tracks = audio.querySelectorAll("track");
171028
+ const hasTranscript = audio.hasAttribute("aria-describedby") || audio.querySelector('a[href*="transcript"]') || audio.closest("div")?.querySelector('a[href*="transcript"]');
171029
+ if (tracks.length === 0 && !hasTranscript) {
171030
+ violations.push({
171031
+ id: "audio-captions",
171032
+ description: "Audio element must have track elements or a transcript link",
171033
+ element: audio,
171034
+ impact: "serious"
171035
+ });
171036
+ } else if (tracks.length > 0) {
171037
+ for (const track of Array.from(tracks)) {
171038
+ if (!track.hasAttribute("srclang")) {
171039
+ violations.push({
171040
+ id: "audio-track-srclang",
171041
+ description: "Audio track must have a srclang attribute",
171042
+ element: track,
171043
+ impact: "serious"
171044
+ });
171045
+ }
171046
+ if (!track.hasAttribute("label")) {
171047
+ violations.push({
171048
+ id: "audio-track-label",
171049
+ description: "Audio track should have a label attribute",
171050
+ element: track,
171051
+ impact: "moderate"
171052
+ });
171053
+ }
171054
+ }
171055
+ }
171056
+ }
171057
+ return violations;
171058
+ }
171059
+ static checkLandmarks(element) {
171060
+ const violations = [];
171061
+ const landmarkTags = ["nav", "main", "header", "footer", "aside", "section", "article"];
171062
+ const mainElements = element.getElementsByTagName("main");
171063
+ if (mainElements.length > 1) {
171064
+ const mains = Array.from(mainElements);
171065
+ for (let i = 1; i < mains.length; i++) {
171066
+ violations.push({
171067
+ id: "landmark-multiple-main",
171068
+ description: "Page should have only one main element",
171069
+ element: mains[i],
171070
+ impact: "serious"
171071
+ });
171072
+ }
171073
+ }
171074
+ for (const tag of landmarkTags) {
171075
+ const landmarks = element.getElementsByTagName(tag);
171076
+ for (const landmark of Array.from(landmarks)) {
171077
+ if (tag === "section" || tag === "article") {
171078
+ const hasHeading = landmark.querySelector("h1, h2, h3, h4, h5, h6");
171079
+ const hasAriaLabel = landmark.hasAttribute("aria-label");
171080
+ const hasAriaLabelledBy = landmark.hasAttribute("aria-labelledby");
171081
+ if (!hasHeading && !hasAriaLabel && !hasAriaLabelledBy) {
171082
+ violations.push({
171083
+ id: "landmark-missing-name",
171084
+ description: `${tag} element should have an accessible name (heading, aria-label, or aria-labelledby)`,
171085
+ element: landmark,
171086
+ impact: "moderate"
171087
+ });
171088
+ }
171089
+ }
171090
+ if (tag === "nav" || tag === "aside") {
171091
+ const hasAriaLabel = landmark.hasAttribute("aria-label");
171092
+ const hasAriaLabelledBy = landmark.hasAttribute("aria-labelledby");
171093
+ const sameType = Array.from(element.getElementsByTagName(tag));
171094
+ const unnamed = sameType.filter(
171095
+ (l) => !l.hasAttribute("aria-label") && !l.hasAttribute("aria-labelledby")
171096
+ );
171097
+ if (unnamed.length > 1 && !hasAriaLabel && !hasAriaLabelledBy) {
171098
+ violations.push({
171099
+ id: "landmark-duplicate-unnamed",
171100
+ description: `Multiple ${tag} elements found. Each should have an accessible name (aria-label or aria-labelledby)`,
171101
+ element: landmark,
171102
+ impact: "moderate"
171103
+ });
171104
+ }
171105
+ }
171106
+ }
171107
+ }
171108
+ return violations;
171109
+ }
171110
+ static checkDialogModal(element) {
171111
+ const violations = [];
171112
+ const dialogs = element.getElementsByTagName("dialog");
171113
+ for (const dialog of Array.from(dialogs)) {
171114
+ const hasAriaLabel = dialog.hasAttribute("aria-label");
171115
+ const hasAriaLabelledBy = dialog.hasAttribute("aria-labelledby");
171116
+ const hasHeading = dialog.querySelector("h1, h2, h3, h4, h5, h6");
171117
+ if (!hasAriaLabel && !hasAriaLabelledBy && !hasHeading) {
171118
+ violations.push({
171119
+ id: "dialog-missing-name",
171120
+ description: "Dialog element must have an accessible name (aria-label, aria-labelledby, or heading)",
171121
+ element: dialog,
171122
+ impact: "serious"
171123
+ });
171124
+ }
171125
+ const isModal = dialog.hasAttribute("open") || dialog.getAttribute("aria-modal") === "true";
171126
+ if (isModal && !dialog.hasAttribute("aria-modal")) {
171127
+ violations.push({
171128
+ id: "dialog-missing-modal",
171129
+ description: 'Modal dialog should have aria-modal="true" attribute',
171130
+ element: dialog,
171131
+ impact: "moderate"
171132
+ });
171133
+ }
171134
+ const hasRole = dialog.hasAttribute("role");
171135
+ const roleValue = dialog.getAttribute("role");
171136
+ if (hasRole && roleValue !== "dialog" && roleValue !== "alertdialog") {
171137
+ violations.push({
171138
+ id: "dialog-invalid-role",
171139
+ description: 'Dialog element should have role="dialog" or role="alertdialog"',
171140
+ element: dialog,
171141
+ impact: "moderate"
171142
+ });
171143
+ }
171144
+ }
171145
+ return violations;
171146
+ }
170885
171147
  static async check(element) {
170886
171148
  const violations = [
170887
171149
  ...this.checkImageAlt(element),
@@ -170889,7 +171151,14 @@ var A11yChecker = class {
170889
171151
  ...this.checkButtonLabel(element),
170890
171152
  ...this.checkFormLabels(element),
170891
171153
  ...this.checkHeadingOrder(element),
170892
- ...this.checkIframeTitle(element)
171154
+ ...this.checkIframeTitle(element),
171155
+ ...this.checkFieldsetLegend(element),
171156
+ ...this.checkTableStructure(element),
171157
+ ...this.checkDetailsSummary(element),
171158
+ ...this.checkVideoCaptions(element),
171159
+ ...this.checkAudioCaptions(element),
171160
+ ...this.checkLandmarks(element),
171161
+ ...this.checkDialogModal(element)
170893
171162
  ];
170894
171163
  if (violations.length > 0) {
170895
171164
  console.warn("\nAccessibility Violations Found:");
@@ -171845,11 +172114,863 @@ var rule6 = {
171845
172114
  };
171846
172115
  var iframe_title_default = rule6;
171847
172116
 
172117
+ // src/linter/eslint-plugin/rules/fieldset-legend.ts
172118
+ var rule7 = {
172119
+ meta: {
172120
+ type: "problem",
172121
+ docs: {
172122
+ description: "Enforce fieldset elements have a legend element as a direct child",
172123
+ category: "Accessibility",
172124
+ recommended: true,
172125
+ url: "https://github.com/nolrm/test-a11y-js"
172126
+ },
172127
+ messages: {
172128
+ missingLegend: "fieldset must have a legend element as a direct child",
172129
+ emptyLegend: "fieldset legend must have non-empty text content"
172130
+ },
172131
+ fixable: void 0,
172132
+ schema: []
172133
+ },
172134
+ create(context) {
172135
+ return {
172136
+ // Check JSX fieldset elements
172137
+ JSXOpeningElement(node) {
172138
+ const jsxNode = node;
172139
+ if (jsxNode.name?.name === "fieldset") {
172140
+ try {
172141
+ const element = jsxToElement(node, context);
172142
+ const violations = A11yChecker.checkFieldsetLegend(element);
172143
+ for (const violation of violations) {
172144
+ if (violation.id === "fieldset-legend") {
172145
+ context.report({
172146
+ node,
172147
+ messageId: "missingLegend"
172148
+ });
172149
+ } else if (violation.id === "fieldset-legend-empty") {
172150
+ context.report({
172151
+ node,
172152
+ messageId: "emptyLegend"
172153
+ });
172154
+ }
172155
+ }
172156
+ } catch (error2) {
172157
+ }
172158
+ }
172159
+ },
172160
+ // Check HTML strings
172161
+ Literal(node) {
172162
+ if (isHTMLLiteral(node)) {
172163
+ const element = htmlNodeToElement(node, context);
172164
+ if (element) {
172165
+ const violations = A11yChecker.checkFieldsetLegend(element);
172166
+ for (const violation of violations) {
172167
+ if (violation.id === "fieldset-legend") {
172168
+ context.report({
172169
+ node,
172170
+ messageId: "missingLegend"
172171
+ });
172172
+ } else if (violation.id === "fieldset-legend-empty") {
172173
+ context.report({
172174
+ node,
172175
+ messageId: "emptyLegend"
172176
+ });
172177
+ }
172178
+ }
172179
+ }
172180
+ }
172181
+ },
172182
+ TemplateLiteral(node) {
172183
+ if (isHTMLLiteral(node)) {
172184
+ const element = htmlNodeToElement(node, context);
172185
+ if (element) {
172186
+ const violations = A11yChecker.checkFieldsetLegend(element);
172187
+ for (const violation of violations) {
172188
+ if (violation.id === "fieldset-legend") {
172189
+ context.report({
172190
+ node,
172191
+ messageId: "missingLegend"
172192
+ });
172193
+ } else if (violation.id === "fieldset-legend-empty") {
172194
+ context.report({
172195
+ node,
172196
+ messageId: "emptyLegend"
172197
+ });
172198
+ }
172199
+ }
172200
+ }
172201
+ }
172202
+ },
172203
+ // Check Vue template fieldset elements
172204
+ VElement(node) {
172205
+ const vueNode = node;
172206
+ if (vueNode.name === "fieldset") {
172207
+ try {
172208
+ const element = vueElementToDOM(node, context);
172209
+ if (element) {
172210
+ const violations = A11yChecker.checkFieldsetLegend(element);
172211
+ for (const violation of violations) {
172212
+ if (violation.id === "fieldset-legend") {
172213
+ context.report({
172214
+ node,
172215
+ messageId: "missingLegend"
172216
+ });
172217
+ } else if (violation.id === "fieldset-legend-empty") {
172218
+ context.report({
172219
+ node,
172220
+ messageId: "emptyLegend"
172221
+ });
172222
+ }
172223
+ }
172224
+ }
172225
+ } catch (error2) {
172226
+ }
172227
+ }
172228
+ }
172229
+ };
172230
+ }
172231
+ };
172232
+ var fieldset_legend_default = rule7;
172233
+
172234
+ // src/linter/eslint-plugin/rules/table-structure.ts
172235
+ var rule8 = {
172236
+ meta: {
172237
+ type: "problem",
172238
+ docs: {
172239
+ description: "Enforce table elements have proper accessibility structure (caption, headers, scope)",
172240
+ category: "Accessibility",
172241
+ recommended: true,
172242
+ url: "https://github.com/nolrm/test-a11y-js"
172243
+ },
172244
+ messages: {
172245
+ missingCaption: "Table must have a caption or aria-label/aria-labelledby",
172246
+ missingHeaders: "Table must have header cells (th elements) when it has data cells",
172247
+ missingScope: "Table header cells (th) should have a scope attribute"
172248
+ },
172249
+ fixable: void 0,
172250
+ schema: []
172251
+ },
172252
+ create(context) {
172253
+ return {
172254
+ // Check JSX table elements
172255
+ JSXOpeningElement(node) {
172256
+ const jsxNode = node;
172257
+ if (jsxNode.name?.name === "table") {
172258
+ try {
172259
+ const element = jsxToElement(node, context);
172260
+ const violations = A11yChecker.checkTableStructure(element);
172261
+ for (const violation of violations) {
172262
+ if (violation.id === "table-caption") {
172263
+ context.report({
172264
+ node,
172265
+ messageId: "missingCaption"
172266
+ });
172267
+ } else if (violation.id === "table-headers") {
172268
+ context.report({
172269
+ node,
172270
+ messageId: "missingHeaders"
172271
+ });
172272
+ } else if (violation.id === "table-header-scope") {
172273
+ context.report({
172274
+ node: violation.element,
172275
+ messageId: "missingScope"
172276
+ });
172277
+ }
172278
+ }
172279
+ } catch (error2) {
172280
+ }
172281
+ }
172282
+ },
172283
+ // Check HTML strings
172284
+ Literal(node) {
172285
+ if (isHTMLLiteral(node)) {
172286
+ const element = htmlNodeToElement(node, context);
172287
+ if (element) {
172288
+ const violations = A11yChecker.checkTableStructure(element);
172289
+ for (const violation of violations) {
172290
+ if (violation.id === "table-caption") {
172291
+ context.report({
172292
+ node,
172293
+ messageId: "missingCaption"
172294
+ });
172295
+ } else if (violation.id === "table-headers") {
172296
+ context.report({
172297
+ node,
172298
+ messageId: "missingHeaders"
172299
+ });
172300
+ }
172301
+ }
172302
+ }
172303
+ }
172304
+ },
172305
+ TemplateLiteral(node) {
172306
+ if (isHTMLLiteral(node)) {
172307
+ const element = htmlNodeToElement(node, context);
172308
+ if (element) {
172309
+ const violations = A11yChecker.checkTableStructure(element);
172310
+ for (const violation of violations) {
172311
+ if (violation.id === "table-caption") {
172312
+ context.report({
172313
+ node,
172314
+ messageId: "missingCaption"
172315
+ });
172316
+ } else if (violation.id === "table-headers") {
172317
+ context.report({
172318
+ node,
172319
+ messageId: "missingHeaders"
172320
+ });
172321
+ }
172322
+ }
172323
+ }
172324
+ }
172325
+ },
172326
+ // Check Vue template table elements
172327
+ VElement(node) {
172328
+ const vueNode = node;
172329
+ if (vueNode.name === "table") {
172330
+ try {
172331
+ const element = vueElementToDOM(node, context);
172332
+ if (element) {
172333
+ const violations = A11yChecker.checkTableStructure(element);
172334
+ for (const violation of violations) {
172335
+ if (violation.id === "table-caption") {
172336
+ context.report({
172337
+ node,
172338
+ messageId: "missingCaption"
172339
+ });
172340
+ } else if (violation.id === "table-headers") {
172341
+ context.report({
172342
+ node,
172343
+ messageId: "missingHeaders"
172344
+ });
172345
+ } else if (violation.id === "table-header-scope") {
172346
+ context.report({
172347
+ node: violation.element,
172348
+ messageId: "missingScope"
172349
+ });
172350
+ }
172351
+ }
172352
+ }
172353
+ } catch (error2) {
172354
+ }
172355
+ }
172356
+ }
172357
+ };
172358
+ }
172359
+ };
172360
+ var table_structure_default = rule8;
172361
+
172362
+ // src/linter/eslint-plugin/rules/details-summary.ts
172363
+ var rule9 = {
172364
+ meta: {
172365
+ type: "problem",
172366
+ docs: {
172367
+ description: "Enforce details elements have a summary element as first child",
172368
+ category: "Accessibility",
172369
+ recommended: true,
172370
+ url: "https://github.com/nolrm/test-a11y-js"
172371
+ },
172372
+ messages: {
172373
+ missingSummary: "details element must have a summary element as its first child",
172374
+ emptySummary: "details summary element must have non-empty text content"
172375
+ },
172376
+ fixable: void 0,
172377
+ schema: []
172378
+ },
172379
+ create(context) {
172380
+ return {
172381
+ // Check JSX details elements
172382
+ JSXOpeningElement(node) {
172383
+ const jsxNode = node;
172384
+ if (jsxNode.name?.name === "details") {
172385
+ try {
172386
+ const element = jsxToElement(node, context);
172387
+ const violations = A11yChecker.checkDetailsSummary(element);
172388
+ for (const violation of violations) {
172389
+ if (violation.id === "details-summary") {
172390
+ context.report({
172391
+ node,
172392
+ messageId: "missingSummary"
172393
+ });
172394
+ } else if (violation.id === "details-summary-empty") {
172395
+ context.report({
172396
+ node,
172397
+ messageId: "emptySummary"
172398
+ });
172399
+ }
172400
+ }
172401
+ } catch (error2) {
172402
+ }
172403
+ }
172404
+ },
172405
+ // Check HTML strings
172406
+ Literal(node) {
172407
+ if (isHTMLLiteral(node)) {
172408
+ const element = htmlNodeToElement(node, context);
172409
+ if (element) {
172410
+ const violations = A11yChecker.checkDetailsSummary(element);
172411
+ for (const violation of violations) {
172412
+ if (violation.id === "details-summary") {
172413
+ context.report({
172414
+ node,
172415
+ messageId: "missingSummary"
172416
+ });
172417
+ } else if (violation.id === "details-summary-empty") {
172418
+ context.report({
172419
+ node,
172420
+ messageId: "emptySummary"
172421
+ });
172422
+ }
172423
+ }
172424
+ }
172425
+ }
172426
+ },
172427
+ TemplateLiteral(node) {
172428
+ if (isHTMLLiteral(node)) {
172429
+ const element = htmlNodeToElement(node, context);
172430
+ if (element) {
172431
+ const violations = A11yChecker.checkDetailsSummary(element);
172432
+ for (const violation of violations) {
172433
+ if (violation.id === "details-summary") {
172434
+ context.report({
172435
+ node,
172436
+ messageId: "missingSummary"
172437
+ });
172438
+ } else if (violation.id === "details-summary-empty") {
172439
+ context.report({
172440
+ node,
172441
+ messageId: "emptySummary"
172442
+ });
172443
+ }
172444
+ }
172445
+ }
172446
+ }
172447
+ },
172448
+ // Check Vue template details elements
172449
+ VElement(node) {
172450
+ const vueNode = node;
172451
+ if (vueNode.name === "details") {
172452
+ try {
172453
+ const element = vueElementToDOM(node, context);
172454
+ if (element) {
172455
+ const violations = A11yChecker.checkDetailsSummary(element);
172456
+ for (const violation of violations) {
172457
+ if (violation.id === "details-summary") {
172458
+ context.report({
172459
+ node,
172460
+ messageId: "missingSummary"
172461
+ });
172462
+ } else if (violation.id === "details-summary-empty") {
172463
+ context.report({
172464
+ node,
172465
+ messageId: "emptySummary"
172466
+ });
172467
+ }
172468
+ }
172469
+ }
172470
+ } catch (error2) {
172471
+ }
172472
+ }
172473
+ }
172474
+ };
172475
+ }
172476
+ };
172477
+ var details_summary_default = rule9;
172478
+
172479
+ // src/linter/eslint-plugin/rules/video-captions.ts
172480
+ var rule10 = {
172481
+ meta: {
172482
+ type: "problem",
172483
+ docs: {
172484
+ description: "Enforce video elements have caption tracks",
172485
+ category: "Accessibility",
172486
+ recommended: true,
172487
+ url: "https://github.com/nolrm/test-a11y-js"
172488
+ },
172489
+ messages: {
172490
+ missingCaptions: 'Video element must have at least one track element with kind="captions"',
172491
+ missingSrclang: "Video caption track must have a srclang attribute",
172492
+ missingLabel: "Video caption track should have a label attribute"
172493
+ },
172494
+ fixable: void 0,
172495
+ schema: []
172496
+ },
172497
+ create(context) {
172498
+ return {
172499
+ // Check JSX video elements
172500
+ JSXOpeningElement(node) {
172501
+ const jsxNode = node;
172502
+ if (jsxNode.name?.name === "video") {
172503
+ try {
172504
+ const element = jsxToElement(node, context);
172505
+ const violations = A11yChecker.checkVideoCaptions(element);
172506
+ for (const violation of violations) {
172507
+ if (violation.id === "video-captions") {
172508
+ context.report({
172509
+ node,
172510
+ messageId: "missingCaptions"
172511
+ });
172512
+ } else if (violation.id === "video-track-srclang") {
172513
+ context.report({
172514
+ node: violation.element,
172515
+ messageId: "missingSrclang"
172516
+ });
172517
+ } else if (violation.id === "video-track-label") {
172518
+ context.report({
172519
+ node: violation.element,
172520
+ messageId: "missingLabel"
172521
+ });
172522
+ }
172523
+ }
172524
+ } catch (error2) {
172525
+ }
172526
+ }
172527
+ },
172528
+ // Check HTML strings
172529
+ Literal(node) {
172530
+ if (isHTMLLiteral(node)) {
172531
+ const element = htmlNodeToElement(node, context);
172532
+ if (element) {
172533
+ const violations = A11yChecker.checkVideoCaptions(element);
172534
+ for (const violation of violations) {
172535
+ if (violation.id === "video-captions") {
172536
+ context.report({
172537
+ node,
172538
+ messageId: "missingCaptions"
172539
+ });
172540
+ }
172541
+ }
172542
+ }
172543
+ }
172544
+ },
172545
+ TemplateLiteral(node) {
172546
+ if (isHTMLLiteral(node)) {
172547
+ const element = htmlNodeToElement(node, context);
172548
+ if (element) {
172549
+ const violations = A11yChecker.checkVideoCaptions(element);
172550
+ for (const violation of violations) {
172551
+ if (violation.id === "video-captions") {
172552
+ context.report({
172553
+ node,
172554
+ messageId: "missingCaptions"
172555
+ });
172556
+ }
172557
+ }
172558
+ }
172559
+ }
172560
+ },
172561
+ // Check Vue template video elements
172562
+ VElement(node) {
172563
+ const vueNode = node;
172564
+ if (vueNode.name === "video") {
172565
+ try {
172566
+ const element = vueElementToDOM(node, context);
172567
+ if (element) {
172568
+ const violations = A11yChecker.checkVideoCaptions(element);
172569
+ for (const violation of violations) {
172570
+ if (violation.id === "video-captions") {
172571
+ context.report({
172572
+ node,
172573
+ messageId: "missingCaptions"
172574
+ });
172575
+ } else if (violation.id === "video-track-srclang") {
172576
+ context.report({
172577
+ node: violation.element,
172578
+ messageId: "missingSrclang"
172579
+ });
172580
+ } else if (violation.id === "video-track-label") {
172581
+ context.report({
172582
+ node: violation.element,
172583
+ messageId: "missingLabel"
172584
+ });
172585
+ }
172586
+ }
172587
+ }
172588
+ } catch (error2) {
172589
+ }
172590
+ }
172591
+ }
172592
+ };
172593
+ }
172594
+ };
172595
+ var video_captions_default = rule10;
172596
+
172597
+ // src/linter/eslint-plugin/rules/audio-captions.ts
172598
+ var rule11 = {
172599
+ meta: {
172600
+ type: "problem",
172601
+ docs: {
172602
+ description: "Enforce audio elements have caption tracks or transcripts",
172603
+ category: "Accessibility",
172604
+ recommended: true,
172605
+ url: "https://github.com/nolrm/test-a11y-js"
172606
+ },
172607
+ messages: {
172608
+ missingCaptions: "Audio element must have track elements or a transcript link",
172609
+ missingSrclang: "Audio track must have a srclang attribute",
172610
+ missingLabel: "Audio track should have a label attribute"
172611
+ },
172612
+ fixable: void 0,
172613
+ schema: []
172614
+ },
172615
+ create(context) {
172616
+ return {
172617
+ // Check JSX audio elements
172618
+ JSXOpeningElement(node) {
172619
+ const jsxNode = node;
172620
+ if (jsxNode.name?.name === "audio") {
172621
+ try {
172622
+ const element = jsxToElement(node, context);
172623
+ const violations = A11yChecker.checkAudioCaptions(element);
172624
+ for (const violation of violations) {
172625
+ if (violation.id === "audio-captions") {
172626
+ context.report({
172627
+ node,
172628
+ messageId: "missingCaptions"
172629
+ });
172630
+ } else if (violation.id === "audio-track-srclang") {
172631
+ context.report({
172632
+ node: violation.element,
172633
+ messageId: "missingSrclang"
172634
+ });
172635
+ } else if (violation.id === "audio-track-label") {
172636
+ context.report({
172637
+ node: violation.element,
172638
+ messageId: "missingLabel"
172639
+ });
172640
+ }
172641
+ }
172642
+ } catch (error2) {
172643
+ }
172644
+ }
172645
+ },
172646
+ // Check HTML strings
172647
+ Literal(node) {
172648
+ if (isHTMLLiteral(node)) {
172649
+ const element = htmlNodeToElement(node, context);
172650
+ if (element) {
172651
+ const violations = A11yChecker.checkAudioCaptions(element);
172652
+ for (const violation of violations) {
172653
+ if (violation.id === "audio-captions") {
172654
+ context.report({
172655
+ node,
172656
+ messageId: "missingCaptions"
172657
+ });
172658
+ }
172659
+ }
172660
+ }
172661
+ }
172662
+ },
172663
+ TemplateLiteral(node) {
172664
+ if (isHTMLLiteral(node)) {
172665
+ const element = htmlNodeToElement(node, context);
172666
+ if (element) {
172667
+ const violations = A11yChecker.checkAudioCaptions(element);
172668
+ for (const violation of violations) {
172669
+ if (violation.id === "audio-captions") {
172670
+ context.report({
172671
+ node,
172672
+ messageId: "missingCaptions"
172673
+ });
172674
+ }
172675
+ }
172676
+ }
172677
+ }
172678
+ },
172679
+ // Check Vue template audio elements
172680
+ VElement(node) {
172681
+ const vueNode = node;
172682
+ if (vueNode.name === "audio") {
172683
+ try {
172684
+ const element = vueElementToDOM(node, context);
172685
+ if (element) {
172686
+ const violations = A11yChecker.checkAudioCaptions(element);
172687
+ for (const violation of violations) {
172688
+ if (violation.id === "audio-captions") {
172689
+ context.report({
172690
+ node,
172691
+ messageId: "missingCaptions"
172692
+ });
172693
+ } else if (violation.id === "audio-track-srclang") {
172694
+ context.report({
172695
+ node: violation.element,
172696
+ messageId: "missingSrclang"
172697
+ });
172698
+ } else if (violation.id === "audio-track-label") {
172699
+ context.report({
172700
+ node: violation.element,
172701
+ messageId: "missingLabel"
172702
+ });
172703
+ }
172704
+ }
172705
+ }
172706
+ } catch (error2) {
172707
+ }
172708
+ }
172709
+ }
172710
+ };
172711
+ }
172712
+ };
172713
+ var audio_captions_default = rule11;
172714
+
172715
+ // src/linter/eslint-plugin/rules/landmark-roles.ts
172716
+ var rule12 = {
172717
+ meta: {
172718
+ type: "problem",
172719
+ docs: {
172720
+ description: "Enforce proper use of landmark elements (nav, main, header, footer, aside, section, article)",
172721
+ category: "Accessibility",
172722
+ recommended: true,
172723
+ url: "https://github.com/nolrm/test-a11y-js"
172724
+ },
172725
+ messages: {
172726
+ multipleMain: "Page should have only one main element",
172727
+ missingName: "Landmark element should have an accessible name (heading, aria-label, or aria-labelledby)",
172728
+ duplicateUnnamed: "Multiple landmark elements found. Each should have an accessible name (aria-label or aria-labelledby)"
172729
+ },
172730
+ fixable: void 0,
172731
+ schema: []
172732
+ },
172733
+ create(context) {
172734
+ return {
172735
+ // Check JSX landmark elements
172736
+ JSXOpeningElement(node) {
172737
+ const jsxNode = node;
172738
+ const tagName = jsxNode.name?.name?.toLowerCase();
172739
+ const landmarkTags = ["nav", "main", "header", "footer", "aside", "section", "article"];
172740
+ if (landmarkTags.includes(tagName)) {
172741
+ try {
172742
+ const element = jsxToElement(node, context);
172743
+ const violations = A11yChecker.checkLandmarks(element);
172744
+ for (const violation of violations) {
172745
+ if (violation.id === "landmark-multiple-main") {
172746
+ context.report({
172747
+ node,
172748
+ messageId: "multipleMain"
172749
+ });
172750
+ } else if (violation.id === "landmark-missing-name") {
172751
+ context.report({
172752
+ node,
172753
+ messageId: "missingName"
172754
+ });
172755
+ } else if (violation.id === "landmark-duplicate-unnamed") {
172756
+ context.report({
172757
+ node,
172758
+ messageId: "duplicateUnnamed"
172759
+ });
172760
+ }
172761
+ }
172762
+ } catch (error2) {
172763
+ }
172764
+ }
172765
+ },
172766
+ // Check HTML strings
172767
+ Literal(node) {
172768
+ if (isHTMLLiteral(node)) {
172769
+ const element = htmlNodeToElement(node, context);
172770
+ if (element) {
172771
+ const violations = A11yChecker.checkLandmarks(element);
172772
+ for (const violation of violations) {
172773
+ if (violation.id === "landmark-multiple-main") {
172774
+ context.report({
172775
+ node,
172776
+ messageId: "multipleMain"
172777
+ });
172778
+ } else if (violation.id === "landmark-missing-name") {
172779
+ context.report({
172780
+ node,
172781
+ messageId: "missingName"
172782
+ });
172783
+ }
172784
+ }
172785
+ }
172786
+ }
172787
+ },
172788
+ TemplateLiteral(node) {
172789
+ if (isHTMLLiteral(node)) {
172790
+ const element = htmlNodeToElement(node, context);
172791
+ if (element) {
172792
+ const violations = A11yChecker.checkLandmarks(element);
172793
+ for (const violation of violations) {
172794
+ if (violation.id === "landmark-multiple-main") {
172795
+ context.report({
172796
+ node,
172797
+ messageId: "multipleMain"
172798
+ });
172799
+ } else if (violation.id === "landmark-missing-name") {
172800
+ context.report({
172801
+ node,
172802
+ messageId: "missingName"
172803
+ });
172804
+ }
172805
+ }
172806
+ }
172807
+ }
172808
+ },
172809
+ // Check Vue template landmark elements
172810
+ VElement(node) {
172811
+ const vueNode = node;
172812
+ const tagName = vueNode.name?.toLowerCase();
172813
+ const landmarkTags = ["nav", "main", "header", "footer", "aside", "section", "article"];
172814
+ if (landmarkTags.includes(tagName)) {
172815
+ try {
172816
+ const element = vueElementToDOM(node, context);
172817
+ if (element) {
172818
+ const violations = A11yChecker.checkLandmarks(element);
172819
+ for (const violation of violations) {
172820
+ if (violation.id === "landmark-multiple-main") {
172821
+ context.report({
172822
+ node,
172823
+ messageId: "multipleMain"
172824
+ });
172825
+ } else if (violation.id === "landmark-missing-name") {
172826
+ context.report({
172827
+ node,
172828
+ messageId: "missingName"
172829
+ });
172830
+ } else if (violation.id === "landmark-duplicate-unnamed") {
172831
+ context.report({
172832
+ node,
172833
+ messageId: "duplicateUnnamed"
172834
+ });
172835
+ }
172836
+ }
172837
+ }
172838
+ } catch (error2) {
172839
+ }
172840
+ }
172841
+ }
172842
+ };
172843
+ }
172844
+ };
172845
+ var landmark_roles_default = rule12;
172846
+
172847
+ // src/linter/eslint-plugin/rules/dialog-modal.ts
172848
+ var rule13 = {
172849
+ meta: {
172850
+ type: "problem",
172851
+ docs: {
172852
+ description: "Enforce dialog elements have proper accessibility attributes",
172853
+ category: "Accessibility",
172854
+ recommended: true,
172855
+ url: "https://github.com/nolrm/test-a11y-js"
172856
+ },
172857
+ messages: {
172858
+ missingName: "Dialog element must have an accessible name (aria-label, aria-labelledby, or heading)",
172859
+ missingModal: 'Modal dialog should have aria-modal="true" attribute',
172860
+ invalidRole: 'Dialog element should have role="dialog" or role="alertdialog"'
172861
+ },
172862
+ fixable: void 0,
172863
+ schema: []
172864
+ },
172865
+ create(context) {
172866
+ return {
172867
+ // Check JSX dialog elements
172868
+ JSXOpeningElement(node) {
172869
+ const jsxNode = node;
172870
+ if (jsxNode.name?.name === "dialog" || jsxNode.name?.name && jsxNode.attributes?.some(
172871
+ (attr) => attr.name?.name === "role" && (attr.value?.value === "dialog" || attr.value?.value === "alertdialog")
172872
+ )) {
172873
+ try {
172874
+ const element = jsxToElement(node, context);
172875
+ const violations = A11yChecker.checkDialogModal(element);
172876
+ for (const violation of violations) {
172877
+ if (violation.id === "dialog-missing-name") {
172878
+ context.report({
172879
+ node,
172880
+ messageId: "missingName"
172881
+ });
172882
+ } else if (violation.id === "dialog-missing-modal") {
172883
+ context.report({
172884
+ node,
172885
+ messageId: "missingModal"
172886
+ });
172887
+ } else if (violation.id === "dialog-invalid-role") {
172888
+ context.report({
172889
+ node,
172890
+ messageId: "invalidRole"
172891
+ });
172892
+ }
172893
+ }
172894
+ } catch (error2) {
172895
+ }
172896
+ }
172897
+ },
172898
+ // Check HTML strings
172899
+ Literal(node) {
172900
+ if (isHTMLLiteral(node)) {
172901
+ const element = htmlNodeToElement(node, context);
172902
+ if (element) {
172903
+ const violations = A11yChecker.checkDialogModal(element);
172904
+ for (const violation of violations) {
172905
+ if (violation.id === "dialog-missing-name") {
172906
+ context.report({
172907
+ node,
172908
+ messageId: "missingName"
172909
+ });
172910
+ }
172911
+ }
172912
+ }
172913
+ }
172914
+ },
172915
+ TemplateLiteral(node) {
172916
+ if (isHTMLLiteral(node)) {
172917
+ const element = htmlNodeToElement(node, context);
172918
+ if (element) {
172919
+ const violations = A11yChecker.checkDialogModal(element);
172920
+ for (const violation of violations) {
172921
+ if (violation.id === "dialog-missing-name") {
172922
+ context.report({
172923
+ node,
172924
+ messageId: "missingName"
172925
+ });
172926
+ }
172927
+ }
172928
+ }
172929
+ }
172930
+ },
172931
+ // Check Vue template dialog elements
172932
+ VElement(node) {
172933
+ const vueNode = node;
172934
+ if (vueNode.name === "dialog" || vueNode.startTag?.attributes?.some(
172935
+ (attr) => attr.key?.name === "role" && (attr.value?.value === "dialog" || attr.value?.value === "alertdialog")
172936
+ )) {
172937
+ try {
172938
+ const element = vueElementToDOM(node, context);
172939
+ if (element) {
172940
+ const violations = A11yChecker.checkDialogModal(element);
172941
+ for (const violation of violations) {
172942
+ if (violation.id === "dialog-missing-name") {
172943
+ context.report({
172944
+ node,
172945
+ messageId: "missingName"
172946
+ });
172947
+ } else if (violation.id === "dialog-missing-modal") {
172948
+ context.report({
172949
+ node,
172950
+ messageId: "missingModal"
172951
+ });
172952
+ } else if (violation.id === "dialog-invalid-role") {
172953
+ context.report({
172954
+ node,
172955
+ messageId: "invalidRole"
172956
+ });
172957
+ }
172958
+ }
172959
+ }
172960
+ } catch (error2) {
172961
+ }
172962
+ }
172963
+ }
172964
+ };
172965
+ }
172966
+ };
172967
+ var dialog_modal_default = rule13;
172968
+
171848
172969
  // src/linter/eslint-plugin/index.ts
171849
172970
  var plugin = {
171850
172971
  meta: {
171851
172972
  name: "test-a11y-js",
171852
- version: "0.2.0"
172973
+ version: "0.6.2"
171853
172974
  },
171854
172975
  rules: {
171855
172976
  "image-alt": image_alt_default,
@@ -171857,7 +172978,14 @@ var plugin = {
171857
172978
  "link-text": link_text_default,
171858
172979
  "form-label": form_label_default,
171859
172980
  "heading-order": heading_order_default,
171860
- "iframe-title": iframe_title_default
172981
+ "iframe-title": iframe_title_default,
172982
+ "fieldset-legend": fieldset_legend_default,
172983
+ "table-structure": table_structure_default,
172984
+ "details-summary": details_summary_default,
172985
+ "video-captions": video_captions_default,
172986
+ "audio-captions": audio_captions_default,
172987
+ "landmark-roles": landmark_roles_default,
172988
+ "dialog-modal": dialog_modal_default
171861
172989
  },
171862
172990
  configs: {
171863
172991
  recommended: {