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.
@@ -868,7 +868,11 @@ var ATTRIBUTE_IGNORED_CODES = {
868
868
  max: DiagnosticCode3.HtmlInputMaxIgnoredForType,
869
869
  step: DiagnosticCode3.HtmlInputStepIgnoredForType,
870
870
  accept: DiagnosticCode3.HtmlInputAcceptIgnoredForType,
871
- capture: DiagnosticCode3.HtmlInputCaptureIgnoredForType
871
+ capture: DiagnosticCode3.HtmlInputCaptureIgnoredForType,
872
+ size: DiagnosticCode3.HtmlInputSizeIgnoredForType,
873
+ alt: DiagnosticCode3.HtmlInputAltIgnoredForType,
874
+ height: DiagnosticCode3.HtmlInputHeightIgnoredForType,
875
+ width: DiagnosticCode3.HtmlInputWidthIgnoredForType
872
876
  };
873
877
  var HtmlDiagnostics = {
874
878
  emptyRole(tag) {
@@ -1102,8 +1106,132 @@ var InvariantBase = class {
1102
1106
  }
1103
1107
  };
1104
1108
 
1105
- // ../../lib/contract/src/aria/polymorphic-validator.ts
1109
+ // ../../lib/contract/src/aria/spec/roles/required-properties.ts
1110
+ var REQUIRED_ARIA_PROPERTIES = {
1111
+ combobox: ["aria-expanded"],
1112
+ option: ["aria-selected"],
1113
+ slider: ["aria-valuenow"],
1114
+ scrollbar: ["aria-controls", "aria-valuenow"],
1115
+ spinbutton: ["aria-valuenow"]
1116
+ };
1117
+
1118
+ // ../../lib/contract/src/aria/spec/roles/name-required.ts
1119
+ var NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["img"]);
1120
+
1121
+ // ../../lib/contract/src/aria/spec/validators/required-properties-validator.ts
1106
1122
  var NO_VIOLATIONS = [{ valid: true }];
1123
+ function requiredAttributeByRole(roles, attribute) {
1124
+ return Object.fromEntries([...roles].map((role) => [role, [attribute]]));
1125
+ }
1126
+ function checkRequiredAttributes(requirement, { props, effectiveRole }) {
1127
+ if (!effectiveRole) return NO_VIOLATIONS;
1128
+ const requiredAttributes = requirement.attributesByRole[effectiveRole];
1129
+ if (!requiredAttributes) return NO_VIOLATIONS;
1130
+ const results = [];
1131
+ for (const attribute of requiredAttributes) {
1132
+ if (attribute in props) continue;
1133
+ results.push({
1134
+ valid: false,
1135
+ fixable: false,
1136
+ severity: "warning",
1137
+ attribute,
1138
+ diagnostic: requirement.diagnosticFor(attribute, effectiveRole)
1139
+ });
1140
+ }
1141
+ return results;
1142
+ }
1143
+
1144
+ // ../../lib/contract/src/aria/spec/roles/live-region.ts
1145
+ var LIVE_REGION_ROLES = /* @__PURE__ */ new Map([
1146
+ ["alert", "assertive"],
1147
+ ["status", "polite"],
1148
+ ["log", "polite"],
1149
+ ["timer", "off"]
1150
+ ]);
1151
+ var ATOMIC_REQUIREMENTS = requiredAttributeByRole(LIVE_REGION_ROLES.keys(), "aria-atomic");
1152
+
1153
+ // ../../lib/contract/src/aria/spec/attributes/aria-value-types.ts
1154
+ var ARIA_VALUE_TYPES = /* @__PURE__ */ new Map([
1155
+ // Boolean (true | false)
1156
+ ["aria-atomic", { kind: "boolean" }],
1157
+ ["aria-busy", { kind: "boolean" }],
1158
+ ["aria-disabled", { kind: "boolean" }],
1159
+ ["aria-expanded", { kind: "boolean" }],
1160
+ ["aria-hidden", { kind: "boolean" }],
1161
+ ["aria-modal", { kind: "boolean" }],
1162
+ ["aria-multiline", { kind: "boolean" }],
1163
+ ["aria-multiselectable", { kind: "boolean" }],
1164
+ ["aria-readonly", { kind: "boolean" }],
1165
+ ["aria-required", { kind: "boolean" }],
1166
+ ["aria-selected", { kind: "boolean" }],
1167
+ // Tristate (true | false | mixed)
1168
+ ["aria-checked", { kind: "tristate" }],
1169
+ ["aria-pressed", { kind: "tristate" }],
1170
+ // Numeric (any finite number)
1171
+ ["aria-valuenow", { kind: "number" }],
1172
+ ["aria-valuemin", { kind: "number" }],
1173
+ ["aria-valuemax", { kind: "number" }],
1174
+ // Integer with optional range
1175
+ ["aria-level", { kind: "integer", min: 1, max: 6 }],
1176
+ ["aria-posinset", { kind: "integer", min: 1 }],
1177
+ ["aria-setsize", { kind: "integer", min: -1 }],
1178
+ ["aria-rowcount", { kind: "integer", min: -1 }],
1179
+ ["aria-colcount", { kind: "integer", min: -1 }],
1180
+ ["aria-rowindex", { kind: "integer", min: 1 }],
1181
+ ["aria-colindex", { kind: "integer", min: 1 }],
1182
+ ["aria-rowspan", { kind: "integer", min: 0 }],
1183
+ ["aria-colspan", { kind: "integer", min: 0 }],
1184
+ // Enum (specific allowed tokens)
1185
+ ["aria-autocomplete", { kind: "enum", values: /* @__PURE__ */ new Set(["inline", "list", "both", "none"]) }],
1186
+ [
1187
+ "aria-current",
1188
+ {
1189
+ kind: "enum",
1190
+ values: /* @__PURE__ */ new Set(["page", "step", "location", "date", "time", "true", "false"])
1191
+ }
1192
+ ],
1193
+ [
1194
+ "aria-haspopup",
1195
+ {
1196
+ kind: "enum",
1197
+ values: /* @__PURE__ */ new Set(["false", "true", "menu", "listbox", "tree", "grid", "dialog"])
1198
+ }
1199
+ ],
1200
+ ["aria-invalid", { kind: "enum", values: /* @__PURE__ */ new Set(["grammar", "false", "spelling", "true"]) }],
1201
+ ["aria-live", { kind: "enum", values: /* @__PURE__ */ new Set(["assertive", "off", "polite"]) }],
1202
+ ["aria-orientation", { kind: "enum", values: /* @__PURE__ */ new Set(["horizontal", "vertical", "undefined"]) }],
1203
+ ["aria-sort", { kind: "enum", values: /* @__PURE__ */ new Set(["ascending", "descending", "none", "other"]) }]
1204
+ ]);
1205
+
1206
+ // ../../lib/contract/src/aria/spec/attributes/aria-relevant-tokens.ts
1207
+ var VALID_RELEVANT_TOKENS = /* @__PURE__ */ new Set([
1208
+ "additions",
1209
+ "removals",
1210
+ "text",
1211
+ "all"
1212
+ ]);
1213
+
1214
+ // ../../lib/contract/src/aria/spec/elements/heading-implicit-levels.ts
1215
+ var HEADING_IMPLICIT_LEVELS = /* @__PURE__ */ new Map([
1216
+ ["h1", 1],
1217
+ ["h2", 2],
1218
+ ["h3", 3],
1219
+ ["h4", 4],
1220
+ ["h5", 5],
1221
+ ["h6", 6]
1222
+ ]);
1223
+
1224
+ // ../../lib/contract/src/aria/spec/elements/interactive-tags.ts
1225
+ var INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
1226
+ "a",
1227
+ "button",
1228
+ "input",
1229
+ "select",
1230
+ "textarea"
1231
+ ]);
1232
+
1233
+ // ../../lib/contract/src/aria/polymorphic-validator.ts
1234
+ var NO_VIOLATIONS2 = [{ valid: true }];
1107
1235
  function isIntrinsicTag(tag) {
1108
1236
  return isString(tag);
1109
1237
  }
@@ -1165,6 +1293,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1165
1293
  const violations = [];
1166
1294
  const fixes = [];
1167
1295
  iterate.forEach(rules, (rule) => {
1296
+ if (isNonNull(rule.tags) && !rule.tags.includes(context.tag)) return;
1168
1297
  iterate.forEach(rule(context), (result) => {
1169
1298
  if (result.valid) return;
1170
1299
  const {
@@ -1190,7 +1319,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1190
1319
  return { violations, fixes };
1191
1320
  }
1192
1321
  static #getRules(context) {
1193
- if (_AriaPolicyEngine.#hasRole(context.props) || isNonNull(context.effectiveRole) && _AriaPolicyEngine.#LIVE_REGION_ROLES.has(context.effectiveRole)) {
1322
+ if (_AriaPolicyEngine.#hasRole(context.props) || isNonNull(context.effectiveRole) && LIVE_REGION_ROLES.has(context.effectiveRole)) {
1194
1323
  return _AriaPolicyEngine.#pipeline;
1195
1324
  }
1196
1325
  return _AriaPolicyEngine.#implicitOnlyRules;
@@ -1392,7 +1521,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1392
1521
  implicitRole
1393
1522
  }) {
1394
1523
  const role = props.role;
1395
- if (!implicitRole || !role || role === implicitRole) return NO_VIOLATIONS;
1524
+ if (!implicitRole || !role || role === implicitRole) return NO_VIOLATIONS2;
1396
1525
  if (isStrongImplicitRole(tag) && role === "region") {
1397
1526
  const diagnostic = HtmlDiagnostics.implicitRoleOverride(tag, implicitRole, role);
1398
1527
  return [
@@ -1405,11 +1534,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1405
1534
  }
1406
1535
  ];
1407
1536
  }
1408
- return NO_VIOLATIONS;
1537
+ return NO_VIOLATIONS2;
1409
1538
  }
1410
1539
  static #checkRedundantRole({ tag, props, implicitRole }) {
1411
1540
  const role = props.role;
1412
- if (!implicitRole || !role || role !== implicitRole) return NO_VIOLATIONS;
1541
+ if (!implicitRole || !role || role !== implicitRole) return NO_VIOLATIONS2;
1413
1542
  const diagnostic = HtmlDiagnostics.implicitRoleRedundant(tag, implicitRole);
1414
1543
  return [
1415
1544
  {
@@ -1423,8 +1552,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1423
1552
  }
1424
1553
  static #checkStandaloneRegion({ tag, props, implicitRole }) {
1425
1554
  const role = props.role;
1426
- if (role !== "region") return NO_VIOLATIONS;
1427
- if (!isStandaloneTag(tag)) return NO_VIOLATIONS;
1555
+ if (role !== "region") return NO_VIOLATIONS2;
1556
+ if (!isStandaloneTag(tag)) return NO_VIOLATIONS2;
1428
1557
  const diagnostic = HtmlDiagnostics.standaloneRegionOverride(tag, implicitRole ?? tag);
1429
1558
  return [
1430
1559
  {
@@ -1441,7 +1570,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1441
1570
  props,
1442
1571
  effectiveRole
1443
1572
  }) {
1444
- if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS;
1573
+ if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
1445
1574
  const results = [];
1446
1575
  iterate.forEachEntry(props, (key) => {
1447
1576
  if (!key.startsWith("aria-")) return;
@@ -1459,62 +1588,6 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1459
1588
  return results;
1460
1589
  }
1461
1590
  // ─── ARIA attribute value validation ──────────────────────────────────────
1462
- // Accepted value shapes for typed ARIA attributes.
1463
- // Attributes not in this map are unconstrained (arbitrary string values permitted).
1464
- static #ARIA_VALUE_TYPES = /* @__PURE__ */ new Map([
1465
- // Boolean (true | false)
1466
- ["aria-atomic", { kind: "boolean" }],
1467
- ["aria-busy", { kind: "boolean" }],
1468
- ["aria-disabled", { kind: "boolean" }],
1469
- ["aria-expanded", { kind: "boolean" }],
1470
- ["aria-hidden", { kind: "boolean" }],
1471
- ["aria-modal", { kind: "boolean" }],
1472
- ["aria-multiline", { kind: "boolean" }],
1473
- ["aria-multiselectable", { kind: "boolean" }],
1474
- ["aria-readonly", { kind: "boolean" }],
1475
- ["aria-required", { kind: "boolean" }],
1476
- ["aria-selected", { kind: "boolean" }],
1477
- // Tristate (true | false | mixed)
1478
- ["aria-checked", { kind: "tristate" }],
1479
- ["aria-pressed", { kind: "tristate" }],
1480
- // Numeric (any finite number)
1481
- ["aria-valuenow", { kind: "number" }],
1482
- ["aria-valuemin", { kind: "number" }],
1483
- ["aria-valuemax", { kind: "number" }],
1484
- // Integer with optional range
1485
- ["aria-level", { kind: "integer", min: 1, max: 6 }],
1486
- ["aria-posinset", { kind: "integer", min: 1 }],
1487
- ["aria-setsize", { kind: "integer", min: -1 }],
1488
- ["aria-rowcount", { kind: "integer", min: -1 }],
1489
- ["aria-colcount", { kind: "integer", min: -1 }],
1490
- ["aria-rowindex", { kind: "integer", min: 1 }],
1491
- ["aria-colindex", { kind: "integer", min: 1 }],
1492
- ["aria-rowspan", { kind: "integer", min: 0 }],
1493
- ["aria-colspan", { kind: "integer", min: 0 }],
1494
- // Enum (specific allowed tokens)
1495
- ["aria-autocomplete", { kind: "enum", values: /* @__PURE__ */ new Set(["inline", "list", "both", "none"]) }],
1496
- [
1497
- "aria-current",
1498
- {
1499
- kind: "enum",
1500
- values: /* @__PURE__ */ new Set(["page", "step", "location", "date", "time", "true", "false"])
1501
- }
1502
- ],
1503
- [
1504
- "aria-haspopup",
1505
- {
1506
- kind: "enum",
1507
- values: /* @__PURE__ */ new Set(["false", "true", "menu", "listbox", "tree", "grid", "dialog"])
1508
- }
1509
- ],
1510
- ["aria-invalid", { kind: "enum", values: /* @__PURE__ */ new Set(["grammar", "false", "spelling", "true"]) }],
1511
- ["aria-live", { kind: "enum", values: /* @__PURE__ */ new Set(["assertive", "off", "polite"]) }],
1512
- [
1513
- "aria-orientation",
1514
- { kind: "enum", values: /* @__PURE__ */ new Set(["horizontal", "vertical", "undefined"]) }
1515
- ],
1516
- ["aria-sort", { kind: "enum", values: /* @__PURE__ */ new Set(["ascending", "descending", "none", "other"]) }]
1517
- ]);
1518
1591
  static #isValidAriaValue(value, type) {
1519
1592
  switch (type.kind) {
1520
1593
  case "boolean":
@@ -1559,11 +1632,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1559
1632
  }
1560
1633
  }
1561
1634
  static #checkAriaAttributeValues({ props, effectiveRole }) {
1562
- if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS;
1635
+ if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
1563
1636
  const results = [];
1564
1637
  iterate.forEachEntry(props, (key, value) => {
1565
1638
  if (!key.startsWith("aria-")) return;
1566
- const type = _AriaPolicyEngine.#ARIA_VALUE_TYPES.get(key);
1639
+ const type = ARIA_VALUE_TYPES.get(key);
1567
1640
  if (!isNonNull(type)) return;
1568
1641
  if (_AriaPolicyEngine.#isValidAriaValue(value, type)) return;
1569
1642
  results.push({
@@ -1582,26 +1655,18 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1582
1655
  return results;
1583
1656
  }
1584
1657
  // ─── Heading implicit level ────────────────────────────────────────────────
1585
- static #HEADING_IMPLICIT_LEVELS = /* @__PURE__ */ new Map([
1586
- ["h1", 1],
1587
- ["h2", 2],
1588
- ["h3", 3],
1589
- ["h4", 4],
1590
- ["h5", 5],
1591
- ["h6", 6]
1592
- ]);
1593
1658
  static #checkRedundantAriaLevel({
1594
1659
  tag,
1595
1660
  props,
1596
1661
  effectiveRole
1597
1662
  }) {
1598
- if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS;
1599
- const implicitLevel = _AriaPolicyEngine.#HEADING_IMPLICIT_LEVELS.get(tag);
1600
- if (!isNonNull(implicitLevel)) return NO_VIOLATIONS;
1663
+ if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
1664
+ const implicitLevel = HEADING_IMPLICIT_LEVELS.get(tag);
1665
+ if (!isNonNull(implicitLevel)) return NO_VIOLATIONS2;
1601
1666
  const raw = props["aria-level"];
1602
- if (!isNonNull(raw)) return NO_VIOLATIONS;
1667
+ if (!isNonNull(raw)) return NO_VIOLATIONS2;
1603
1668
  const n = typeof raw === "number" ? raw : typeof raw === "string" ? parseInt(raw, 10) : NaN;
1604
- if (!Number.isFinite(n) || n !== implicitLevel) return NO_VIOLATIONS;
1669
+ if (!Number.isFinite(n) || n !== implicitLevel) return NO_VIOLATIONS2;
1605
1670
  return [
1606
1671
  {
1607
1672
  valid: false,
@@ -1614,20 +1679,14 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1614
1679
  ];
1615
1680
  }
1616
1681
  // ─── Name-required roles ───────────────────────────────────────────────────
1617
- // Roles that always require an accessible name per WAI-ARIA APG.
1618
- // Dialog and landmark names are enforced via contracts (ariaContract) rather than
1619
- // the built-in pipeline so consumers can opt in; img is built in because role=img
1620
- // on any element (including bare <img>) is definitionally useless without a name.
1621
- static #NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["img"]);
1622
1682
  static #checkNameRequiredRoles({
1623
1683
  tag,
1624
1684
  props,
1625
1685
  effectiveRole
1626
1686
  }) {
1627
- if (!effectiveRole || !_AriaPolicyEngine.#NAME_REQUIRED_ROLES.has(effectiveRole))
1628
- return NO_VIOLATIONS;
1629
- if ("aria-label" in props || "aria-labelledby" in props) return NO_VIOLATIONS;
1630
- if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return NO_VIOLATIONS;
1687
+ if (!effectiveRole || !NAME_REQUIRED_ROLES.has(effectiveRole)) return NO_VIOLATIONS2;
1688
+ if ("aria-label" in props || "aria-labelledby" in props) return NO_VIOLATIONS2;
1689
+ if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return NO_VIOLATIONS2;
1631
1690
  return [
1632
1691
  {
1633
1692
  valid: false,
@@ -1637,51 +1696,21 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1637
1696
  }
1638
1697
  ];
1639
1698
  }
1640
- // WAI-ARIA 1.2 required states and properties, keyed by role.
1641
- // Source: https://www.w3.org/TR/wai-aria-1.2/#requiredState
1642
- static #REQUIRED_PROPERTIES = /* @__PURE__ */ new Map([
1643
- ["combobox", ["aria-expanded"]],
1644
- ["option", ["aria-selected"]],
1645
- ["slider", ["aria-valuenow"]],
1646
- ["scrollbar", ["aria-controls", "aria-valuenow"]],
1647
- ["spinbutton", ["aria-valuenow"]]
1648
- ]);
1649
- static #checkRequiredAriaProperties({
1650
- props,
1651
- effectiveRole
1652
- }) {
1653
- if (!effectiveRole) return NO_VIOLATIONS;
1654
- const required = _AriaPolicyEngine.#REQUIRED_PROPERTIES.get(effectiveRole);
1655
- if (!isNonNull(required)) return NO_VIOLATIONS;
1656
- const results = [];
1657
- iterate.forEach(required, (attr) => {
1658
- if (attr in props) return;
1659
- results.push({
1660
- valid: false,
1661
- fixable: false,
1662
- severity: "warning",
1663
- attribute: attr,
1664
- diagnostic: AriaDiagnostics.requiredProperty(attr, effectiveRole)
1665
- });
1666
- });
1667
- return results;
1699
+ static #requiredAriaPropertiesRule = {
1700
+ attributesByRole: REQUIRED_ARIA_PROPERTIES,
1701
+ diagnosticFor: (attribute, role) => AriaDiagnostics.requiredProperty(attribute, role)
1702
+ };
1703
+ static #checkRequiredAriaProperties(context) {
1704
+ return checkRequiredAttributes(_AriaPolicyEngine.#requiredAriaPropertiesRule, context);
1668
1705
  }
1669
- // Natively interactive HTML elements — always keyboard-reachable unless explicitly disabled.
1670
- static #INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
1671
- "a",
1672
- "button",
1673
- "input",
1674
- "select",
1675
- "textarea"
1676
- ]);
1677
1706
  // WAI-ARIA 1.2 §6.6: aria-hidden="true" must not be placed on focusable elements.
1678
1707
  static #checkAriaHiddenOnFocusable({ tag, props }) {
1679
- if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return NO_VIOLATIONS;
1680
- const isInteractive = _AriaPolicyEngine.#INTERACTIVE_TAGS.has(tag);
1708
+ if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return NO_VIOLATIONS2;
1709
+ const isInteractive = INTERACTIVE_TAGS.has(tag);
1681
1710
  if (!isInteractive) {
1682
1711
  const tabindex = props.tabindex;
1683
1712
  const n = typeof tabindex === "number" ? tabindex : typeof tabindex === "string" ? parseInt(tabindex, 10) : NaN;
1684
- if (!Number.isFinite(n) || n < 0) return NO_VIOLATIONS;
1713
+ if (!Number.isFinite(n) || n < 0) return NO_VIOLATIONS2;
1685
1714
  }
1686
1715
  return [
1687
1716
  {
@@ -1700,7 +1729,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1700
1729
  props,
1701
1730
  effectiveRole
1702
1731
  }) {
1703
- if (effectiveRole !== "none" && effectiveRole !== "presentation") return NO_VIOLATIONS;
1732
+ if (effectiveRole !== "none" && effectiveRole !== "presentation") return NO_VIOLATIONS2;
1704
1733
  const results = [];
1705
1734
  iterate.forEachEntry(props, (key) => {
1706
1735
  if (!key.startsWith("aria-")) return;
@@ -1716,18 +1745,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1716
1745
  });
1717
1746
  return results;
1718
1747
  }
1719
- // WAI-ARIA live region roles and their implied aria-live politeness values.
1720
- static #LIVE_REGION_ROLES = /* @__PURE__ */ new Map([
1721
- ["alert", "assertive"],
1722
- ["status", "polite"],
1723
- ["log", "polite"],
1724
- ["timer", "off"]
1725
- ]);
1726
1748
  static #checkMissingLiveRegion({ effectiveRole, props }) {
1727
- if (!effectiveRole) return NO_VIOLATIONS;
1728
- const impliedLive = _AriaPolicyEngine.#LIVE_REGION_ROLES.get(effectiveRole);
1729
- if (!impliedLive) return NO_VIOLATIONS;
1730
- if ("aria-live" in props) return NO_VIOLATIONS;
1749
+ if (!effectiveRole) return NO_VIOLATIONS2;
1750
+ const impliedLive = LIVE_REGION_ROLES.get(effectiveRole);
1751
+ if (!impliedLive) return NO_VIOLATIONS2;
1752
+ if ("aria-live" in props) return NO_VIOLATIONS2;
1731
1753
  const injectLive = {
1732
1754
  kind: `injectLive:${effectiveRole}`,
1733
1755
  apply: (ctx) => ({
@@ -1746,20 +1768,13 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1746
1768
  }
1747
1769
  ];
1748
1770
  }
1749
- static #checkMissingAtomic({ effectiveRole, props }) {
1750
- if (!effectiveRole || !_AriaPolicyEngine.#LIVE_REGION_ROLES.has(effectiveRole))
1751
- return NO_VIOLATIONS;
1752
- if ("aria-atomic" in props) return NO_VIOLATIONS;
1753
- return [
1754
- {
1755
- valid: false,
1756
- fixable: false,
1757
- severity: "warning",
1758
- diagnostic: AriaDiagnostics.missingAtomic(effectiveRole)
1759
- }
1760
- ];
1771
+ static #missingAtomicRule = {
1772
+ attributesByRole: ATOMIC_REQUIREMENTS,
1773
+ diagnosticFor: (_attribute, role) => AriaDiagnostics.missingAtomic(role)
1774
+ };
1775
+ static #checkMissingAtomic(context) {
1776
+ return checkRequiredAttributes(_AriaPolicyEngine.#missingAtomicRule, context);
1761
1777
  }
1762
- static #VALID_RELEVANT_TOKENS = /* @__PURE__ */ new Set(["additions", "removals", "text", "all"]);
1763
1778
  // Custom fix rules passed via `options.rules` must be pure functions of (tag, props) — the cache
1764
1779
  // replays stored fixes against new prop objects, so fixes that close over external state will
1765
1780
  // produce inconsistent results on cache hits.
@@ -1773,10 +1788,10 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1773
1788
  };
1774
1789
  static #checkInvalidAriaRelevant({ props }) {
1775
1790
  const relevant = props["aria-relevant"];
1776
- if (relevant === void 0) return NO_VIOLATIONS;
1777
- if (typeof relevant !== "string") return NO_VIOLATIONS;
1791
+ if (relevant === void 0) return NO_VIOLATIONS2;
1792
+ if (typeof relevant !== "string") return NO_VIOLATIONS2;
1778
1793
  const tokens = relevant.trim().split(/\s+/);
1779
- const invalid = tokens.filter((t) => !_AriaPolicyEngine.#VALID_RELEVANT_TOKENS.has(t));
1794
+ const invalid = tokens.filter((t) => !VALID_RELEVANT_TOKENS.has(t));
1780
1795
  if (invalid.length > 0) {
1781
1796
  return [
1782
1797
  {
@@ -1801,7 +1816,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1801
1816
  }
1802
1817
  ];
1803
1818
  }
1804
- return NO_VIOLATIONS;
1819
+ return NO_VIOLATIONS2;
1805
1820
  }
1806
1821
  };
1807
1822
 
@@ -2121,7 +2136,59 @@ var readonlyProps = ({
2121
2136
  // ../core/src/html/evaluators.ts
2122
2137
  import { warnDiagnostics as warnDiagnostics2 } from "../_shared/diagnostics.js";
2123
2138
 
2124
- // ../core/src/html/input-rules.ts
2139
+ // ../core/src/html/spec/vocabulary/input.ts
2140
+ var TEXT_INPUT_TYPES = ["text", "search", "url", "tel", "email", "password"];
2141
+ var NUMERIC_INPUT_TYPES = [
2142
+ "number",
2143
+ "range",
2144
+ "date",
2145
+ "month",
2146
+ "week",
2147
+ "time",
2148
+ "datetime-local"
2149
+ ];
2150
+ var HTML_INPUT_TYPES = /* @__PURE__ */ new Set([
2151
+ ...TEXT_INPUT_TYPES,
2152
+ ...NUMERIC_INPUT_TYPES,
2153
+ "checkbox",
2154
+ "radio",
2155
+ "file",
2156
+ "color",
2157
+ "hidden",
2158
+ "button",
2159
+ "submit",
2160
+ "reset",
2161
+ "image"
2162
+ ]);
2163
+
2164
+ // ../core/src/html/spec/attributes/input.ts
2165
+ var INPUT_ATTRIBUTE_TYPE_POLICIES = [
2166
+ { attribute: "checked", allowedTypes: ["checkbox", "radio"] },
2167
+ { attribute: "multiple", allowedTypes: ["email", "file"] },
2168
+ { attribute: "maxLength", allowedTypes: TEXT_INPUT_TYPES },
2169
+ { attribute: "minLength", allowedTypes: TEXT_INPUT_TYPES },
2170
+ { attribute: "pattern", allowedTypes: TEXT_INPUT_TYPES },
2171
+ { attribute: "min", allowedTypes: NUMERIC_INPUT_TYPES },
2172
+ { attribute: "max", allowedTypes: NUMERIC_INPUT_TYPES },
2173
+ { attribute: "step", allowedTypes: NUMERIC_INPUT_TYPES },
2174
+ { attribute: "accept", allowedTypes: ["file"] },
2175
+ { attribute: "capture", allowedTypes: ["file"] },
2176
+ { attribute: "size", allowedTypes: TEXT_INPUT_TYPES },
2177
+ { attribute: "alt", allowedTypes: ["image"] },
2178
+ { attribute: "height", allowedTypes: ["image"] },
2179
+ { attribute: "width", allowedTypes: ["image"] }
2180
+ ];
2181
+
2182
+ // ../core/src/html/spec/constraints/input.ts
2183
+ var REQUIRED_READONLY_CONFLICT = {
2184
+ props: ["required", "readOnly"],
2185
+ diagnostic: () => InputAccessibilityDiagnostics.requiredReadOnlyConflict()
2186
+ };
2187
+ var INPUT_MUTUALLY_EXCLUSIVE_POLICIES = [
2188
+ REQUIRED_READONLY_CONFLICT
2189
+ ];
2190
+
2191
+ // ../core/src/html/spec/validators/attribute-type-validator.ts
2125
2192
  var DEFAULT_INPUT_TYPE = "text";
2126
2193
  function omit(props, key) {
2127
2194
  const next = { ...props };
@@ -2137,7 +2204,10 @@ function removeAttributeFix(attribute) {
2137
2204
  }
2138
2205
  };
2139
2206
  }
2140
- function inputAttributeRequiresType(attribute, allowedTypes) {
2207
+ function createInputAttributeTypeRule({
2208
+ attribute,
2209
+ allowedTypes
2210
+ }) {
2141
2211
  const rule = ({ tag, props }) => {
2142
2212
  if (tag !== "input" || !(attribute in props)) return [];
2143
2213
  const type = typeof props.type === "string" ? props.type : DEFAULT_INPUT_TYPE;
@@ -2153,31 +2223,30 @@ function inputAttributeRequiresType(attribute, allowedTypes) {
2153
2223
  }
2154
2224
  ];
2155
2225
  };
2156
- return Object.assign(rule, { readsProps: ["type", attribute] });
2226
+ return Object.assign(rule, { readsProps: ["type", attribute], tags: ["input"] });
2227
+ }
2228
+
2229
+ // ../core/src/html/spec/validators/mutually-exclusive-validator.ts
2230
+ function createMutuallyExclusiveRule({
2231
+ props: conflictingProps,
2232
+ diagnostic: createDiagnostic
2233
+ }) {
2234
+ const [first, second] = conflictingProps;
2235
+ const rule = ({ tag, props }) => {
2236
+ if (tag !== "input" || !props[first] || !props[second]) return [];
2237
+ const diagnostic = createDiagnostic();
2238
+ return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
2239
+ };
2240
+ return Object.assign(rule, { readsProps: conflictingProps, tags: ["input"] });
2241
+ }
2242
+
2243
+ // ../core/src/html/input-rules.ts
2244
+ var policyByAttribute = Object.fromEntries(
2245
+ INPUT_ATTRIBUTE_TYPE_POLICIES.map((policy) => [policy.attribute, policy])
2246
+ );
2247
+ function policyFor(attribute) {
2248
+ return policyByAttribute[attribute];
2157
2249
  }
2158
- var TEXT_INPUT_TYPES = ["text", "search", "url", "tel", "email", "password"];
2159
- var NUMERIC_INPUT_TYPES = [
2160
- "number",
2161
- "range",
2162
- "date",
2163
- "month",
2164
- "week",
2165
- "time",
2166
- "datetime-local"
2167
- ];
2168
- var HTML_INPUT_TYPES = /* @__PURE__ */ new Set([
2169
- ...TEXT_INPUT_TYPES,
2170
- ...NUMERIC_INPUT_TYPES,
2171
- "checkbox",
2172
- "radio",
2173
- "file",
2174
- "color",
2175
- "hidden",
2176
- "button",
2177
- "submit",
2178
- "reset",
2179
- "image"
2180
- ]);
2181
2250
  var supportedInputTypeRule = Object.assign(
2182
2251
  ({ tag, props }) => {
2183
2252
  if (tag !== "input" || typeof props.type !== "string") return [];
@@ -2193,30 +2262,22 @@ var supportedInputTypeRule = Object.assign(
2193
2262
  }
2194
2263
  ];
2195
2264
  },
2196
- { readsProps: ["type"] }
2197
- );
2198
- var checkedRequiresCheckableTypeRule = inputAttributeRequiresType("checked", [
2199
- "checkbox",
2200
- "radio"
2201
- ]);
2202
- var multipleRequiresSupportedTypeRule = inputAttributeRequiresType("multiple", [
2203
- "email",
2204
- "file"
2205
- ]);
2206
- var maxLengthRequiresTextTypeRule = inputAttributeRequiresType(
2207
- "maxLength",
2208
- TEXT_INPUT_TYPES
2209
- );
2210
- var minLengthRequiresTextTypeRule = inputAttributeRequiresType(
2211
- "minLength",
2212
- TEXT_INPUT_TYPES
2265
+ { readsProps: ["type"], tags: ["input"] }
2213
2266
  );
2214
- var patternRequiresTextTypeRule = inputAttributeRequiresType("pattern", TEXT_INPUT_TYPES);
2215
- var minRequiresNumericTypeRule = inputAttributeRequiresType("min", NUMERIC_INPUT_TYPES);
2216
- var maxRequiresNumericTypeRule = inputAttributeRequiresType("max", NUMERIC_INPUT_TYPES);
2217
- var stepRequiresNumericTypeRule = inputAttributeRequiresType("step", NUMERIC_INPUT_TYPES);
2218
- var acceptRequiresFileTypeRule = inputAttributeRequiresType("accept", ["file"]);
2219
- var captureRequiresFileTypeRule = inputAttributeRequiresType("capture", ["file"]);
2267
+ var checkedRequiresCheckableTypeRule = createInputAttributeTypeRule(policyFor("checked"));
2268
+ var multipleRequiresSupportedTypeRule = createInputAttributeTypeRule(policyFor("multiple"));
2269
+ var maxLengthRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("maxLength"));
2270
+ var minLengthRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("minLength"));
2271
+ var patternRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("pattern"));
2272
+ var minRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("min"));
2273
+ var maxRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("max"));
2274
+ var stepRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("step"));
2275
+ var acceptRequiresFileTypeRule = createInputAttributeTypeRule(policyFor("accept"));
2276
+ var captureRequiresFileTypeRule = createInputAttributeTypeRule(policyFor("capture"));
2277
+ var sizeRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("size"));
2278
+ var altRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("alt"));
2279
+ var heightRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("height"));
2280
+ var widthRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("width"));
2220
2281
  var inputAccessibleNameRule = Object.assign(
2221
2282
  ({ tag, props }) => {
2222
2283
  if (tag !== "input" || props.type === "hidden") return [];
@@ -2225,7 +2286,10 @@ var inputAccessibleNameRule = Object.assign(
2225
2286
  const diagnostic = hasPlaceholder ? InputAccessibilityDiagnostics.placeholderIsNotLabel() : InputAccessibilityDiagnostics.missingAccessibleName();
2226
2287
  return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
2227
2288
  },
2228
- { readsProps: ["type", "aria-label", "aria-labelledby", "placeholder"] }
2289
+ {
2290
+ readsProps: ["type", "aria-label", "aria-labelledby", "placeholder"],
2291
+ tags: ["input"]
2292
+ }
2229
2293
  );
2230
2294
  var PASSWORD_AUTOCOMPLETE_VALUES = ["current-password", "new-password"];
2231
2295
  var passwordAutocompleteRule = Object.assign(
@@ -2237,16 +2301,9 @@ var passwordAutocompleteRule = Object.assign(
2237
2301
  const diagnostic = InputAccessibilityDiagnostics.passwordMissingAutocomplete();
2238
2302
  return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
2239
2303
  },
2240
- { readsProps: ["type", "autoComplete"] }
2241
- );
2242
- var requiredReadOnlyConflictRule = Object.assign(
2243
- ({ tag, props }) => {
2244
- if (tag !== "input" || !props.required || !props.readOnly) return [];
2245
- const diagnostic = InputAccessibilityDiagnostics.requiredReadOnlyConflict();
2246
- return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
2247
- },
2248
- { readsProps: ["required", "readOnly"] }
2304
+ { readsProps: ["type", "autoComplete"], tags: ["input"] }
2249
2305
  );
2306
+ var requiredReadOnlyConflictRule = createMutuallyExclusiveRule(REQUIRED_READONLY_CONFLICT);
2250
2307
  var INPUT_RULES = [
2251
2308
  supportedInputTypeRule,
2252
2309
  checkedRequiresCheckableTypeRule,
@@ -2259,11 +2316,132 @@ var INPUT_RULES = [
2259
2316
  stepRequiresNumericTypeRule,
2260
2317
  acceptRequiresFileTypeRule,
2261
2318
  captureRequiresFileTypeRule,
2319
+ sizeRequiresTextTypeRule,
2320
+ altRequiresImageTypeRule,
2321
+ heightRequiresImageTypeRule,
2322
+ widthRequiresImageTypeRule,
2262
2323
  inputAccessibleNameRule,
2263
2324
  passwordAutocompleteRule,
2264
2325
  requiredReadOnlyConflictRule
2265
2326
  ];
2266
2327
 
2328
+ // ../core/src/html/spec/types.ts
2329
+ function definePropRolePolicy(prop, map2, fallback) {
2330
+ return { kind: "byProp", prop, map: map2, fallback };
2331
+ }
2332
+ function resolveAllowedRoles(spec, props) {
2333
+ const policy = spec.allowedRoles;
2334
+ if (!policy) return void 0;
2335
+ switch (policy.kind) {
2336
+ case "fixed":
2337
+ return policy.roles;
2338
+ case "byProp": {
2339
+ const value = typeof props[policy.prop] === "string" ? props[policy.prop] : policy.fallback;
2340
+ return policy.map[value];
2341
+ }
2342
+ case "dynamic":
2343
+ return policy.resolve({ props });
2344
+ }
2345
+ }
2346
+
2347
+ // ../core/src/html/spec/roles/input.ts
2348
+ var ALLOWED_INPUT_ROLES = {
2349
+ checkbox: ["menuitemcheckbox", "option", "switch", "button"],
2350
+ radio: ["menuitemradio"],
2351
+ range: [],
2352
+ number: [],
2353
+ search: ["combobox"],
2354
+ text: ["combobox", "searchbox", "spinbutton"],
2355
+ email: ["combobox"],
2356
+ tel: ["combobox"],
2357
+ url: ["combobox"],
2358
+ button: [
2359
+ "link",
2360
+ "menuitem",
2361
+ "menuitemcheckbox",
2362
+ "menuitemradio",
2363
+ "option",
2364
+ "radio",
2365
+ "switch",
2366
+ "tab"
2367
+ ],
2368
+ submit: [
2369
+ "link",
2370
+ "menuitem",
2371
+ "menuitemcheckbox",
2372
+ "menuitemradio",
2373
+ "option",
2374
+ "radio",
2375
+ "switch",
2376
+ "tab"
2377
+ ],
2378
+ reset: [
2379
+ "link",
2380
+ "menuitem",
2381
+ "menuitemcheckbox",
2382
+ "menuitemradio",
2383
+ "option",
2384
+ "radio",
2385
+ "switch",
2386
+ "tab"
2387
+ ],
2388
+ image: [
2389
+ "link",
2390
+ "menuitem",
2391
+ "menuitemcheckbox",
2392
+ "menuitemradio",
2393
+ "option",
2394
+ "radio",
2395
+ "switch",
2396
+ "tab"
2397
+ ],
2398
+ hidden: []
2399
+ };
2400
+
2401
+ // ../core/src/html/spec/elements/input.ts
2402
+ var inputElementSpec = {
2403
+ tag: "input",
2404
+ allowedRoles: definePropRolePolicy("type", ALLOWED_INPUT_ROLES, "text"),
2405
+ attributes: INPUT_ATTRIBUTE_TYPE_POLICIES,
2406
+ mutuallyExclusive: INPUT_MUTUALLY_EXCLUSIVE_POLICIES
2407
+ };
2408
+
2409
+ // ../core/src/html/spec/roles/img.ts
2410
+ var IMG_NAMED_ROLES = [
2411
+ "button",
2412
+ "checkbox",
2413
+ "link",
2414
+ "menuitem",
2415
+ "menuitemcheckbox",
2416
+ "menuitemradio",
2417
+ "option",
2418
+ "progressbar",
2419
+ "scrollbar",
2420
+ "separator",
2421
+ "slider",
2422
+ "switch",
2423
+ "tab",
2424
+ "treeitem"
2425
+ ];
2426
+
2427
+ // ../core/src/html/spec/elements/img.ts
2428
+ var imgElementSpec = {
2429
+ tag: "img",
2430
+ allowedRoles: {
2431
+ kind: "dynamic",
2432
+ resolve: ({ props }) => props.alt === "" ? [] : IMG_NAMED_ROLES
2433
+ }
2434
+ };
2435
+
2436
+ // ../core/src/html/spec/roles/table.ts
2437
+ var ALLOWED_TABLE_ROLES = ["grid", "treegrid"];
2438
+
2439
+ // ../core/src/html/spec/elements/table.ts
2440
+ var tableElementSpec = {
2441
+ tag: "table",
2442
+ allowedRoles: { kind: "fixed", roles: ALLOWED_TABLE_ROLES }
2443
+ };
2444
+
2267
2445
  // ../core/src/html/role-restrictions.ts
2268
2446
  var ALLOWED_ROLES = {
2269
2447
  article: ["application", "document", "feed", "main", "none", "presentation", "region"],
@@ -2336,86 +2514,17 @@ var ALLOWED_ROLES = {
2336
2514
  "tab",
2337
2515
  "treeitem"
2338
2516
  ],
2339
- table: ["grid", "treegrid"],
2340
2517
  dialog: ["alertdialog"],
2341
2518
  fieldset: ["none", "presentation", "radiogroup"]
2342
2519
  };
2343
- var IMG_NAMED_ROLES = [
2344
- "button",
2345
- "checkbox",
2346
- "link",
2347
- "menuitem",
2348
- "menuitemcheckbox",
2349
- "menuitemradio",
2350
- "option",
2351
- "progressbar",
2352
- "scrollbar",
2353
- "separator",
2354
- "slider",
2355
- "switch",
2356
- "tab",
2357
- "treeitem"
2358
- ];
2359
- var ALLOWED_INPUT_ROLES = {
2360
- checkbox: ["menuitemcheckbox", "option", "switch", "button"],
2361
- radio: ["menuitemradio"],
2362
- range: [],
2363
- number: [],
2364
- search: ["combobox"],
2365
- text: ["combobox", "searchbox", "spinbutton"],
2366
- email: ["combobox"],
2367
- tel: ["combobox"],
2368
- url: ["combobox"],
2369
- button: [
2370
- "link",
2371
- "menuitem",
2372
- "menuitemcheckbox",
2373
- "menuitemradio",
2374
- "option",
2375
- "radio",
2376
- "switch",
2377
- "tab"
2378
- ],
2379
- submit: [
2380
- "link",
2381
- "menuitem",
2382
- "menuitemcheckbox",
2383
- "menuitemradio",
2384
- "option",
2385
- "radio",
2386
- "switch",
2387
- "tab"
2388
- ],
2389
- reset: [
2390
- "link",
2391
- "menuitem",
2392
- "menuitemcheckbox",
2393
- "menuitemradio",
2394
- "option",
2395
- "radio",
2396
- "switch",
2397
- "tab"
2398
- ],
2399
- image: [
2400
- "link",
2401
- "menuitem",
2402
- "menuitemcheckbox",
2403
- "menuitemradio",
2404
- "option",
2405
- "radio",
2406
- "switch",
2407
- "tab"
2408
- ],
2409
- hidden: []
2520
+ var ELEMENT_SPECS = {
2521
+ input: inputElementSpec,
2522
+ img: imgElementSpec,
2523
+ table: tableElementSpec
2410
2524
  };
2411
2525
  function getAllowedRoles(tag, props) {
2412
- if (tag === "input") {
2413
- const type = typeof props.type === "string" ? props.type : "text";
2414
- return ALLOWED_INPUT_ROLES[type];
2415
- }
2416
- if (tag === "img") {
2417
- return props.alt === "" ? [] : IMG_NAMED_ROLES;
2418
- }
2526
+ const spec = ELEMENT_SPECS[tag];
2527
+ if (spec) return resolveAllowedRoles(spec, props);
2419
2528
  return ALLOWED_ROLES[tag];
2420
2529
  }
2421
2530
  var removeRoleFix = {
@@ -2456,21 +2565,24 @@ var removeLandmarkRoleOverride = {
2456
2565
  return { applied: true, next: rest, previous: props };
2457
2566
  }
2458
2567
  };
2459
- function landmarkRoleRule({ tag, props, implicitRole }) {
2460
- if (!LANDMARK_TAG_SET.has(tag) || !implicitRole) return [];
2461
- const role = props.role;
2462
- if (!role || role === implicitRole) return [];
2463
- const diagnostic = HtmlDiagnostics.landmarkRoleOverride(tag, implicitRole, role);
2464
- return [
2465
- {
2466
- valid: false,
2467
- fixable: true,
2468
- severity: diagnostic.severity,
2469
- fix: removeLandmarkRoleOverride,
2470
- diagnostic
2471
- }
2472
- ];
2473
- }
2568
+ var landmarkRoleRule = Object.assign(
2569
+ ({ tag, props, implicitRole }) => {
2570
+ if (!LANDMARK_TAG_SET.has(tag) || !implicitRole) return [];
2571
+ const role = props.role;
2572
+ if (!role || role === implicitRole) return [];
2573
+ const diagnostic = HtmlDiagnostics.landmarkRoleOverride(tag, implicitRole, role);
2574
+ return [
2575
+ {
2576
+ valid: false,
2577
+ fixable: true,
2578
+ severity: diagnostic.severity,
2579
+ fix: removeLandmarkRoleOverride,
2580
+ diagnostic
2581
+ }
2582
+ ];
2583
+ },
2584
+ { tags: [...LANDMARK_TAG_SET] }
2585
+ );
2474
2586
  function requireAccessibleName({ tag, props }) {
2475
2587
  if ("aria-label" in props || "aria-labelledby" in props) return [];
2476
2588
  return [
@@ -2483,10 +2595,13 @@ function requireAccessibleName({ tag, props }) {
2483
2595
  ];
2484
2596
  }
2485
2597
  var NAMED_LANDMARK_TAGS = /* @__PURE__ */ new Set(["nav", "aside"]);
2486
- function landmarkNameAdvisory(ctx) {
2487
- if (!ctx.implicitRole || !NAMED_LANDMARK_TAGS.has(ctx.tag)) return [];
2488
- return requireAccessibleName(ctx);
2489
- }
2598
+ var landmarkNameAdvisory = Object.assign(
2599
+ (ctx) => {
2600
+ if (!ctx.implicitRole || !NAMED_LANDMARK_TAGS.has(ctx.tag)) return [];
2601
+ return requireAccessibleName(ctx);
2602
+ },
2603
+ { tags: [...NAMED_LANDMARK_TAGS] }
2604
+ );
2490
2605
  var HTML_ARIA_RULES = [
2491
2606
  landmarkRoleRule,
2492
2607
  landmarkNameAdvisory,
@@ -2587,6 +2702,62 @@ var figureContract = contract([
2587
2702
  ]);
2588
2703
  var detailsContract = firstChildContract("summary", "summary");
2589
2704
  var fieldsetContract = firstChildContract("legend", "legend");
2705
+ var objectContract = contract([
2706
+ { name: "param", match: isTag("param") },
2707
+ { name: "content", match: isOpenContent("param") }
2708
+ ]);
2709
+ var INTERACTIVE_CONTENT_TAGS = ["a", "button", "input", "select", "textarea", "label"];
2710
+ var buttonContract = closedContract([
2711
+ { name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
2712
+ ]);
2713
+ var anchorContract = closedContract([
2714
+ { name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
2715
+ ]);
2716
+ var LABELABLE_TAGS = [
2717
+ "button",
2718
+ "input",
2719
+ "meter",
2720
+ "output",
2721
+ "progress",
2722
+ "select",
2723
+ "textarea"
2724
+ ];
2725
+ var labelContract = contract([
2726
+ { name: "control", match: isTag(...LABELABLE_TAGS), cardinality: { max: 1 } }
2727
+ ]);
2728
+ var P_BLOCKED_TAGS = [
2729
+ "address",
2730
+ "article",
2731
+ "aside",
2732
+ "blockquote",
2733
+ "details",
2734
+ "dialog",
2735
+ "div",
2736
+ "dl",
2737
+ "fieldset",
2738
+ "figure",
2739
+ "footer",
2740
+ "form",
2741
+ "h1",
2742
+ "h2",
2743
+ "h3",
2744
+ "h4",
2745
+ "h5",
2746
+ "h6",
2747
+ "header",
2748
+ "hr",
2749
+ "main",
2750
+ "nav",
2751
+ "ol",
2752
+ "p",
2753
+ "pre",
2754
+ "section",
2755
+ "table",
2756
+ "ul"
2757
+ ];
2758
+ var pContract = closedContract([
2759
+ { name: "content", match: isOpenContent(...P_BLOCKED_TAGS) }
2760
+ ]);
2590
2761
  var mediaContract = contract([
2591
2762
  { name: "source", match: isTag("source") },
2592
2763
  { name: "track", match: isTag("track") },
@@ -2641,6 +2812,11 @@ var htmlContracts = {
2641
2812
  details: detailsContract,
2642
2813
  fieldset: fieldsetContract,
2643
2814
  dialog: dialogContract,
2815
+ object: objectContract,
2816
+ button: buttonContract,
2817
+ a: anchorContract,
2818
+ label: labelContract,
2819
+ p: pContract,
2644
2820
  head: headContract,
2645
2821
  html: htmlContract
2646
2822
  };