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.
package/dist/lit/index.js CHANGED
@@ -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) {
@@ -1056,8 +1060,132 @@ var InvariantBase = class {
1056
1060
  }
1057
1061
  };
1058
1062
 
1059
- // ../../lib/contract/src/aria/polymorphic-validator.ts
1063
+ // ../../lib/contract/src/aria/spec/roles/required-properties.ts
1064
+ var REQUIRED_ARIA_PROPERTIES = {
1065
+ combobox: ["aria-expanded"],
1066
+ option: ["aria-selected"],
1067
+ slider: ["aria-valuenow"],
1068
+ scrollbar: ["aria-controls", "aria-valuenow"],
1069
+ spinbutton: ["aria-valuenow"]
1070
+ };
1071
+
1072
+ // ../../lib/contract/src/aria/spec/roles/name-required.ts
1073
+ var NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["img"]);
1074
+
1075
+ // ../../lib/contract/src/aria/spec/validators/required-properties-validator.ts
1060
1076
  var NO_VIOLATIONS = [{ valid: true }];
1077
+ function requiredAttributeByRole(roles, attribute) {
1078
+ return Object.fromEntries([...roles].map((role) => [role, [attribute]]));
1079
+ }
1080
+ function checkRequiredAttributes(requirement, { props, effectiveRole }) {
1081
+ if (!effectiveRole) return NO_VIOLATIONS;
1082
+ const requiredAttributes = requirement.attributesByRole[effectiveRole];
1083
+ if (!requiredAttributes) return NO_VIOLATIONS;
1084
+ const results = [];
1085
+ for (const attribute of requiredAttributes) {
1086
+ if (attribute in props) continue;
1087
+ results.push({
1088
+ valid: false,
1089
+ fixable: false,
1090
+ severity: "warning",
1091
+ attribute,
1092
+ diagnostic: requirement.diagnosticFor(attribute, effectiveRole)
1093
+ });
1094
+ }
1095
+ return results;
1096
+ }
1097
+
1098
+ // ../../lib/contract/src/aria/spec/roles/live-region.ts
1099
+ var LIVE_REGION_ROLES = /* @__PURE__ */ new Map([
1100
+ ["alert", "assertive"],
1101
+ ["status", "polite"],
1102
+ ["log", "polite"],
1103
+ ["timer", "off"]
1104
+ ]);
1105
+ var ATOMIC_REQUIREMENTS = requiredAttributeByRole(LIVE_REGION_ROLES.keys(), "aria-atomic");
1106
+
1107
+ // ../../lib/contract/src/aria/spec/attributes/aria-value-types.ts
1108
+ var ARIA_VALUE_TYPES = /* @__PURE__ */ new Map([
1109
+ // Boolean (true | false)
1110
+ ["aria-atomic", { kind: "boolean" }],
1111
+ ["aria-busy", { kind: "boolean" }],
1112
+ ["aria-disabled", { kind: "boolean" }],
1113
+ ["aria-expanded", { kind: "boolean" }],
1114
+ ["aria-hidden", { kind: "boolean" }],
1115
+ ["aria-modal", { kind: "boolean" }],
1116
+ ["aria-multiline", { kind: "boolean" }],
1117
+ ["aria-multiselectable", { kind: "boolean" }],
1118
+ ["aria-readonly", { kind: "boolean" }],
1119
+ ["aria-required", { kind: "boolean" }],
1120
+ ["aria-selected", { kind: "boolean" }],
1121
+ // Tristate (true | false | mixed)
1122
+ ["aria-checked", { kind: "tristate" }],
1123
+ ["aria-pressed", { kind: "tristate" }],
1124
+ // Numeric (any finite number)
1125
+ ["aria-valuenow", { kind: "number" }],
1126
+ ["aria-valuemin", { kind: "number" }],
1127
+ ["aria-valuemax", { kind: "number" }],
1128
+ // Integer with optional range
1129
+ ["aria-level", { kind: "integer", min: 1, max: 6 }],
1130
+ ["aria-posinset", { kind: "integer", min: 1 }],
1131
+ ["aria-setsize", { kind: "integer", min: -1 }],
1132
+ ["aria-rowcount", { kind: "integer", min: -1 }],
1133
+ ["aria-colcount", { kind: "integer", min: -1 }],
1134
+ ["aria-rowindex", { kind: "integer", min: 1 }],
1135
+ ["aria-colindex", { kind: "integer", min: 1 }],
1136
+ ["aria-rowspan", { kind: "integer", min: 0 }],
1137
+ ["aria-colspan", { kind: "integer", min: 0 }],
1138
+ // Enum (specific allowed tokens)
1139
+ ["aria-autocomplete", { kind: "enum", values: /* @__PURE__ */ new Set(["inline", "list", "both", "none"]) }],
1140
+ [
1141
+ "aria-current",
1142
+ {
1143
+ kind: "enum",
1144
+ values: /* @__PURE__ */ new Set(["page", "step", "location", "date", "time", "true", "false"])
1145
+ }
1146
+ ],
1147
+ [
1148
+ "aria-haspopup",
1149
+ {
1150
+ kind: "enum",
1151
+ values: /* @__PURE__ */ new Set(["false", "true", "menu", "listbox", "tree", "grid", "dialog"])
1152
+ }
1153
+ ],
1154
+ ["aria-invalid", { kind: "enum", values: /* @__PURE__ */ new Set(["grammar", "false", "spelling", "true"]) }],
1155
+ ["aria-live", { kind: "enum", values: /* @__PURE__ */ new Set(["assertive", "off", "polite"]) }],
1156
+ ["aria-orientation", { kind: "enum", values: /* @__PURE__ */ new Set(["horizontal", "vertical", "undefined"]) }],
1157
+ ["aria-sort", { kind: "enum", values: /* @__PURE__ */ new Set(["ascending", "descending", "none", "other"]) }]
1158
+ ]);
1159
+
1160
+ // ../../lib/contract/src/aria/spec/attributes/aria-relevant-tokens.ts
1161
+ var VALID_RELEVANT_TOKENS = /* @__PURE__ */ new Set([
1162
+ "additions",
1163
+ "removals",
1164
+ "text",
1165
+ "all"
1166
+ ]);
1167
+
1168
+ // ../../lib/contract/src/aria/spec/elements/heading-implicit-levels.ts
1169
+ var HEADING_IMPLICIT_LEVELS = /* @__PURE__ */ new Map([
1170
+ ["h1", 1],
1171
+ ["h2", 2],
1172
+ ["h3", 3],
1173
+ ["h4", 4],
1174
+ ["h5", 5],
1175
+ ["h6", 6]
1176
+ ]);
1177
+
1178
+ // ../../lib/contract/src/aria/spec/elements/interactive-tags.ts
1179
+ var INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
1180
+ "a",
1181
+ "button",
1182
+ "input",
1183
+ "select",
1184
+ "textarea"
1185
+ ]);
1186
+
1187
+ // ../../lib/contract/src/aria/polymorphic-validator.ts
1188
+ var NO_VIOLATIONS2 = [{ valid: true }];
1061
1189
  function isIntrinsicTag(tag) {
1062
1190
  return isString(tag);
1063
1191
  }
@@ -1119,6 +1247,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1119
1247
  const violations = [];
1120
1248
  const fixes = [];
1121
1249
  iterate.forEach(rules, (rule) => {
1250
+ if (isNonNull(rule.tags) && !rule.tags.includes(context.tag)) return;
1122
1251
  iterate.forEach(rule(context), (result) => {
1123
1252
  if (result.valid) return;
1124
1253
  const {
@@ -1144,7 +1273,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1144
1273
  return { violations, fixes };
1145
1274
  }
1146
1275
  static #getRules(context) {
1147
- if (_AriaPolicyEngine.#hasRole(context.props) || isNonNull(context.effectiveRole) && _AriaPolicyEngine.#LIVE_REGION_ROLES.has(context.effectiveRole)) {
1276
+ if (_AriaPolicyEngine.#hasRole(context.props) || isNonNull(context.effectiveRole) && LIVE_REGION_ROLES.has(context.effectiveRole)) {
1148
1277
  return _AriaPolicyEngine.#pipeline;
1149
1278
  }
1150
1279
  return _AriaPolicyEngine.#implicitOnlyRules;
@@ -1346,7 +1475,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1346
1475
  implicitRole
1347
1476
  }) {
1348
1477
  const role = props.role;
1349
- if (!implicitRole || !role || role === implicitRole) return NO_VIOLATIONS;
1478
+ if (!implicitRole || !role || role === implicitRole) return NO_VIOLATIONS2;
1350
1479
  if (isStrongImplicitRole(tag) && role === "region") {
1351
1480
  const diagnostic = HtmlDiagnostics.implicitRoleOverride(tag, implicitRole, role);
1352
1481
  return [
@@ -1359,11 +1488,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1359
1488
  }
1360
1489
  ];
1361
1490
  }
1362
- return NO_VIOLATIONS;
1491
+ return NO_VIOLATIONS2;
1363
1492
  }
1364
1493
  static #checkRedundantRole({ tag, props, implicitRole }) {
1365
1494
  const role = props.role;
1366
- if (!implicitRole || !role || role !== implicitRole) return NO_VIOLATIONS;
1495
+ if (!implicitRole || !role || role !== implicitRole) return NO_VIOLATIONS2;
1367
1496
  const diagnostic = HtmlDiagnostics.implicitRoleRedundant(tag, implicitRole);
1368
1497
  return [
1369
1498
  {
@@ -1377,8 +1506,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1377
1506
  }
1378
1507
  static #checkStandaloneRegion({ tag, props, implicitRole }) {
1379
1508
  const role = props.role;
1380
- if (role !== "region") return NO_VIOLATIONS;
1381
- if (!isStandaloneTag(tag)) return NO_VIOLATIONS;
1509
+ if (role !== "region") return NO_VIOLATIONS2;
1510
+ if (!isStandaloneTag(tag)) return NO_VIOLATIONS2;
1382
1511
  const diagnostic = HtmlDiagnostics.standaloneRegionOverride(tag, implicitRole ?? tag);
1383
1512
  return [
1384
1513
  {
@@ -1395,7 +1524,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1395
1524
  props,
1396
1525
  effectiveRole
1397
1526
  }) {
1398
- if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS;
1527
+ if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
1399
1528
  const results = [];
1400
1529
  iterate.forEachEntry(props, (key) => {
1401
1530
  if (!key.startsWith("aria-")) return;
@@ -1413,62 +1542,6 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1413
1542
  return results;
1414
1543
  }
1415
1544
  // ─── ARIA attribute value validation ──────────────────────────────────────
1416
- // Accepted value shapes for typed ARIA attributes.
1417
- // Attributes not in this map are unconstrained (arbitrary string values permitted).
1418
- static #ARIA_VALUE_TYPES = /* @__PURE__ */ new Map([
1419
- // Boolean (true | false)
1420
- ["aria-atomic", { kind: "boolean" }],
1421
- ["aria-busy", { kind: "boolean" }],
1422
- ["aria-disabled", { kind: "boolean" }],
1423
- ["aria-expanded", { kind: "boolean" }],
1424
- ["aria-hidden", { kind: "boolean" }],
1425
- ["aria-modal", { kind: "boolean" }],
1426
- ["aria-multiline", { kind: "boolean" }],
1427
- ["aria-multiselectable", { kind: "boolean" }],
1428
- ["aria-readonly", { kind: "boolean" }],
1429
- ["aria-required", { kind: "boolean" }],
1430
- ["aria-selected", { kind: "boolean" }],
1431
- // Tristate (true | false | mixed)
1432
- ["aria-checked", { kind: "tristate" }],
1433
- ["aria-pressed", { kind: "tristate" }],
1434
- // Numeric (any finite number)
1435
- ["aria-valuenow", { kind: "number" }],
1436
- ["aria-valuemin", { kind: "number" }],
1437
- ["aria-valuemax", { kind: "number" }],
1438
- // Integer with optional range
1439
- ["aria-level", { kind: "integer", min: 1, max: 6 }],
1440
- ["aria-posinset", { kind: "integer", min: 1 }],
1441
- ["aria-setsize", { kind: "integer", min: -1 }],
1442
- ["aria-rowcount", { kind: "integer", min: -1 }],
1443
- ["aria-colcount", { kind: "integer", min: -1 }],
1444
- ["aria-rowindex", { kind: "integer", min: 1 }],
1445
- ["aria-colindex", { kind: "integer", min: 1 }],
1446
- ["aria-rowspan", { kind: "integer", min: 0 }],
1447
- ["aria-colspan", { kind: "integer", min: 0 }],
1448
- // Enum (specific allowed tokens)
1449
- ["aria-autocomplete", { kind: "enum", values: /* @__PURE__ */ new Set(["inline", "list", "both", "none"]) }],
1450
- [
1451
- "aria-current",
1452
- {
1453
- kind: "enum",
1454
- values: /* @__PURE__ */ new Set(["page", "step", "location", "date", "time", "true", "false"])
1455
- }
1456
- ],
1457
- [
1458
- "aria-haspopup",
1459
- {
1460
- kind: "enum",
1461
- values: /* @__PURE__ */ new Set(["false", "true", "menu", "listbox", "tree", "grid", "dialog"])
1462
- }
1463
- ],
1464
- ["aria-invalid", { kind: "enum", values: /* @__PURE__ */ new Set(["grammar", "false", "spelling", "true"]) }],
1465
- ["aria-live", { kind: "enum", values: /* @__PURE__ */ new Set(["assertive", "off", "polite"]) }],
1466
- [
1467
- "aria-orientation",
1468
- { kind: "enum", values: /* @__PURE__ */ new Set(["horizontal", "vertical", "undefined"]) }
1469
- ],
1470
- ["aria-sort", { kind: "enum", values: /* @__PURE__ */ new Set(["ascending", "descending", "none", "other"]) }]
1471
- ]);
1472
1545
  static #isValidAriaValue(value, type) {
1473
1546
  switch (type.kind) {
1474
1547
  case "boolean":
@@ -1513,11 +1586,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1513
1586
  }
1514
1587
  }
1515
1588
  static #checkAriaAttributeValues({ props, effectiveRole }) {
1516
- if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS;
1589
+ if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
1517
1590
  const results = [];
1518
1591
  iterate.forEachEntry(props, (key, value) => {
1519
1592
  if (!key.startsWith("aria-")) return;
1520
- const type = _AriaPolicyEngine.#ARIA_VALUE_TYPES.get(key);
1593
+ const type = ARIA_VALUE_TYPES.get(key);
1521
1594
  if (!isNonNull(type)) return;
1522
1595
  if (_AriaPolicyEngine.#isValidAriaValue(value, type)) return;
1523
1596
  results.push({
@@ -1536,26 +1609,18 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1536
1609
  return results;
1537
1610
  }
1538
1611
  // ─── Heading implicit level ────────────────────────────────────────────────
1539
- static #HEADING_IMPLICIT_LEVELS = /* @__PURE__ */ new Map([
1540
- ["h1", 1],
1541
- ["h2", 2],
1542
- ["h3", 3],
1543
- ["h4", 4],
1544
- ["h5", 5],
1545
- ["h6", 6]
1546
- ]);
1547
1612
  static #checkRedundantAriaLevel({
1548
1613
  tag,
1549
1614
  props,
1550
1615
  effectiveRole
1551
1616
  }) {
1552
- if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS;
1553
- const implicitLevel = _AriaPolicyEngine.#HEADING_IMPLICIT_LEVELS.get(tag);
1554
- if (!isNonNull(implicitLevel)) return NO_VIOLATIONS;
1617
+ if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS2;
1618
+ const implicitLevel = HEADING_IMPLICIT_LEVELS.get(tag);
1619
+ if (!isNonNull(implicitLevel)) return NO_VIOLATIONS2;
1555
1620
  const raw = props["aria-level"];
1556
- if (!isNonNull(raw)) return NO_VIOLATIONS;
1621
+ if (!isNonNull(raw)) return NO_VIOLATIONS2;
1557
1622
  const n = typeof raw === "number" ? raw : typeof raw === "string" ? parseInt(raw, 10) : NaN;
1558
- if (!Number.isFinite(n) || n !== implicitLevel) return NO_VIOLATIONS;
1623
+ if (!Number.isFinite(n) || n !== implicitLevel) return NO_VIOLATIONS2;
1559
1624
  return [
1560
1625
  {
1561
1626
  valid: false,
@@ -1568,20 +1633,14 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1568
1633
  ];
1569
1634
  }
1570
1635
  // ─── Name-required roles ───────────────────────────────────────────────────
1571
- // Roles that always require an accessible name per WAI-ARIA APG.
1572
- // Dialog and landmark names are enforced via contracts (ariaContract) rather than
1573
- // the built-in pipeline so consumers can opt in; img is built in because role=img
1574
- // on any element (including bare <img>) is definitionally useless without a name.
1575
- static #NAME_REQUIRED_ROLES = /* @__PURE__ */ new Set(["img"]);
1576
1636
  static #checkNameRequiredRoles({
1577
1637
  tag,
1578
1638
  props,
1579
1639
  effectiveRole
1580
1640
  }) {
1581
- if (!effectiveRole || !_AriaPolicyEngine.#NAME_REQUIRED_ROLES.has(effectiveRole))
1582
- return NO_VIOLATIONS;
1583
- if ("aria-label" in props || "aria-labelledby" in props) return NO_VIOLATIONS;
1584
- if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return NO_VIOLATIONS;
1641
+ if (!effectiveRole || !NAME_REQUIRED_ROLES.has(effectiveRole)) return NO_VIOLATIONS2;
1642
+ if ("aria-label" in props || "aria-labelledby" in props) return NO_VIOLATIONS2;
1643
+ if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return NO_VIOLATIONS2;
1585
1644
  return [
1586
1645
  {
1587
1646
  valid: false,
@@ -1591,51 +1650,21 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1591
1650
  }
1592
1651
  ];
1593
1652
  }
1594
- // WAI-ARIA 1.2 required states and properties, keyed by role.
1595
- // Source: https://www.w3.org/TR/wai-aria-1.2/#requiredState
1596
- static #REQUIRED_PROPERTIES = /* @__PURE__ */ new Map([
1597
- ["combobox", ["aria-expanded"]],
1598
- ["option", ["aria-selected"]],
1599
- ["slider", ["aria-valuenow"]],
1600
- ["scrollbar", ["aria-controls", "aria-valuenow"]],
1601
- ["spinbutton", ["aria-valuenow"]]
1602
- ]);
1603
- static #checkRequiredAriaProperties({
1604
- props,
1605
- effectiveRole
1606
- }) {
1607
- if (!effectiveRole) return NO_VIOLATIONS;
1608
- const required = _AriaPolicyEngine.#REQUIRED_PROPERTIES.get(effectiveRole);
1609
- if (!isNonNull(required)) return NO_VIOLATIONS;
1610
- const results = [];
1611
- iterate.forEach(required, (attr) => {
1612
- if (attr in props) return;
1613
- results.push({
1614
- valid: false,
1615
- fixable: false,
1616
- severity: "warning",
1617
- attribute: attr,
1618
- diagnostic: AriaDiagnostics.requiredProperty(attr, effectiveRole)
1619
- });
1620
- });
1621
- return results;
1653
+ static #requiredAriaPropertiesRule = {
1654
+ attributesByRole: REQUIRED_ARIA_PROPERTIES,
1655
+ diagnosticFor: (attribute, role) => AriaDiagnostics.requiredProperty(attribute, role)
1656
+ };
1657
+ static #checkRequiredAriaProperties(context) {
1658
+ return checkRequiredAttributes(_AriaPolicyEngine.#requiredAriaPropertiesRule, context);
1622
1659
  }
1623
- // Natively interactive HTML elements — always keyboard-reachable unless explicitly disabled.
1624
- static #INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
1625
- "a",
1626
- "button",
1627
- "input",
1628
- "select",
1629
- "textarea"
1630
- ]);
1631
1660
  // WAI-ARIA 1.2 §6.6: aria-hidden="true" must not be placed on focusable elements.
1632
1661
  static #checkAriaHiddenOnFocusable({ tag, props }) {
1633
- if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return NO_VIOLATIONS;
1634
- const isInteractive = _AriaPolicyEngine.#INTERACTIVE_TAGS.has(tag);
1662
+ if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return NO_VIOLATIONS2;
1663
+ const isInteractive = INTERACTIVE_TAGS.has(tag);
1635
1664
  if (!isInteractive) {
1636
1665
  const tabindex = props.tabindex;
1637
1666
  const n = typeof tabindex === "number" ? tabindex : typeof tabindex === "string" ? parseInt(tabindex, 10) : NaN;
1638
- if (!Number.isFinite(n) || n < 0) return NO_VIOLATIONS;
1667
+ if (!Number.isFinite(n) || n < 0) return NO_VIOLATIONS2;
1639
1668
  }
1640
1669
  return [
1641
1670
  {
@@ -1654,7 +1683,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1654
1683
  props,
1655
1684
  effectiveRole
1656
1685
  }) {
1657
- if (effectiveRole !== "none" && effectiveRole !== "presentation") return NO_VIOLATIONS;
1686
+ if (effectiveRole !== "none" && effectiveRole !== "presentation") return NO_VIOLATIONS2;
1658
1687
  const results = [];
1659
1688
  iterate.forEachEntry(props, (key) => {
1660
1689
  if (!key.startsWith("aria-")) return;
@@ -1670,18 +1699,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1670
1699
  });
1671
1700
  return results;
1672
1701
  }
1673
- // WAI-ARIA live region roles and their implied aria-live politeness values.
1674
- static #LIVE_REGION_ROLES = /* @__PURE__ */ new Map([
1675
- ["alert", "assertive"],
1676
- ["status", "polite"],
1677
- ["log", "polite"],
1678
- ["timer", "off"]
1679
- ]);
1680
1702
  static #checkMissingLiveRegion({ effectiveRole, props }) {
1681
- if (!effectiveRole) return NO_VIOLATIONS;
1682
- const impliedLive = _AriaPolicyEngine.#LIVE_REGION_ROLES.get(effectiveRole);
1683
- if (!impliedLive) return NO_VIOLATIONS;
1684
- if ("aria-live" in props) return NO_VIOLATIONS;
1703
+ if (!effectiveRole) return NO_VIOLATIONS2;
1704
+ const impliedLive = LIVE_REGION_ROLES.get(effectiveRole);
1705
+ if (!impliedLive) return NO_VIOLATIONS2;
1706
+ if ("aria-live" in props) return NO_VIOLATIONS2;
1685
1707
  const injectLive = {
1686
1708
  kind: `injectLive:${effectiveRole}`,
1687
1709
  apply: (ctx) => ({
@@ -1700,20 +1722,13 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1700
1722
  }
1701
1723
  ];
1702
1724
  }
1703
- static #checkMissingAtomic({ effectiveRole, props }) {
1704
- if (!effectiveRole || !_AriaPolicyEngine.#LIVE_REGION_ROLES.has(effectiveRole))
1705
- return NO_VIOLATIONS;
1706
- if ("aria-atomic" in props) return NO_VIOLATIONS;
1707
- return [
1708
- {
1709
- valid: false,
1710
- fixable: false,
1711
- severity: "warning",
1712
- diagnostic: AriaDiagnostics.missingAtomic(effectiveRole)
1713
- }
1714
- ];
1725
+ static #missingAtomicRule = {
1726
+ attributesByRole: ATOMIC_REQUIREMENTS,
1727
+ diagnosticFor: (_attribute, role) => AriaDiagnostics.missingAtomic(role)
1728
+ };
1729
+ static #checkMissingAtomic(context) {
1730
+ return checkRequiredAttributes(_AriaPolicyEngine.#missingAtomicRule, context);
1715
1731
  }
1716
- static #VALID_RELEVANT_TOKENS = /* @__PURE__ */ new Set(["additions", "removals", "text", "all"]);
1717
1732
  // Custom fix rules passed via `options.rules` must be pure functions of (tag, props) — the cache
1718
1733
  // replays stored fixes against new prop objects, so fixes that close over external state will
1719
1734
  // produce inconsistent results on cache hits.
@@ -1727,10 +1742,10 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1727
1742
  };
1728
1743
  static #checkInvalidAriaRelevant({ props }) {
1729
1744
  const relevant = props["aria-relevant"];
1730
- if (relevant === void 0) return NO_VIOLATIONS;
1731
- if (typeof relevant !== "string") return NO_VIOLATIONS;
1745
+ if (relevant === void 0) return NO_VIOLATIONS2;
1746
+ if (typeof relevant !== "string") return NO_VIOLATIONS2;
1732
1747
  const tokens = relevant.trim().split(/\s+/);
1733
- const invalid = tokens.filter((t) => !_AriaPolicyEngine.#VALID_RELEVANT_TOKENS.has(t));
1748
+ const invalid = tokens.filter((t) => !VALID_RELEVANT_TOKENS.has(t));
1734
1749
  if (invalid.length > 0) {
1735
1750
  return [
1736
1751
  {
@@ -1755,7 +1770,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1755
1770
  }
1756
1771
  ];
1757
1772
  }
1758
- return NO_VIOLATIONS;
1773
+ return NO_VIOLATIONS2;
1759
1774
  }
1760
1775
  };
1761
1776
 
@@ -2075,7 +2090,59 @@ var readonlyProps = ({
2075
2090
  // ../core/src/html/evaluators.ts
2076
2091
  import { warnDiagnostics as warnDiagnostics2 } from "../_shared/diagnostics.js";
2077
2092
 
2078
- // ../core/src/html/input-rules.ts
2093
+ // ../core/src/html/spec/vocabulary/input.ts
2094
+ var TEXT_INPUT_TYPES = ["text", "search", "url", "tel", "email", "password"];
2095
+ var NUMERIC_INPUT_TYPES = [
2096
+ "number",
2097
+ "range",
2098
+ "date",
2099
+ "month",
2100
+ "week",
2101
+ "time",
2102
+ "datetime-local"
2103
+ ];
2104
+ var HTML_INPUT_TYPES = /* @__PURE__ */ new Set([
2105
+ ...TEXT_INPUT_TYPES,
2106
+ ...NUMERIC_INPUT_TYPES,
2107
+ "checkbox",
2108
+ "radio",
2109
+ "file",
2110
+ "color",
2111
+ "hidden",
2112
+ "button",
2113
+ "submit",
2114
+ "reset",
2115
+ "image"
2116
+ ]);
2117
+
2118
+ // ../core/src/html/spec/attributes/input.ts
2119
+ var INPUT_ATTRIBUTE_TYPE_POLICIES = [
2120
+ { attribute: "checked", allowedTypes: ["checkbox", "radio"] },
2121
+ { attribute: "multiple", allowedTypes: ["email", "file"] },
2122
+ { attribute: "maxLength", allowedTypes: TEXT_INPUT_TYPES },
2123
+ { attribute: "minLength", allowedTypes: TEXT_INPUT_TYPES },
2124
+ { attribute: "pattern", allowedTypes: TEXT_INPUT_TYPES },
2125
+ { attribute: "min", allowedTypes: NUMERIC_INPUT_TYPES },
2126
+ { attribute: "max", allowedTypes: NUMERIC_INPUT_TYPES },
2127
+ { attribute: "step", allowedTypes: NUMERIC_INPUT_TYPES },
2128
+ { attribute: "accept", allowedTypes: ["file"] },
2129
+ { attribute: "capture", allowedTypes: ["file"] },
2130
+ { attribute: "size", allowedTypes: TEXT_INPUT_TYPES },
2131
+ { attribute: "alt", allowedTypes: ["image"] },
2132
+ { attribute: "height", allowedTypes: ["image"] },
2133
+ { attribute: "width", allowedTypes: ["image"] }
2134
+ ];
2135
+
2136
+ // ../core/src/html/spec/constraints/input.ts
2137
+ var REQUIRED_READONLY_CONFLICT = {
2138
+ props: ["required", "readOnly"],
2139
+ diagnostic: () => InputAccessibilityDiagnostics.requiredReadOnlyConflict()
2140
+ };
2141
+ var INPUT_MUTUALLY_EXCLUSIVE_POLICIES = [
2142
+ REQUIRED_READONLY_CONFLICT
2143
+ ];
2144
+
2145
+ // ../core/src/html/spec/validators/attribute-type-validator.ts
2079
2146
  var DEFAULT_INPUT_TYPE = "text";
2080
2147
  function omit(props, key) {
2081
2148
  const next = { ...props };
@@ -2091,7 +2158,10 @@ function removeAttributeFix(attribute) {
2091
2158
  }
2092
2159
  };
2093
2160
  }
2094
- function inputAttributeRequiresType(attribute, allowedTypes) {
2161
+ function createInputAttributeTypeRule({
2162
+ attribute,
2163
+ allowedTypes
2164
+ }) {
2095
2165
  const rule = ({ tag, props }) => {
2096
2166
  if (tag !== "input" || !(attribute in props)) return [];
2097
2167
  const type = typeof props.type === "string" ? props.type : DEFAULT_INPUT_TYPE;
@@ -2107,31 +2177,30 @@ function inputAttributeRequiresType(attribute, allowedTypes) {
2107
2177
  }
2108
2178
  ];
2109
2179
  };
2110
- return Object.assign(rule, { readsProps: ["type", attribute] });
2180
+ return Object.assign(rule, { readsProps: ["type", attribute], tags: ["input"] });
2181
+ }
2182
+
2183
+ // ../core/src/html/spec/validators/mutually-exclusive-validator.ts
2184
+ function createMutuallyExclusiveRule({
2185
+ props: conflictingProps,
2186
+ diagnostic: createDiagnostic
2187
+ }) {
2188
+ const [first, second] = conflictingProps;
2189
+ const rule = ({ tag, props }) => {
2190
+ if (tag !== "input" || !props[first] || !props[second]) return [];
2191
+ const diagnostic = createDiagnostic();
2192
+ return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
2193
+ };
2194
+ return Object.assign(rule, { readsProps: conflictingProps, tags: ["input"] });
2195
+ }
2196
+
2197
+ // ../core/src/html/input-rules.ts
2198
+ var policyByAttribute = Object.fromEntries(
2199
+ INPUT_ATTRIBUTE_TYPE_POLICIES.map((policy) => [policy.attribute, policy])
2200
+ );
2201
+ function policyFor(attribute) {
2202
+ return policyByAttribute[attribute];
2111
2203
  }
2112
- var TEXT_INPUT_TYPES = ["text", "search", "url", "tel", "email", "password"];
2113
- var NUMERIC_INPUT_TYPES = [
2114
- "number",
2115
- "range",
2116
- "date",
2117
- "month",
2118
- "week",
2119
- "time",
2120
- "datetime-local"
2121
- ];
2122
- var HTML_INPUT_TYPES = /* @__PURE__ */ new Set([
2123
- ...TEXT_INPUT_TYPES,
2124
- ...NUMERIC_INPUT_TYPES,
2125
- "checkbox",
2126
- "radio",
2127
- "file",
2128
- "color",
2129
- "hidden",
2130
- "button",
2131
- "submit",
2132
- "reset",
2133
- "image"
2134
- ]);
2135
2204
  var supportedInputTypeRule = Object.assign(
2136
2205
  ({ tag, props }) => {
2137
2206
  if (tag !== "input" || typeof props.type !== "string") return [];
@@ -2147,30 +2216,22 @@ var supportedInputTypeRule = Object.assign(
2147
2216
  }
2148
2217
  ];
2149
2218
  },
2150
- { readsProps: ["type"] }
2151
- );
2152
- var checkedRequiresCheckableTypeRule = inputAttributeRequiresType("checked", [
2153
- "checkbox",
2154
- "radio"
2155
- ]);
2156
- var multipleRequiresSupportedTypeRule = inputAttributeRequiresType("multiple", [
2157
- "email",
2158
- "file"
2159
- ]);
2160
- var maxLengthRequiresTextTypeRule = inputAttributeRequiresType(
2161
- "maxLength",
2162
- TEXT_INPUT_TYPES
2219
+ { readsProps: ["type"], tags: ["input"] }
2163
2220
  );
2164
- var minLengthRequiresTextTypeRule = inputAttributeRequiresType(
2165
- "minLength",
2166
- TEXT_INPUT_TYPES
2167
- );
2168
- var patternRequiresTextTypeRule = inputAttributeRequiresType("pattern", TEXT_INPUT_TYPES);
2169
- var minRequiresNumericTypeRule = inputAttributeRequiresType("min", NUMERIC_INPUT_TYPES);
2170
- var maxRequiresNumericTypeRule = inputAttributeRequiresType("max", NUMERIC_INPUT_TYPES);
2171
- var stepRequiresNumericTypeRule = inputAttributeRequiresType("step", NUMERIC_INPUT_TYPES);
2172
- var acceptRequiresFileTypeRule = inputAttributeRequiresType("accept", ["file"]);
2173
- var captureRequiresFileTypeRule = inputAttributeRequiresType("capture", ["file"]);
2221
+ var checkedRequiresCheckableTypeRule = createInputAttributeTypeRule(policyFor("checked"));
2222
+ var multipleRequiresSupportedTypeRule = createInputAttributeTypeRule(policyFor("multiple"));
2223
+ var maxLengthRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("maxLength"));
2224
+ var minLengthRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("minLength"));
2225
+ var patternRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("pattern"));
2226
+ var minRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("min"));
2227
+ var maxRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("max"));
2228
+ var stepRequiresNumericTypeRule = createInputAttributeTypeRule(policyFor("step"));
2229
+ var acceptRequiresFileTypeRule = createInputAttributeTypeRule(policyFor("accept"));
2230
+ var captureRequiresFileTypeRule = createInputAttributeTypeRule(policyFor("capture"));
2231
+ var sizeRequiresTextTypeRule = createInputAttributeTypeRule(policyFor("size"));
2232
+ var altRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("alt"));
2233
+ var heightRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("height"));
2234
+ var widthRequiresImageTypeRule = createInputAttributeTypeRule(policyFor("width"));
2174
2235
  var inputAccessibleNameRule = Object.assign(
2175
2236
  ({ tag, props }) => {
2176
2237
  if (tag !== "input" || props.type === "hidden") return [];
@@ -2179,7 +2240,10 @@ var inputAccessibleNameRule = Object.assign(
2179
2240
  const diagnostic = hasPlaceholder ? InputAccessibilityDiagnostics.placeholderIsNotLabel() : InputAccessibilityDiagnostics.missingAccessibleName();
2180
2241
  return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
2181
2242
  },
2182
- { readsProps: ["type", "aria-label", "aria-labelledby", "placeholder"] }
2243
+ {
2244
+ readsProps: ["type", "aria-label", "aria-labelledby", "placeholder"],
2245
+ tags: ["input"]
2246
+ }
2183
2247
  );
2184
2248
  var PASSWORD_AUTOCOMPLETE_VALUES = ["current-password", "new-password"];
2185
2249
  var passwordAutocompleteRule = Object.assign(
@@ -2191,16 +2255,9 @@ var passwordAutocompleteRule = Object.assign(
2191
2255
  const diagnostic = InputAccessibilityDiagnostics.passwordMissingAutocomplete();
2192
2256
  return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
2193
2257
  },
2194
- { readsProps: ["type", "autoComplete"] }
2195
- );
2196
- var requiredReadOnlyConflictRule = Object.assign(
2197
- ({ tag, props }) => {
2198
- if (tag !== "input" || !props.required || !props.readOnly) return [];
2199
- const diagnostic = InputAccessibilityDiagnostics.requiredReadOnlyConflict();
2200
- return [{ valid: false, fixable: false, severity: diagnostic.severity, diagnostic }];
2201
- },
2202
- { readsProps: ["required", "readOnly"] }
2258
+ { readsProps: ["type", "autoComplete"], tags: ["input"] }
2203
2259
  );
2260
+ var requiredReadOnlyConflictRule = createMutuallyExclusiveRule(REQUIRED_READONLY_CONFLICT);
2204
2261
  var INPUT_RULES = [
2205
2262
  supportedInputTypeRule,
2206
2263
  checkedRequiresCheckableTypeRule,
@@ -2213,11 +2270,132 @@ var INPUT_RULES = [
2213
2270
  stepRequiresNumericTypeRule,
2214
2271
  acceptRequiresFileTypeRule,
2215
2272
  captureRequiresFileTypeRule,
2273
+ sizeRequiresTextTypeRule,
2274
+ altRequiresImageTypeRule,
2275
+ heightRequiresImageTypeRule,
2276
+ widthRequiresImageTypeRule,
2216
2277
  inputAccessibleNameRule,
2217
2278
  passwordAutocompleteRule,
2218
2279
  requiredReadOnlyConflictRule
2219
2280
  ];
2220
2281
 
2282
+ // ../core/src/html/spec/types.ts
2283
+ function definePropRolePolicy(prop, map2, fallback) {
2284
+ return { kind: "byProp", prop, map: map2, fallback };
2285
+ }
2286
+ function resolveAllowedRoles(spec, props) {
2287
+ const policy = spec.allowedRoles;
2288
+ if (!policy) return void 0;
2289
+ switch (policy.kind) {
2290
+ case "fixed":
2291
+ return policy.roles;
2292
+ case "byProp": {
2293
+ const value = typeof props[policy.prop] === "string" ? props[policy.prop] : policy.fallback;
2294
+ return policy.map[value];
2295
+ }
2296
+ case "dynamic":
2297
+ return policy.resolve({ props });
2298
+ }
2299
+ }
2300
+
2301
+ // ../core/src/html/spec/roles/input.ts
2302
+ var ALLOWED_INPUT_ROLES = {
2303
+ checkbox: ["menuitemcheckbox", "option", "switch", "button"],
2304
+ radio: ["menuitemradio"],
2305
+ range: [],
2306
+ number: [],
2307
+ search: ["combobox"],
2308
+ text: ["combobox", "searchbox", "spinbutton"],
2309
+ email: ["combobox"],
2310
+ tel: ["combobox"],
2311
+ url: ["combobox"],
2312
+ button: [
2313
+ "link",
2314
+ "menuitem",
2315
+ "menuitemcheckbox",
2316
+ "menuitemradio",
2317
+ "option",
2318
+ "radio",
2319
+ "switch",
2320
+ "tab"
2321
+ ],
2322
+ submit: [
2323
+ "link",
2324
+ "menuitem",
2325
+ "menuitemcheckbox",
2326
+ "menuitemradio",
2327
+ "option",
2328
+ "radio",
2329
+ "switch",
2330
+ "tab"
2331
+ ],
2332
+ reset: [
2333
+ "link",
2334
+ "menuitem",
2335
+ "menuitemcheckbox",
2336
+ "menuitemradio",
2337
+ "option",
2338
+ "radio",
2339
+ "switch",
2340
+ "tab"
2341
+ ],
2342
+ image: [
2343
+ "link",
2344
+ "menuitem",
2345
+ "menuitemcheckbox",
2346
+ "menuitemradio",
2347
+ "option",
2348
+ "radio",
2349
+ "switch",
2350
+ "tab"
2351
+ ],
2352
+ hidden: []
2353
+ };
2354
+
2355
+ // ../core/src/html/spec/elements/input.ts
2356
+ var inputElementSpec = {
2357
+ tag: "input",
2358
+ allowedRoles: definePropRolePolicy("type", ALLOWED_INPUT_ROLES, "text"),
2359
+ attributes: INPUT_ATTRIBUTE_TYPE_POLICIES,
2360
+ mutuallyExclusive: INPUT_MUTUALLY_EXCLUSIVE_POLICIES
2361
+ };
2362
+
2363
+ // ../core/src/html/spec/roles/img.ts
2364
+ var IMG_NAMED_ROLES = [
2365
+ "button",
2366
+ "checkbox",
2367
+ "link",
2368
+ "menuitem",
2369
+ "menuitemcheckbox",
2370
+ "menuitemradio",
2371
+ "option",
2372
+ "progressbar",
2373
+ "scrollbar",
2374
+ "separator",
2375
+ "slider",
2376
+ "switch",
2377
+ "tab",
2378
+ "treeitem"
2379
+ ];
2380
+
2381
+ // ../core/src/html/spec/elements/img.ts
2382
+ var imgElementSpec = {
2383
+ tag: "img",
2384
+ allowedRoles: {
2385
+ kind: "dynamic",
2386
+ resolve: ({ props }) => props.alt === "" ? [] : IMG_NAMED_ROLES
2387
+ }
2388
+ };
2389
+
2390
+ // ../core/src/html/spec/roles/table.ts
2391
+ var ALLOWED_TABLE_ROLES = ["grid", "treegrid"];
2392
+
2393
+ // ../core/src/html/spec/elements/table.ts
2394
+ var tableElementSpec = {
2395
+ tag: "table",
2396
+ allowedRoles: { kind: "fixed", roles: ALLOWED_TABLE_ROLES }
2397
+ };
2398
+
2221
2399
  // ../core/src/html/role-restrictions.ts
2222
2400
  var ALLOWED_ROLES = {
2223
2401
  article: ["application", "document", "feed", "main", "none", "presentation", "region"],
@@ -2290,86 +2468,17 @@ var ALLOWED_ROLES = {
2290
2468
  "tab",
2291
2469
  "treeitem"
2292
2470
  ],
2293
- table: ["grid", "treegrid"],
2294
2471
  dialog: ["alertdialog"],
2295
2472
  fieldset: ["none", "presentation", "radiogroup"]
2296
2473
  };
2297
- var IMG_NAMED_ROLES = [
2298
- "button",
2299
- "checkbox",
2300
- "link",
2301
- "menuitem",
2302
- "menuitemcheckbox",
2303
- "menuitemradio",
2304
- "option",
2305
- "progressbar",
2306
- "scrollbar",
2307
- "separator",
2308
- "slider",
2309
- "switch",
2310
- "tab",
2311
- "treeitem"
2312
- ];
2313
- var ALLOWED_INPUT_ROLES = {
2314
- checkbox: ["menuitemcheckbox", "option", "switch", "button"],
2315
- radio: ["menuitemradio"],
2316
- range: [],
2317
- number: [],
2318
- search: ["combobox"],
2319
- text: ["combobox", "searchbox", "spinbutton"],
2320
- email: ["combobox"],
2321
- tel: ["combobox"],
2322
- url: ["combobox"],
2323
- button: [
2324
- "link",
2325
- "menuitem",
2326
- "menuitemcheckbox",
2327
- "menuitemradio",
2328
- "option",
2329
- "radio",
2330
- "switch",
2331
- "tab"
2332
- ],
2333
- submit: [
2334
- "link",
2335
- "menuitem",
2336
- "menuitemcheckbox",
2337
- "menuitemradio",
2338
- "option",
2339
- "radio",
2340
- "switch",
2341
- "tab"
2342
- ],
2343
- reset: [
2344
- "link",
2345
- "menuitem",
2346
- "menuitemcheckbox",
2347
- "menuitemradio",
2348
- "option",
2349
- "radio",
2350
- "switch",
2351
- "tab"
2352
- ],
2353
- image: [
2354
- "link",
2355
- "menuitem",
2356
- "menuitemcheckbox",
2357
- "menuitemradio",
2358
- "option",
2359
- "radio",
2360
- "switch",
2361
- "tab"
2362
- ],
2363
- hidden: []
2474
+ var ELEMENT_SPECS = {
2475
+ input: inputElementSpec,
2476
+ img: imgElementSpec,
2477
+ table: tableElementSpec
2364
2478
  };
2365
2479
  function getAllowedRoles(tag, props) {
2366
- if (tag === "input") {
2367
- const type = typeof props.type === "string" ? props.type : "text";
2368
- return ALLOWED_INPUT_ROLES[type];
2369
- }
2370
- if (tag === "img") {
2371
- return props.alt === "" ? [] : IMG_NAMED_ROLES;
2372
- }
2480
+ const spec = ELEMENT_SPECS[tag];
2481
+ if (spec) return resolveAllowedRoles(spec, props);
2373
2482
  return ALLOWED_ROLES[tag];
2374
2483
  }
2375
2484
  var removeRoleFix = {
@@ -2410,21 +2519,24 @@ var removeLandmarkRoleOverride = {
2410
2519
  return { applied: true, next: rest, previous: props };
2411
2520
  }
2412
2521
  };
2413
- function landmarkRoleRule({ tag, props, implicitRole }) {
2414
- if (!LANDMARK_TAG_SET.has(tag) || !implicitRole) return [];
2415
- const role = props.role;
2416
- if (!role || role === implicitRole) return [];
2417
- const diagnostic = HtmlDiagnostics.landmarkRoleOverride(tag, implicitRole, role);
2418
- return [
2419
- {
2420
- valid: false,
2421
- fixable: true,
2422
- severity: diagnostic.severity,
2423
- fix: removeLandmarkRoleOverride,
2424
- diagnostic
2425
- }
2426
- ];
2427
- }
2522
+ var landmarkRoleRule = Object.assign(
2523
+ ({ tag, props, implicitRole }) => {
2524
+ if (!LANDMARK_TAG_SET.has(tag) || !implicitRole) return [];
2525
+ const role = props.role;
2526
+ if (!role || role === implicitRole) return [];
2527
+ const diagnostic = HtmlDiagnostics.landmarkRoleOverride(tag, implicitRole, role);
2528
+ return [
2529
+ {
2530
+ valid: false,
2531
+ fixable: true,
2532
+ severity: diagnostic.severity,
2533
+ fix: removeLandmarkRoleOverride,
2534
+ diagnostic
2535
+ }
2536
+ ];
2537
+ },
2538
+ { tags: [...LANDMARK_TAG_SET] }
2539
+ );
2428
2540
  function requireAccessibleName({ tag, props }) {
2429
2541
  if ("aria-label" in props || "aria-labelledby" in props) return [];
2430
2542
  return [
@@ -2437,10 +2549,13 @@ function requireAccessibleName({ tag, props }) {
2437
2549
  ];
2438
2550
  }
2439
2551
  var NAMED_LANDMARK_TAGS = /* @__PURE__ */ new Set(["nav", "aside"]);
2440
- function landmarkNameAdvisory(ctx) {
2441
- if (!ctx.implicitRole || !NAMED_LANDMARK_TAGS.has(ctx.tag)) return [];
2442
- return requireAccessibleName(ctx);
2443
- }
2552
+ var landmarkNameAdvisory = Object.assign(
2553
+ (ctx) => {
2554
+ if (!ctx.implicitRole || !NAMED_LANDMARK_TAGS.has(ctx.tag)) return [];
2555
+ return requireAccessibleName(ctx);
2556
+ },
2557
+ { tags: [...NAMED_LANDMARK_TAGS] }
2558
+ );
2444
2559
  var HTML_ARIA_RULES = [
2445
2560
  landmarkRoleRule,
2446
2561
  landmarkNameAdvisory,
@@ -2541,6 +2656,62 @@ var figureContract = contract([
2541
2656
  ]);
2542
2657
  var detailsContract = firstChildContract("summary", "summary");
2543
2658
  var fieldsetContract = firstChildContract("legend", "legend");
2659
+ var objectContract = contract([
2660
+ { name: "param", match: isTag("param") },
2661
+ { name: "content", match: isOpenContent("param") }
2662
+ ]);
2663
+ var INTERACTIVE_CONTENT_TAGS = ["a", "button", "input", "select", "textarea", "label"];
2664
+ var buttonContract = closedContract([
2665
+ { name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
2666
+ ]);
2667
+ var anchorContract = closedContract([
2668
+ { name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
2669
+ ]);
2670
+ var LABELABLE_TAGS = [
2671
+ "button",
2672
+ "input",
2673
+ "meter",
2674
+ "output",
2675
+ "progress",
2676
+ "select",
2677
+ "textarea"
2678
+ ];
2679
+ var labelContract = contract([
2680
+ { name: "control", match: isTag(...LABELABLE_TAGS), cardinality: { max: 1 } }
2681
+ ]);
2682
+ var P_BLOCKED_TAGS = [
2683
+ "address",
2684
+ "article",
2685
+ "aside",
2686
+ "blockquote",
2687
+ "details",
2688
+ "dialog",
2689
+ "div",
2690
+ "dl",
2691
+ "fieldset",
2692
+ "figure",
2693
+ "footer",
2694
+ "form",
2695
+ "h1",
2696
+ "h2",
2697
+ "h3",
2698
+ "h4",
2699
+ "h5",
2700
+ "h6",
2701
+ "header",
2702
+ "hr",
2703
+ "main",
2704
+ "nav",
2705
+ "ol",
2706
+ "p",
2707
+ "pre",
2708
+ "section",
2709
+ "table",
2710
+ "ul"
2711
+ ];
2712
+ var pContract = closedContract([
2713
+ { name: "content", match: isOpenContent(...P_BLOCKED_TAGS) }
2714
+ ]);
2544
2715
  var mediaContract = contract([
2545
2716
  { name: "source", match: isTag("source") },
2546
2717
  { name: "track", match: isTag("track") },
@@ -2595,6 +2766,11 @@ var htmlContracts = {
2595
2766
  details: detailsContract,
2596
2767
  fieldset: fieldsetContract,
2597
2768
  dialog: dialogContract,
2769
+ object: objectContract,
2770
+ button: buttonContract,
2771
+ a: anchorContract,
2772
+ label: labelContract,
2773
+ p: pContract,
2598
2774
  head: headContract,
2599
2775
  html: htmlContract
2600
2776
  };