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