praxis-kit 6.2.3 → 6.5.0

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.
@@ -1003,7 +1003,11 @@ var ATTRIBUTE_IGNORED_CODES = {
1003
1003
  max: DiagnosticCode3.HtmlInputMaxIgnoredForType,
1004
1004
  step: DiagnosticCode3.HtmlInputStepIgnoredForType,
1005
1005
  accept: DiagnosticCode3.HtmlInputAcceptIgnoredForType,
1006
- capture: DiagnosticCode3.HtmlInputCaptureIgnoredForType
1006
+ capture: DiagnosticCode3.HtmlInputCaptureIgnoredForType,
1007
+ size: DiagnosticCode3.HtmlInputSizeIgnoredForType,
1008
+ alt: DiagnosticCode3.HtmlInputAltIgnoredForType,
1009
+ height: DiagnosticCode3.HtmlInputHeightIgnoredForType,
1010
+ width: DiagnosticCode3.HtmlInputWidthIgnoredForType
1007
1011
  };
1008
1012
  var HtmlDiagnostics = {
1009
1013
  emptyRole(tag) {
@@ -1237,8 +1241,132 @@ var InvariantBase = class {
1237
1241
  }
1238
1242
  };
1239
1243
 
1240
- // ../../lib/contract/src/aria/polymorphic-validator.ts
1244
+ // ../../lib/contract/src/aria/spec/roles/required-properties.ts
1245
+ var REQUIRED_ARIA_PROPERTIES = {
1246
+ combobox: ["aria-expanded"],
1247
+ option: ["aria-selected"],
1248
+ slider: ["aria-valuenow"],
1249
+ scrollbar: ["aria-controls", "aria-valuenow"],
1250
+ spinbutton: ["aria-valuenow"]
1251
+ };
1252
+
1253
+ // ../../lib/contract/src/aria/spec/roles/name-required.ts
1254
+ var NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["img"]);
1255
+
1256
+ // ../../lib/contract/src/aria/spec/validators/required-properties-validator.ts
1241
1257
  var NO_VIOLATIONS = [{ valid: true }];
1258
+ function requiredAttributeByRole(roles, attribute) {
1259
+ return Object.fromEntries([...roles].map((role) => [role, [attribute]]));
1260
+ }
1261
+ function checkRequiredAttributes(requirement, { props, effectiveRole }) {
1262
+ if (!effectiveRole) return NO_VIOLATIONS;
1263
+ const requiredAttributes = requirement.attributesByRole[effectiveRole];
1264
+ if (!requiredAttributes) return NO_VIOLATIONS;
1265
+ const results = [];
1266
+ for (const attribute of requiredAttributes) {
1267
+ if (attribute in props) continue;
1268
+ results.push({
1269
+ valid: false,
1270
+ fixable: false,
1271
+ severity: "warning",
1272
+ attribute,
1273
+ diagnostic: requirement.diagnosticFor(attribute, effectiveRole)
1274
+ });
1275
+ }
1276
+ return results;
1277
+ }
1278
+
1279
+ // ../../lib/contract/src/aria/spec/roles/live-region.ts
1280
+ var LIVE_REGION_ROLES = /* @__PURE__ */ new Map([
1281
+ ["alert", "assertive"],
1282
+ ["status", "polite"],
1283
+ ["log", "polite"],
1284
+ ["timer", "off"]
1285
+ ]);
1286
+ var ATOMIC_REQUIREMENTS = requiredAttributeByRole(LIVE_REGION_ROLES.keys(), "aria-atomic");
1287
+
1288
+ // ../../lib/contract/src/aria/spec/attributes/aria-value-types.ts
1289
+ var ARIA_VALUE_TYPES = /* @__PURE__ */ new Map([
1290
+ // Boolean (true | false)
1291
+ ["aria-atomic", { kind: "boolean" }],
1292
+ ["aria-busy", { kind: "boolean" }],
1293
+ ["aria-disabled", { kind: "boolean" }],
1294
+ ["aria-expanded", { kind: "boolean" }],
1295
+ ["aria-hidden", { kind: "boolean" }],
1296
+ ["aria-modal", { kind: "boolean" }],
1297
+ ["aria-multiline", { kind: "boolean" }],
1298
+ ["aria-multiselectable", { kind: "boolean" }],
1299
+ ["aria-readonly", { kind: "boolean" }],
1300
+ ["aria-required", { kind: "boolean" }],
1301
+ ["aria-selected", { kind: "boolean" }],
1302
+ // Tristate (true | false | mixed)
1303
+ ["aria-checked", { kind: "tristate" }],
1304
+ ["aria-pressed", { kind: "tristate" }],
1305
+ // Numeric (any finite number)
1306
+ ["aria-valuenow", { kind: "number" }],
1307
+ ["aria-valuemin", { kind: "number" }],
1308
+ ["aria-valuemax", { kind: "number" }],
1309
+ // Integer with optional range
1310
+ ["aria-level", { kind: "integer", min: 1, max: 6 }],
1311
+ ["aria-posinset", { kind: "integer", min: 1 }],
1312
+ ["aria-setsize", { kind: "integer", min: -1 }],
1313
+ ["aria-rowcount", { kind: "integer", min: -1 }],
1314
+ ["aria-colcount", { kind: "integer", min: -1 }],
1315
+ ["aria-rowindex", { kind: "integer", min: 1 }],
1316
+ ["aria-colindex", { kind: "integer", min: 1 }],
1317
+ ["aria-rowspan", { kind: "integer", min: 0 }],
1318
+ ["aria-colspan", { kind: "integer", min: 0 }],
1319
+ // Enum (specific allowed tokens)
1320
+ ["aria-autocomplete", { kind: "enum", values: /* @__PURE__ */ new Set(["inline", "list", "both", "none"]) }],
1321
+ [
1322
+ "aria-current",
1323
+ {
1324
+ kind: "enum",
1325
+ values: /* @__PURE__ */ new Set(["page", "step", "location", "date", "time", "true", "false"])
1326
+ }
1327
+ ],
1328
+ [
1329
+ "aria-haspopup",
1330
+ {
1331
+ kind: "enum",
1332
+ values: /* @__PURE__ */ new Set(["false", "true", "menu", "listbox", "tree", "grid", "dialog"])
1333
+ }
1334
+ ],
1335
+ ["aria-invalid", { kind: "enum", values: /* @__PURE__ */ new Set(["grammar", "false", "spelling", "true"]) }],
1336
+ ["aria-live", { kind: "enum", values: /* @__PURE__ */ new Set(["assertive", "off", "polite"]) }],
1337
+ ["aria-orientation", { kind: "enum", values: /* @__PURE__ */ new Set(["horizontal", "vertical", "undefined"]) }],
1338
+ ["aria-sort", { kind: "enum", values: /* @__PURE__ */ new Set(["ascending", "descending", "none", "other"]) }]
1339
+ ]);
1340
+
1341
+ // ../../lib/contract/src/aria/spec/attributes/aria-relevant-tokens.ts
1342
+ var VALID_RELEVANT_TOKENS = /* @__PURE__ */ new Set([
1343
+ "additions",
1344
+ "removals",
1345
+ "text",
1346
+ "all"
1347
+ ]);
1348
+
1349
+ // ../../lib/contract/src/aria/spec/elements/heading-implicit-levels.ts
1350
+ var HEADING_IMPLICIT_LEVELS = /* @__PURE__ */ new Map([
1351
+ ["h1", 1],
1352
+ ["h2", 2],
1353
+ ["h3", 3],
1354
+ ["h4", 4],
1355
+ ["h5", 5],
1356
+ ["h6", 6]
1357
+ ]);
1358
+
1359
+ // ../../lib/contract/src/aria/spec/elements/interactive-tags.ts
1360
+ var INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
1361
+ "a",
1362
+ "button",
1363
+ "input",
1364
+ "select",
1365
+ "textarea"
1366
+ ]);
1367
+
1368
+ // ../../lib/contract/src/aria/polymorphic-validator.ts
1369
+ var NO_VIOLATIONS2 = [{ valid: true }];
1242
1370
  function isIntrinsicTag(tag) {
1243
1371
  return isString(tag);
1244
1372
  }
@@ -1300,6 +1428,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1300
1428
  const violations = [];
1301
1429
  const fixes = [];
1302
1430
  iterate.forEach(rules, (rule) => {
1431
+ if (isNonNull(rule.tags) && !rule.tags.includes(context.tag)) return;
1303
1432
  iterate.forEach(rule(context), (result) => {
1304
1433
  if (result.valid) return;
1305
1434
  const {
@@ -1325,7 +1454,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1325
1454
  return { violations, fixes };
1326
1455
  }
1327
1456
  static #getRules(context) {
1328
- if (_AriaPolicyEngine.#hasRole(context.props) || isNonNull(context.effectiveRole) && _AriaPolicyEngine.#LIVE_REGION_ROLES.has(context.effectiveRole)) {
1457
+ if (_AriaPolicyEngine.#hasRole(context.props) || isNonNull(context.effectiveRole) && LIVE_REGION_ROLES.has(context.effectiveRole)) {
1329
1458
  return _AriaPolicyEngine.#pipeline;
1330
1459
  }
1331
1460
  return _AriaPolicyEngine.#implicitOnlyRules;
@@ -1527,7 +1656,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1527
1656
  implicitRole
1528
1657
  }) {
1529
1658
  const role = props.role;
1530
- if (!implicitRole || !role || role === implicitRole) return NO_VIOLATIONS;
1659
+ if (!implicitRole || !role || role === implicitRole) return NO_VIOLATIONS2;
1531
1660
  if (isStrongImplicitRole(tag) && role === "region") {
1532
1661
  const diagnostic = HtmlDiagnostics.implicitRoleOverride(tag, implicitRole, role);
1533
1662
  return [
@@ -1540,11 +1669,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1540
1669
  }
1541
1670
  ];
1542
1671
  }
1543
- return NO_VIOLATIONS;
1672
+ return NO_VIOLATIONS2;
1544
1673
  }
1545
1674
  static #checkRedundantRole({ tag, props, implicitRole }) {
1546
1675
  const role = props.role;
1547
- if (!implicitRole || !role || role !== implicitRole) return NO_VIOLATIONS;
1676
+ if (!implicitRole || !role || role !== implicitRole) return NO_VIOLATIONS2;
1548
1677
  const diagnostic = HtmlDiagnostics.implicitRoleRedundant(tag, implicitRole);
1549
1678
  return [
1550
1679
  {
@@ -1558,8 +1687,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1558
1687
  }
1559
1688
  static #checkStandaloneRegion({ tag, props, implicitRole }) {
1560
1689
  const role = props.role;
1561
- if (role !== "region") return NO_VIOLATIONS;
1562
- if (!isStandaloneTag(tag)) return NO_VIOLATIONS;
1690
+ if (role !== "region") return NO_VIOLATIONS2;
1691
+ if (!isStandaloneTag(tag)) return NO_VIOLATIONS2;
1563
1692
  const diagnostic = HtmlDiagnostics.standaloneRegionOverride(tag, implicitRole ?? tag);
1564
1693
  return [
1565
1694
  {
@@ -1576,7 +1705,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1576
1705
  props,
1577
1706
  effectiveRole
1578
1707
  }) {
1579
- if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS;
1708
+ if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
1580
1709
  const results = [];
1581
1710
  iterate.forEachEntry(props, (key) => {
1582
1711
  if (!key.startsWith("aria-")) return;
@@ -1594,62 +1723,6 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1594
1723
  return results;
1595
1724
  }
1596
1725
  // ─── ARIA attribute value validation ──────────────────────────────────────
1597
- // Accepted value shapes for typed ARIA attributes.
1598
- // Attributes not in this map are unconstrained (arbitrary string values permitted).
1599
- static #ARIA_VALUE_TYPES = /* @__PURE__ */ new Map([
1600
- // Boolean (true | false)
1601
- ["aria-atomic", { kind: "boolean" }],
1602
- ["aria-busy", { kind: "boolean" }],
1603
- ["aria-disabled", { kind: "boolean" }],
1604
- ["aria-expanded", { kind: "boolean" }],
1605
- ["aria-hidden", { kind: "boolean" }],
1606
- ["aria-modal", { kind: "boolean" }],
1607
- ["aria-multiline", { kind: "boolean" }],
1608
- ["aria-multiselectable", { kind: "boolean" }],
1609
- ["aria-readonly", { kind: "boolean" }],
1610
- ["aria-required", { kind: "boolean" }],
1611
- ["aria-selected", { kind: "boolean" }],
1612
- // Tristate (true | false | mixed)
1613
- ["aria-checked", { kind: "tristate" }],
1614
- ["aria-pressed", { kind: "tristate" }],
1615
- // Numeric (any finite number)
1616
- ["aria-valuenow", { kind: "number" }],
1617
- ["aria-valuemin", { kind: "number" }],
1618
- ["aria-valuemax", { kind: "number" }],
1619
- // Integer with optional range
1620
- ["aria-level", { kind: "integer", min: 1, max: 6 }],
1621
- ["aria-posinset", { kind: "integer", min: 1 }],
1622
- ["aria-setsize", { kind: "integer", min: -1 }],
1623
- ["aria-rowcount", { kind: "integer", min: -1 }],
1624
- ["aria-colcount", { kind: "integer", min: -1 }],
1625
- ["aria-rowindex", { kind: "integer", min: 1 }],
1626
- ["aria-colindex", { kind: "integer", min: 1 }],
1627
- ["aria-rowspan", { kind: "integer", min: 0 }],
1628
- ["aria-colspan", { kind: "integer", min: 0 }],
1629
- // Enum (specific allowed tokens)
1630
- ["aria-autocomplete", { kind: "enum", values: /* @__PURE__ */ new Set(["inline", "list", "both", "none"]) }],
1631
- [
1632
- "aria-current",
1633
- {
1634
- kind: "enum",
1635
- values: /* @__PURE__ */ new Set(["page", "step", "location", "date", "time", "true", "false"])
1636
- }
1637
- ],
1638
- [
1639
- "aria-haspopup",
1640
- {
1641
- kind: "enum",
1642
- values: /* @__PURE__ */ new Set(["false", "true", "menu", "listbox", "tree", "grid", "dialog"])
1643
- }
1644
- ],
1645
- ["aria-invalid", { kind: "enum", values: /* @__PURE__ */ new Set(["grammar", "false", "spelling", "true"]) }],
1646
- ["aria-live", { kind: "enum", values: /* @__PURE__ */ new Set(["assertive", "off", "polite"]) }],
1647
- [
1648
- "aria-orientation",
1649
- { kind: "enum", values: /* @__PURE__ */ new Set(["horizontal", "vertical", "undefined"]) }
1650
- ],
1651
- ["aria-sort", { kind: "enum", values: /* @__PURE__ */ new Set(["ascending", "descending", "none", "other"]) }]
1652
- ]);
1653
1726
  static #isValidAriaValue(value, type) {
1654
1727
  switch (type.kind) {
1655
1728
  case "boolean":
@@ -1694,11 +1767,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1694
1767
  }
1695
1768
  }
1696
1769
  static #checkAriaAttributeValues({ props, effectiveRole }) {
1697
- if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS;
1770
+ if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
1698
1771
  const results = [];
1699
1772
  iterate.forEachEntry(props, (key, value) => {
1700
1773
  if (!key.startsWith("aria-")) return;
1701
- const type = _AriaPolicyEngine.#ARIA_VALUE_TYPES.get(key);
1774
+ const type = ARIA_VALUE_TYPES.get(key);
1702
1775
  if (!isNonNull(type)) return;
1703
1776
  if (_AriaPolicyEngine.#isValidAriaValue(value, type)) return;
1704
1777
  results.push({
@@ -1717,26 +1790,18 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1717
1790
  return results;
1718
1791
  }
1719
1792
  // ─── Heading implicit level ────────────────────────────────────────────────
1720
- static #HEADING_IMPLICIT_LEVELS = /* @__PURE__ */ new Map([
1721
- ["h1", 1],
1722
- ["h2", 2],
1723
- ["h3", 3],
1724
- ["h4", 4],
1725
- ["h5", 5],
1726
- ["h6", 6]
1727
- ]);
1728
1793
  static #checkRedundantAriaLevel({
1729
1794
  tag,
1730
1795
  props,
1731
1796
  effectiveRole
1732
1797
  }) {
1733
- if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS;
1734
- const implicitLevel = _AriaPolicyEngine.#HEADING_IMPLICIT_LEVELS.get(tag);
1735
- if (!isNonNull(implicitLevel)) return NO_VIOLATIONS;
1798
+ if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
1799
+ const implicitLevel = HEADING_IMPLICIT_LEVELS.get(tag);
1800
+ if (!isNonNull(implicitLevel)) return NO_VIOLATIONS2;
1736
1801
  const raw = props["aria-level"];
1737
- if (!isNonNull(raw)) return NO_VIOLATIONS;
1802
+ if (!isNonNull(raw)) return NO_VIOLATIONS2;
1738
1803
  const n = typeof raw === "number" ? raw : typeof raw === "string" ? parseInt(raw, 10) : NaN;
1739
- if (!Number.isFinite(n) || n !== implicitLevel) return NO_VIOLATIONS;
1804
+ if (!Number.isFinite(n) || n !== implicitLevel) return NO_VIOLATIONS2;
1740
1805
  return [
1741
1806
  {
1742
1807
  valid: false,
@@ -1749,20 +1814,14 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1749
1814
  ];
1750
1815
  }
1751
1816
  // ─── Name-required roles ───────────────────────────────────────────────────
1752
- // Roles that always require an accessible name per WAI-ARIA APG.
1753
- // Dialog and landmark names are enforced via contracts (ariaContract) rather than
1754
- // the built-in pipeline so consumers can opt in; img is built in because role=img
1755
- // on any element (including bare <img>) is definitionally useless without a name.
1756
- static #NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["img"]);
1757
1817
  static #checkNameRequiredRoles({
1758
1818
  tag,
1759
1819
  props,
1760
1820
  effectiveRole
1761
1821
  }) {
1762
- if (!effectiveRole || !_AriaPolicyEngine.#NAME_REQUIRED_ROLES.has(effectiveRole))
1763
- return NO_VIOLATIONS;
1764
- if ("aria-label" in props || "aria-labelledby" in props) return NO_VIOLATIONS;
1765
- if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return NO_VIOLATIONS;
1822
+ if (!effectiveRole || !NAME_REQUIRED_ROLES.has(effectiveRole)) return NO_VIOLATIONS2;
1823
+ if ("aria-label" in props || "aria-labelledby" in props) return NO_VIOLATIONS2;
1824
+ if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return NO_VIOLATIONS2;
1766
1825
  return [
1767
1826
  {
1768
1827
  valid: false,
@@ -1772,51 +1831,21 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1772
1831
  }
1773
1832
  ];
1774
1833
  }
1775
- // WAI-ARIA 1.2 required states and properties, keyed by role.
1776
- // Source: https://www.w3.org/TR/wai-aria-1.2/#requiredState
1777
- static #REQUIRED_PROPERTIES = /* @__PURE__ */ new Map([
1778
- ["combobox", ["aria-expanded"]],
1779
- ["option", ["aria-selected"]],
1780
- ["slider", ["aria-valuenow"]],
1781
- ["scrollbar", ["aria-controls", "aria-valuenow"]],
1782
- ["spinbutton", ["aria-valuenow"]]
1783
- ]);
1784
- static #checkRequiredAriaProperties({
1785
- props,
1786
- effectiveRole
1787
- }) {
1788
- if (!effectiveRole) return NO_VIOLATIONS;
1789
- const required = _AriaPolicyEngine.#REQUIRED_PROPERTIES.get(effectiveRole);
1790
- if (!isNonNull(required)) return NO_VIOLATIONS;
1791
- const results = [];
1792
- iterate.forEach(required, (attr) => {
1793
- if (attr in props) return;
1794
- results.push({
1795
- valid: false,
1796
- fixable: false,
1797
- severity: "warning",
1798
- attribute: attr,
1799
- diagnostic: AriaDiagnostics.requiredProperty(attr, effectiveRole)
1800
- });
1801
- });
1802
- return results;
1834
+ static #requiredAriaPropertiesRule = {
1835
+ attributesByRole: REQUIRED_ARIA_PROPERTIES,
1836
+ diagnosticFor: (attribute, role) => AriaDiagnostics.requiredProperty(attribute, role)
1837
+ };
1838
+ static #checkRequiredAriaProperties(context) {
1839
+ return checkRequiredAttributes(_AriaPolicyEngine.#requiredAriaPropertiesRule, context);
1803
1840
  }
1804
- // Natively interactive HTML elements — always keyboard-reachable unless explicitly disabled.
1805
- static #INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
1806
- "a",
1807
- "button",
1808
- "input",
1809
- "select",
1810
- "textarea"
1811
- ]);
1812
1841
  // WAI-ARIA 1.2 §6.6: aria-hidden="true" must not be placed on focusable elements.
1813
1842
  static #checkAriaHiddenOnFocusable({ tag, props }) {
1814
- if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return NO_VIOLATIONS;
1815
- const isInteractive = _AriaPolicyEngine.#INTERACTIVE_TAGS.has(tag);
1843
+ if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return NO_VIOLATIONS2;
1844
+ const isInteractive = INTERACTIVE_TAGS.has(tag);
1816
1845
  if (!isInteractive) {
1817
1846
  const tabindex = props.tabindex;
1818
1847
  const n = typeof tabindex === "number" ? tabindex : typeof tabindex === "string" ? parseInt(tabindex, 10) : NaN;
1819
- if (!Number.isFinite(n) || n < 0) return NO_VIOLATIONS;
1848
+ if (!Number.isFinite(n) || n < 0) return NO_VIOLATIONS2;
1820
1849
  }
1821
1850
  return [
1822
1851
  {
@@ -1835,7 +1864,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1835
1864
  props,
1836
1865
  effectiveRole
1837
1866
  }) {
1838
- if (effectiveRole !== "none" && effectiveRole !== "presentation") return NO_VIOLATIONS;
1867
+ if (effectiveRole !== "none" && effectiveRole !== "presentation") return NO_VIOLATIONS2;
1839
1868
  const results = [];
1840
1869
  iterate.forEachEntry(props, (key) => {
1841
1870
  if (!key.startsWith("aria-")) return;
@@ -1851,18 +1880,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1851
1880
  });
1852
1881
  return results;
1853
1882
  }
1854
- // WAI-ARIA live region roles and their implied aria-live politeness values.
1855
- static #LIVE_REGION_ROLES = /* @__PURE__ */ new Map([
1856
- ["alert", "assertive"],
1857
- ["status", "polite"],
1858
- ["log", "polite"],
1859
- ["timer", "off"]
1860
- ]);
1861
1883
  static #checkMissingLiveRegion({ effectiveRole, props }) {
1862
- if (!effectiveRole) return NO_VIOLATIONS;
1863
- const impliedLive = _AriaPolicyEngine.#LIVE_REGION_ROLES.get(effectiveRole);
1864
- if (!impliedLive) return NO_VIOLATIONS;
1865
- if ("aria-live" in props) return NO_VIOLATIONS;
1884
+ if (!effectiveRole) return NO_VIOLATIONS2;
1885
+ const impliedLive = LIVE_REGION_ROLES.get(effectiveRole);
1886
+ if (!impliedLive) return NO_VIOLATIONS2;
1887
+ if ("aria-live" in props) return NO_VIOLATIONS2;
1866
1888
  const injectLive = {
1867
1889
  kind: `injectLive:${effectiveRole}`,
1868
1890
  apply: (ctx) => ({
@@ -1881,20 +1903,13 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1881
1903
  }
1882
1904
  ];
1883
1905
  }
1884
- static #checkMissingAtomic({ effectiveRole, props }) {
1885
- if (!effectiveRole || !_AriaPolicyEngine.#LIVE_REGION_ROLES.has(effectiveRole))
1886
- return NO_VIOLATIONS;
1887
- if ("aria-atomic" in props) return NO_VIOLATIONS;
1888
- return [
1889
- {
1890
- valid: false,
1891
- fixable: false,
1892
- severity: "warning",
1893
- diagnostic: AriaDiagnostics.missingAtomic(effectiveRole)
1894
- }
1895
- ];
1906
+ static #missingAtomicRule = {
1907
+ attributesByRole: ATOMIC_REQUIREMENTS,
1908
+ diagnosticFor: (_attribute, role) => AriaDiagnostics.missingAtomic(role)
1909
+ };
1910
+ static #checkMissingAtomic(context) {
1911
+ return checkRequiredAttributes(_AriaPolicyEngine.#missingAtomicRule, context);
1896
1912
  }
1897
- static #VALID_RELEVANT_TOKENS = /* @__PURE__ */ new Set(["additions", "removals", "text", "all"]);
1898
1913
  // Custom fix rules passed via `options.rules` must be pure functions of (tag, props) — the cache
1899
1914
  // replays stored fixes against new prop objects, so fixes that close over external state will
1900
1915
  // produce inconsistent results on cache hits.
@@ -1908,10 +1923,10 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1908
1923
  };
1909
1924
  static #checkInvalidAriaRelevant({ props }) {
1910
1925
  const relevant = props["aria-relevant"];
1911
- if (relevant === void 0) return NO_VIOLATIONS;
1912
- if (typeof relevant !== "string") return NO_VIOLATIONS;
1926
+ if (relevant === void 0) return NO_VIOLATIONS2;
1927
+ if (typeof relevant !== "string") return NO_VIOLATIONS2;
1913
1928
  const tokens = relevant.trim().split(/\s+/);
1914
- const invalid = tokens.filter((t) => !_AriaPolicyEngine.#VALID_RELEVANT_TOKENS.has(t));
1929
+ const invalid = tokens.filter((t) => !VALID_RELEVANT_TOKENS.has(t));
1915
1930
  if (invalid.length > 0) {
1916
1931
  return [
1917
1932
  {
@@ -1936,7 +1951,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1936
1951
  }
1937
1952
  ];
1938
1953
  }
1939
- return NO_VIOLATIONS;
1954
+ return NO_VIOLATIONS2;
1940
1955
  }
1941
1956
  };
1942
1957
 
@@ -2256,7 +2271,59 @@ var readonlyProps = ({
2256
2271
  // ../core/src/html/evaluators.ts
2257
2272
  import { warnDiagnostics as warnDiagnostics2 } from "./_shared/diagnostics.js";
2258
2273
 
2259
- // ../core/src/html/input-rules.ts
2274
+ // ../core/src/html/spec/vocabulary/input.ts
2275
+ var TEXT_INPUT_TYPES = ["text", "search", "url", "tel", "email", "password"];
2276
+ var NUMERIC_INPUT_TYPES = [
2277
+ "number",
2278
+ "range",
2279
+ "date",
2280
+ "month",
2281
+ "week",
2282
+ "time",
2283
+ "datetime-local"
2284
+ ];
2285
+ var HTML_INPUT_TYPES = /* @__PURE__ */ new Set([
2286
+ ...TEXT_INPUT_TYPES,
2287
+ ...NUMERIC_INPUT_TYPES,
2288
+ "checkbox",
2289
+ "radio",
2290
+ "file",
2291
+ "color",
2292
+ "hidden",
2293
+ "button",
2294
+ "submit",
2295
+ "reset",
2296
+ "image"
2297
+ ]);
2298
+
2299
+ // ../core/src/html/spec/attributes/input.ts
2300
+ var INPUT_ATTRIBUTE_TYPE_POLICIES = [
2301
+ { attribute: "checked", allowedTypes: ["checkbox", "radio"] },
2302
+ { attribute: "multiple", allowedTypes: ["email", "file"] },
2303
+ { attribute: "maxLength", allowedTypes: TEXT_INPUT_TYPES },
2304
+ { attribute: "minLength", allowedTypes: TEXT_INPUT_TYPES },
2305
+ { attribute: "pattern", allowedTypes: TEXT_INPUT_TYPES },
2306
+ { attribute: "min", allowedTypes: NUMERIC_INPUT_TYPES },
2307
+ { attribute: "max", allowedTypes: NUMERIC_INPUT_TYPES },
2308
+ { attribute: "step", allowedTypes: NUMERIC_INPUT_TYPES },
2309
+ { attribute: "accept", allowedTypes: ["file"] },
2310
+ { attribute: "capture", allowedTypes: ["file"] },
2311
+ { attribute: "size", allowedTypes: TEXT_INPUT_TYPES },
2312
+ { attribute: "alt", allowedTypes: ["image"] },
2313
+ { attribute: "height", allowedTypes: ["image"] },
2314
+ { attribute: "width", allowedTypes: ["image"] }
2315
+ ];
2316
+
2317
+ // ../core/src/html/spec/constraints/input.ts
2318
+ var REQUIRED_READONLY_CONFLICT = {
2319
+ props: ["required", "readOnly"],
2320
+ diagnostic: () => InputAccessibilityDiagnostics.requiredReadOnlyConflict()
2321
+ };
2322
+ var INPUT_MUTUALLY_EXCLUSIVE_POLICIES = [
2323
+ REQUIRED_READONLY_CONFLICT
2324
+ ];
2325
+
2326
+ // ../core/src/html/spec/validators/attribute-type-validator.ts
2260
2327
  var DEFAULT_INPUT_TYPE = "text";
2261
2328
  function omit(props, key) {
2262
2329
  const next = { ...props };
@@ -2272,7 +2339,10 @@ function removeAttributeFix(attribute) {
2272
2339
  }
2273
2340
  };
2274
2341
  }
2275
- function inputAttributeRequiresType(attribute, allowedTypes) {
2342
+ function createInputAttributeTypeRule({
2343
+ attribute,
2344
+ allowedTypes
2345
+ }) {
2276
2346
  const rule = ({ tag, props }) => {
2277
2347
  if (tag !== "input" || !(attribute in props)) return [];
2278
2348
  const type = typeof props.type === "string" ? props.type : DEFAULT_INPUT_TYPE;
@@ -2288,31 +2358,30 @@ function inputAttributeRequiresType(attribute, allowedTypes) {
2288
2358
  }
2289
2359
  ];
2290
2360
  };
2291
- return Object.assign(rule, { readsProps: ["type", attribute] });
2361
+ return Object.assign(rule, { readsProps: ["type", attribute], tags: ["input"] });
2362
+ }
2363
+
2364
+ // ../core/src/html/spec/validators/mutually-exclusive-validator.ts
2365
+ function createMutuallyExclusiveRule({
2366
+ props: conflictingProps,
2367
+ diagnostic: createDiagnostic
2368
+ }) {
2369
+ const [first, second] = conflictingProps;
2370
+ const rule = ({ tag, props }) => {
2371
+ if (tag !== "input" || !props[first] || !props[second]) return [];
2372
+ const diagnostic = createDiagnostic();
2373
+ return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
2374
+ };
2375
+ return Object.assign(rule, { readsProps: conflictingProps, tags: ["input"] });
2376
+ }
2377
+
2378
+ // ../core/src/html/input-rules.ts
2379
+ var policyByAttribute = Object.fromEntries(
2380
+ INPUT_ATTRIBUTE_TYPE_POLICIES.map((policy) => [policy.attribute, policy])
2381
+ );
2382
+ function policyFor(attribute) {
2383
+ return policyByAttribute[attribute];
2292
2384
  }
2293
- var TEXT_INPUT_TYPES = ["text", "search", "url", "tel", "email", "password"];
2294
- var NUMERIC_INPUT_TYPES = [
2295
- "number",
2296
- "range",
2297
- "date",
2298
- "month",
2299
- "week",
2300
- "time",
2301
- "datetime-local"
2302
- ];
2303
- var HTML_INPUT_TYPES = /* @__PURE__ */ new Set([
2304
- ...TEXT_INPUT_TYPES,
2305
- ...NUMERIC_INPUT_TYPES,
2306
- "checkbox",
2307
- "radio",
2308
- "file",
2309
- "color",
2310
- "hidden",
2311
- "button",
2312
- "submit",
2313
- "reset",
2314
- "image"
2315
- ]);
2316
2385
  var supportedInputTypeRule = Object.assign(
2317
2386
  ({ tag, props }) => {
2318
2387
  if (tag !== "input" || typeof props.type !== "string") return [];
@@ -2328,30 +2397,22 @@ var supportedInputTypeRule = Object.assign(
2328
2397
  }
2329
2398
  ];
2330
2399
  },
2331
- { readsProps: ["type"] }
2400
+ { readsProps: ["type"], tags: ["input"] }
2332
2401
  );
2333
- var checkedRequiresCheckableTypeRule = inputAttributeRequiresType("checked", [
2334
- "checkbox",
2335
- "radio"
2336
- ]);
2337
- var multipleRequiresSupportedTypeRule = inputAttributeRequiresType("multiple", [
2338
- "email",
2339
- "file"
2340
- ]);
2341
- var maxLengthRequiresTextTypeRule = inputAttributeRequiresType(
2342
- "maxLength",
2343
- TEXT_INPUT_TYPES
2344
- );
2345
- var minLengthRequiresTextTypeRule = inputAttributeRequiresType(
2346
- "minLength",
2347
- TEXT_INPUT_TYPES
2348
- );
2349
- var patternRequiresTextTypeRule = inputAttributeRequiresType("pattern", TEXT_INPUT_TYPES);
2350
- var minRequiresNumericTypeRule = inputAttributeRequiresType("min", NUMERIC_INPUT_TYPES);
2351
- var maxRequiresNumericTypeRule = inputAttributeRequiresType("max", NUMERIC_INPUT_TYPES);
2352
- var stepRequiresNumericTypeRule = inputAttributeRequiresType("step", NUMERIC_INPUT_TYPES);
2353
- var acceptRequiresFileTypeRule = inputAttributeRequiresType("accept", ["file"]);
2354
- var captureRequiresFileTypeRule = inputAttributeRequiresType("capture", ["file"]);
2402
+ var checkedRequiresCheckableTypeRule = createInputAttributeTypeRule(policyFor("checked"));
2403
+ var multipleRequiresSupportedTypeRule = createInputAttributeTypeRule(policyFor("multiple"));
2404
+ var maxLengthRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("maxLength"));
2405
+ var minLengthRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("minLength"));
2406
+ var patternRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("pattern"));
2407
+ var minRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("min"));
2408
+ var maxRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("max"));
2409
+ var stepRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("step"));
2410
+ var acceptRequiresFileTypeRule = createInputAttributeTypeRule(policyFor("accept"));
2411
+ var captureRequiresFileTypeRule = createInputAttributeTypeRule(policyFor("capture"));
2412
+ var sizeRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("size"));
2413
+ var altRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("alt"));
2414
+ var heightRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("height"));
2415
+ var widthRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("width"));
2355
2416
  var inputAccessibleNameRule = Object.assign(
2356
2417
  ({ tag, props }) => {
2357
2418
  if (tag !== "input" || props.type === "hidden") return [];
@@ -2360,7 +2421,10 @@ var inputAccessibleNameRule = Object.assign(
2360
2421
  const diagnostic = hasPlaceholder ? InputAccessibilityDiagnostics.placeholderIsNotLabel() : InputAccessibilityDiagnostics.missingAccessibleName();
2361
2422
  return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
2362
2423
  },
2363
- { readsProps: ["type", "aria-label", "aria-labelledby", "placeholder"] }
2424
+ {
2425
+ readsProps: ["type", "aria-label", "aria-labelledby", "placeholder"],
2426
+ tags: ["input"]
2427
+ }
2364
2428
  );
2365
2429
  var PASSWORD_AUTOCOMPLETE_VALUES = ["current-password", "new-password"];
2366
2430
  var passwordAutocompleteRule = Object.assign(
@@ -2372,16 +2436,9 @@ var passwordAutocompleteRule = Object.assign(
2372
2436
  const diagnostic = InputAccessibilityDiagnostics.passwordMissingAutocomplete();
2373
2437
  return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
2374
2438
  },
2375
- { readsProps: ["type", "autoComplete"] }
2376
- );
2377
- var requiredReadOnlyConflictRule = Object.assign(
2378
- ({ tag, props }) => {
2379
- if (tag !== "input" || !props.required || !props.readOnly) return [];
2380
- const diagnostic = InputAccessibilityDiagnostics.requiredReadOnlyConflict();
2381
- return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
2382
- },
2383
- { readsProps: ["required", "readOnly"] }
2439
+ { readsProps: ["type", "autoComplete"], tags: ["input"] }
2384
2440
  );
2441
+ var requiredReadOnlyConflictRule = createMutuallyExclusiveRule(REQUIRED_READONLY_CONFLICT);
2385
2442
  var INPUT_RULES = [
2386
2443
  supportedInputTypeRule,
2387
2444
  checkedRequiresCheckableTypeRule,
@@ -2394,11 +2451,132 @@ var INPUT_RULES = [
2394
2451
  stepRequiresNumericTypeRule,
2395
2452
  acceptRequiresFileTypeRule,
2396
2453
  captureRequiresFileTypeRule,
2454
+ sizeRequiresTextTypeRule,
2455
+ altRequiresImageTypeRule,
2456
+ heightRequiresImageTypeRule,
2457
+ widthRequiresImageTypeRule,
2397
2458
  inputAccessibleNameRule,
2398
2459
  passwordAutocompleteRule,
2399
2460
  requiredReadOnlyConflictRule
2400
2461
  ];
2401
2462
 
2463
+ // ../core/src/html/spec/types.ts
2464
+ function definePropRolePolicy(prop, map2, fallback) {
2465
+ return { kind: "byProp", prop, map: map2, fallback };
2466
+ }
2467
+ function resolveAllowedRoles(spec, props) {
2468
+ const policy = spec.allowedRoles;
2469
+ if (!policy) return void 0;
2470
+ switch (policy.kind) {
2471
+ case "fixed":
2472
+ return policy.roles;
2473
+ case "byProp": {
2474
+ const value = typeof props[policy.prop] === "string" ? props[policy.prop] : policy.fallback;
2475
+ return policy.map[value];
2476
+ }
2477
+ case "dynamic":
2478
+ return policy.resolve({ props });
2479
+ }
2480
+ }
2481
+
2482
+ // ../core/src/html/spec/roles/input.ts
2483
+ var ALLOWED_INPUT_ROLES = {
2484
+ checkbox: ["menuitemcheckbox", "option", "switch", "button"],
2485
+ radio: ["menuitemradio"],
2486
+ range: [],
2487
+ number: [],
2488
+ search: ["combobox"],
2489
+ text: ["combobox", "searchbox", "spinbutton"],
2490
+ email: ["combobox"],
2491
+ tel: ["combobox"],
2492
+ url: ["combobox"],
2493
+ button: [
2494
+ "link",
2495
+ "menuitem",
2496
+ "menuitemcheckbox",
2497
+ "menuitemradio",
2498
+ "option",
2499
+ "radio",
2500
+ "switch",
2501
+ "tab"
2502
+ ],
2503
+ submit: [
2504
+ "link",
2505
+ "menuitem",
2506
+ "menuitemcheckbox",
2507
+ "menuitemradio",
2508
+ "option",
2509
+ "radio",
2510
+ "switch",
2511
+ "tab"
2512
+ ],
2513
+ reset: [
2514
+ "link",
2515
+ "menuitem",
2516
+ "menuitemcheckbox",
2517
+ "menuitemradio",
2518
+ "option",
2519
+ "radio",
2520
+ "switch",
2521
+ "tab"
2522
+ ],
2523
+ image: [
2524
+ "link",
2525
+ "menuitem",
2526
+ "menuitemcheckbox",
2527
+ "menuitemradio",
2528
+ "option",
2529
+ "radio",
2530
+ "switch",
2531
+ "tab"
2532
+ ],
2533
+ hidden: []
2534
+ };
2535
+
2536
+ // ../core/src/html/spec/elements/input.ts
2537
+ var inputElementSpec = {
2538
+ tag: "input",
2539
+ allowedRoles: definePropRolePolicy("type", ALLOWED_INPUT_ROLES, "text"),
2540
+ attributes: INPUT_ATTRIBUTE_TYPE_POLICIES,
2541
+ mutuallyExclusive: INPUT_MUTUALLY_EXCLUSIVE_POLICIES
2542
+ };
2543
+
2544
+ // ../core/src/html/spec/roles/img.ts
2545
+ var IMG_NAMED_ROLES = [
2546
+ "button",
2547
+ "checkbox",
2548
+ "link",
2549
+ "menuitem",
2550
+ "menuitemcheckbox",
2551
+ "menuitemradio",
2552
+ "option",
2553
+ "progressbar",
2554
+ "scrollbar",
2555
+ "separator",
2556
+ "slider",
2557
+ "switch",
2558
+ "tab",
2559
+ "treeitem"
2560
+ ];
2561
+
2562
+ // ../core/src/html/spec/elements/img.ts
2563
+ var imgElementSpec = {
2564
+ tag: "img",
2565
+ allowedRoles: {
2566
+ kind: "dynamic",
2567
+ resolve: ({ props }) => props.alt === "" ? [] : IMG_NAMED_ROLES
2568
+ }
2569
+ };
2570
+
2571
+ // ../core/src/html/spec/roles/table.ts
2572
+ var ALLOWED_TABLE_ROLES = ["grid", "treegrid"];
2573
+
2574
+ // ../core/src/html/spec/elements/table.ts
2575
+ var tableElementSpec = {
2576
+ tag: "table",
2577
+ allowedRoles: { kind: "fixed", roles: ALLOWED_TABLE_ROLES }
2578
+ };
2579
+
2402
2580
  // ../core/src/html/role-restrictions.ts
2403
2581
  var ALLOWED_ROLES = {
2404
2582
  article: ["application", "document", "feed", "main", "none", "presentation", "region"],
@@ -2471,86 +2649,17 @@ var ALLOWED_ROLES = {
2471
2649
  "tab",
2472
2650
  "treeitem"
2473
2651
  ],
2474
- table: ["grid", "treegrid"],
2475
2652
  dialog: ["alertdialog"],
2476
2653
  fieldset: ["none", "presentation", "radiogroup"]
2477
2654
  };
2478
- var IMG_NAMED_ROLES = [
2479
- "button",
2480
- "checkbox",
2481
- "link",
2482
- "menuitem",
2483
- "menuitemcheckbox",
2484
- "menuitemradio",
2485
- "option",
2486
- "progressbar",
2487
- "scrollbar",
2488
- "separator",
2489
- "slider",
2490
- "switch",
2491
- "tab",
2492
- "treeitem"
2493
- ];
2494
- var ALLOWED_INPUT_ROLES = {
2495
- checkbox: ["menuitemcheckbox", "option", "switch", "button"],
2496
- radio: ["menuitemradio"],
2497
- range: [],
2498
- number: [],
2499
- search: ["combobox"],
2500
- text: ["combobox", "searchbox", "spinbutton"],
2501
- email: ["combobox"],
2502
- tel: ["combobox"],
2503
- url: ["combobox"],
2504
- button: [
2505
- "link",
2506
- "menuitem",
2507
- "menuitemcheckbox",
2508
- "menuitemradio",
2509
- "option",
2510
- "radio",
2511
- "switch",
2512
- "tab"
2513
- ],
2514
- submit: [
2515
- "link",
2516
- "menuitem",
2517
- "menuitemcheckbox",
2518
- "menuitemradio",
2519
- "option",
2520
- "radio",
2521
- "switch",
2522
- "tab"
2523
- ],
2524
- reset: [
2525
- "link",
2526
- "menuitem",
2527
- "menuitemcheckbox",
2528
- "menuitemradio",
2529
- "option",
2530
- "radio",
2531
- "switch",
2532
- "tab"
2533
- ],
2534
- image: [
2535
- "link",
2536
- "menuitem",
2537
- "menuitemcheckbox",
2538
- "menuitemradio",
2539
- "option",
2540
- "radio",
2541
- "switch",
2542
- "tab"
2543
- ],
2544
- hidden: []
2655
+ var ELEMENT_SPECS = {
2656
+ input: inputElementSpec,
2657
+ img: imgElementSpec,
2658
+ table: tableElementSpec
2545
2659
  };
2546
2660
  function getAllowedRoles(tag, props) {
2547
- if (tag === "input") {
2548
- const type = typeof props.type === "string" ? props.type : "text";
2549
- return ALLOWED_INPUT_ROLES[type];
2550
- }
2551
- if (tag === "img") {
2552
- return props.alt === "" ? [] : IMG_NAMED_ROLES;
2553
- }
2661
+ const spec = ELEMENT_SPECS[tag];
2662
+ if (spec) return resolveAllowedRoles(spec, props);
2554
2663
  return ALLOWED_ROLES[tag];
2555
2664
  }
2556
2665
  var removeRoleFix = {
@@ -2591,21 +2700,24 @@ var removeLandmarkRoleOverride = {
2591
2700
  return { applied: true, next: rest, previous: props };
2592
2701
  }
2593
2702
  };
2594
- function landmarkRoleRule({ tag, props, implicitRole }) {
2595
- if (!LANDMARK_TAG_SET.has(tag) || !implicitRole) return [];
2596
- const role = props.role;
2597
- if (!role || role === implicitRole) return [];
2598
- const diagnostic = HtmlDiagnostics.landmarkRoleOverride(tag, implicitRole, role);
2599
- return [
2600
- {
2601
- valid: false,
2602
- fixable: true,
2603
- severity: diagnostic.severity,
2604
- fix: removeLandmarkRoleOverride,
2605
- diagnostic
2606
- }
2607
- ];
2608
- }
2703
+ var landmarkRoleRule = Object.assign(
2704
+ ({ tag, props, implicitRole }) => {
2705
+ if (!LANDMARK_TAG_SET.has(tag) || !implicitRole) return [];
2706
+ const role = props.role;
2707
+ if (!role || role === implicitRole) return [];
2708
+ const diagnostic = HtmlDiagnostics.landmarkRoleOverride(tag, implicitRole, role);
2709
+ return [
2710
+ {
2711
+ valid: false,
2712
+ fixable: true,
2713
+ severity: diagnostic.severity,
2714
+ fix: removeLandmarkRoleOverride,
2715
+ diagnostic
2716
+ }
2717
+ ];
2718
+ },
2719
+ { tags: [...LANDMARK_TAG_SET] }
2720
+ );
2609
2721
  function requireAccessibleName({ tag, props }) {
2610
2722
  if ("aria-label" in props || "aria-labelledby" in props) return [];
2611
2723
  return [
@@ -2618,10 +2730,13 @@ function requireAccessibleName({ tag, props }) {
2618
2730
  ];
2619
2731
  }
2620
2732
  var NAMED_LANDMARK_TAGS = /* @__PURE__ */ new Set(["nav", "aside"]);
2621
- function landmarkNameAdvisory(ctx) {
2622
- if (!ctx.implicitRole || !NAMED_LANDMARK_TAGS.has(ctx.tag)) return [];
2623
- return requireAccessibleName(ctx);
2624
- }
2733
+ var landmarkNameAdvisory = Object.assign(
2734
+ (ctx) => {
2735
+ if (!ctx.implicitRole || !NAMED_LANDMARK_TAGS.has(ctx.tag)) return [];
2736
+ return requireAccessibleName(ctx);
2737
+ },
2738
+ { tags: [...NAMED_LANDMARK_TAGS] }
2739
+ );
2625
2740
  var HTML_ARIA_RULES = [
2626
2741
  landmarkRoleRule,
2627
2742
  landmarkNameAdvisory,
@@ -2722,6 +2837,62 @@ var figureContract = contract([
2722
2837
  ]);
2723
2838
  var detailsContract = firstChildContract("summary", "summary");
2724
2839
  var fieldsetContract = firstChildContract("legend", "legend");
2840
+ var objectContract = contract([
2841
+ { name: "param", match: isTag("param") },
2842
+ { name: "content", match: isOpenContent("param") }
2843
+ ]);
2844
+ var INTERACTIVE_CONTENT_TAGS = ["a", "button", "input", "select", "textarea", "label"];
2845
+ var buttonContract = closedContract([
2846
+ { name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
2847
+ ]);
2848
+ var anchorContract = closedContract([
2849
+ { name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
2850
+ ]);
2851
+ var LABELABLE_TAGS = [
2852
+ "button",
2853
+ "input",
2854
+ "meter",
2855
+ "output",
2856
+ "progress",
2857
+ "select",
2858
+ "textarea"
2859
+ ];
2860
+ var labelContract = contract([
2861
+ { name: "control", match: isTag(...LABELABLE_TAGS), cardinality: { max: 1 } }
2862
+ ]);
2863
+ var P_BLOCKED_TAGS = [
2864
+ "address",
2865
+ "article",
2866
+ "aside",
2867
+ "blockquote",
2868
+ "details",
2869
+ "dialog",
2870
+ "div",
2871
+ "dl",
2872
+ "fieldset",
2873
+ "figure",
2874
+ "footer",
2875
+ "form",
2876
+ "h1",
2877
+ "h2",
2878
+ "h3",
2879
+ "h4",
2880
+ "h5",
2881
+ "h6",
2882
+ "header",
2883
+ "hr",
2884
+ "main",
2885
+ "nav",
2886
+ "ol",
2887
+ "p",
2888
+ "pre",
2889
+ "section",
2890
+ "table",
2891
+ "ul"
2892
+ ];
2893
+ var pContract = closedContract([
2894
+ { name: "content", match: isOpenContent(...P_BLOCKED_TAGS) }
2895
+ ]);
2725
2896
  var mediaContract = contract([
2726
2897
  { name: "source", match: isTag("source") },
2727
2898
  { name: "track", match: isTag("track") },
@@ -2776,6 +2947,11 @@ var htmlContracts = {
2776
2947
  details: detailsContract,
2777
2948
  fieldset: fieldsetContract,
2778
2949
  dialog: dialogContract,
2950
+ object: objectContract,
2951
+ button: buttonContract,
2952
+ a: anchorContract,
2953
+ label: labelContract,
2954
+ p: pContract,
2779
2955
  head: headContract,
2780
2956
  html: htmlContract
2781
2957
  };