praxis-kit 6.5.0 → 6.6.1

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.
@@ -25,6 +25,9 @@ function isNull(value) {
25
25
  function isNonNull(value) {
26
26
  return value != null;
27
27
  }
28
+ function isNullish(value) {
29
+ return isNull(value) || value === void 0;
30
+ }
28
31
 
29
32
  // ../../lib/primitive/src/utils/type-guards.ts
30
33
  function isObject(value, excludeArrays = false) {
@@ -526,20 +529,29 @@ function isAriaAttributeValidForRole(attr, role) {
526
529
  }
527
530
 
528
531
  // ../../lib/primitive/src/guards/aria/is-aria-role.ts
532
+ function lookupImplicitRole(tag) {
533
+ return IMPLICIT_ROLE_RECORD[tag];
534
+ }
529
535
  function isStrongImplicitRole(tag) {
530
- if (!(tag in IMPLICIT_ROLE_RECORD)) return false;
531
- return STRONG_ROLES_SET.has(IMPLICIT_ROLE_RECORD[tag]);
536
+ const role = lookupImplicitRole(tag);
537
+ return !isNullish(role) && STRONG_ROLES_SET.has(role);
532
538
  }
533
- function isStandaloneTag(tag) {
534
- if (!(tag in IMPLICIT_ROLE_RECORD)) return false;
535
- return STANDALONE_ROLES_SET.has(IMPLICIT_ROLE_RECORD[tag]);
539
+ function hasStandaloneRole(tag) {
540
+ const role = lookupImplicitRole(tag);
541
+ return !isNullish(role) && STANDALONE_ROLES_SET.has(role);
536
542
  }
537
- function getInputImplicitRole(type) {
538
- if (!isString(type) || !(type in INPUT_TYPE_ROLE_MAP)) return void 0;
539
- return INPUT_TYPE_ROLE_MAP[type];
543
+ var LIST_ELIGIBLE_INPUT_TYPES = /* @__PURE__ */ new Set(["text", "search", "tel", "url", "email"]);
544
+ function getInputImplicitRole(type, list) {
545
+ if (!isString(type)) return void 0;
546
+ const role = INPUT_TYPE_ROLE_MAP[type];
547
+ if (!role) return void 0;
548
+ if (!isNullish(list) && LIST_ELIGIBLE_INPUT_TYPES.has(type)) {
549
+ return "combobox";
550
+ }
551
+ return role;
540
552
  }
541
553
  function getConditionalImplicitRole(tag, ariaLabel, ariaLabelledBy) {
542
- const isNamed = isString(ariaLabel) || isString(ariaLabelledBy);
554
+ const isNamed = isString(ariaLabel) && ariaLabel.trim().length > 0 || isString(ariaLabelledBy) && ariaLabelledBy.trim().length > 0;
543
555
  if (!isNamed) return void 0;
544
556
  if (tag === "section") return "region";
545
557
  if (tag === "form") return "form";
@@ -585,7 +597,7 @@ function isTag(...args) {
585
597
  // ../../lib/contract/src/aria/aria-role-policy.ts
586
598
  function getImplicitRole(tag, props) {
587
599
  if (tag in IMPLICIT_ROLE_RECORD) return IMPLICIT_ROLE_RECORD[tag];
588
- if (tag === "input") return getInputImplicitRole(props?.type);
600
+ if (tag === "input") return getInputImplicitRole(props?.type, props?.list);
589
601
  if (tag === "img") return props?.alt === "" ? "none" : "img";
590
602
  if (tag === "section" || tag === "form") {
591
603
  return getConditionalImplicitRole(tag, props?.["aria-label"], props?.["aria-labelledby"]);
@@ -1274,7 +1286,6 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1274
1286
  if (!isIntrinsicTag(tag)) return { proceed: false, result: { props, violations: [] } };
1275
1287
  const implicitRole = getImplicitRole(tag, props);
1276
1288
  const hasRole = isNonNull(implicitRole) || isString(props.role) && props.role.length > 0;
1277
- if (!hasRole) return { proceed: false, result: { props, violations: [] } };
1278
1289
  const normalized = _AriaPolicyEngine.#normalizeEmptyRole(tag, props);
1279
1290
  const workingProps = normalized.normalized ? normalized.result.props : props;
1280
1291
  const preExistingViolations = normalized.normalized ? normalized.result.violations : [];
@@ -1284,6 +1295,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1284
1295
  tag,
1285
1296
  implicitRole,
1286
1297
  effectiveRole,
1298
+ hasRole,
1287
1299
  props: workingProps,
1288
1300
  preExistingViolations,
1289
1301
  context: { tag, props: workingProps, implicitRole, effectiveRole }
@@ -1327,6 +1339,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1327
1339
  static evaluate(tag, props) {
1328
1340
  const derived = _AriaPolicyEngine.#deriveContext(tag, props);
1329
1341
  if (!derived.proceed) return derived.result;
1342
+ if (!derived.hasRole)
1343
+ return { props: derived.props, violations: [...derived.preExistingViolations] };
1330
1344
  const {
1331
1345
  tag: narrowedTag,
1332
1346
  implicitRole,
@@ -1351,10 +1365,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1351
1365
  props: workingProps,
1352
1366
  preExistingViolations
1353
1367
  } = derived;
1354
- const { violations, fixes } = _AriaPolicyEngine.#runRules(
1355
- [..._AriaPolicyEngine.#getRules(context), ...extraRules],
1356
- context
1357
- );
1368
+ const rules = derived.hasRole ? [..._AriaPolicyEngine.#getRules(context), ...extraRules] : extraRules;
1369
+ const { violations, fixes } = _AriaPolicyEngine.#runRules(rules, context);
1358
1370
  const next = _AriaPolicyEngine.#applyFixes(narrowedTag, implicitRole, workingProps, fixes);
1359
1371
  return { props: next, violations: [...preExistingViolations, ...violations] };
1360
1372
  }
@@ -1553,7 +1565,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1553
1565
  static #checkStandaloneRegion({ tag, props, implicitRole }) {
1554
1566
  const role = props.role;
1555
1567
  if (role !== "region") return NO_VIOLATIONS2;
1556
- if (!isStandaloneTag(tag)) return NO_VIOLATIONS2;
1568
+ if (!hasStandaloneRole(tag)) return NO_VIOLATIONS2;
1557
1569
  const diagnostic = HtmlDiagnostics.standaloneRegionOverride(tag, implicitRole ?? tag);
1558
1570
  return [
1559
1571
  {
@@ -2136,6 +2148,93 @@ var readonlyProps = ({
2136
2148
  // ../core/src/html/evaluators.ts
2137
2149
  import { warnDiagnostics as warnDiagnostics2 } from "../_shared/diagnostics.js";
2138
2150
 
2151
+ // ../core/src/html/contracts/categories.ts
2152
+ var METADATA_TAGS = ["script", "template"];
2153
+ var VOID_TAGS = [
2154
+ "area",
2155
+ "base",
2156
+ "br",
2157
+ "col",
2158
+ "embed",
2159
+ "hr",
2160
+ "img",
2161
+ "input",
2162
+ "link",
2163
+ "meta",
2164
+ "param",
2165
+ "source",
2166
+ "track",
2167
+ "wbr"
2168
+ ];
2169
+ var TEXT_ONLY_TAGS = [
2170
+ "option",
2171
+ "script",
2172
+ "style",
2173
+ "textarea",
2174
+ "title"
2175
+ ];
2176
+ var LANDMARK_TAGS = [
2177
+ "article",
2178
+ "aside",
2179
+ "footer",
2180
+ "header",
2181
+ "main",
2182
+ "nav"
2183
+ ];
2184
+ var INTERACTIVE_CONTENT_TAGS = [
2185
+ "a",
2186
+ "button",
2187
+ "input",
2188
+ "select",
2189
+ "textarea",
2190
+ "label"
2191
+ ];
2192
+ var LABELABLE_TAGS = [
2193
+ "button",
2194
+ "input",
2195
+ "meter",
2196
+ "output",
2197
+ "progress",
2198
+ "select",
2199
+ "textarea"
2200
+ ];
2201
+ var OTHER_INTERACTIVE_TAGS = [
2202
+ "a",
2203
+ "details",
2204
+ "embed",
2205
+ "iframe"
2206
+ ];
2207
+ var P_BLOCKED_TAGS = [
2208
+ "address",
2209
+ "article",
2210
+ "aside",
2211
+ "blockquote",
2212
+ "details",
2213
+ "dialog",
2214
+ "div",
2215
+ "dl",
2216
+ "fieldset",
2217
+ "figure",
2218
+ "footer",
2219
+ "form",
2220
+ "h1",
2221
+ "h2",
2222
+ "h3",
2223
+ "h4",
2224
+ "h5",
2225
+ "h6",
2226
+ "header",
2227
+ "hr",
2228
+ "main",
2229
+ "nav",
2230
+ "ol",
2231
+ "p",
2232
+ "pre",
2233
+ "section",
2234
+ "table",
2235
+ "ul"
2236
+ ];
2237
+
2139
2238
  // ../core/src/html/spec/vocabulary/input.ts
2140
2239
  var TEXT_INPUT_TYPES = ["text", "search", "url", "tel", "email", "password"];
2141
2240
  var NUMERIC_INPUT_TYPES = [
@@ -2515,7 +2614,11 @@ var ALLOWED_ROLES = {
2515
2614
  "treeitem"
2516
2615
  ],
2517
2616
  dialog: ["alertdialog"],
2518
- fieldset: ["none", "presentation", "radiogroup"]
2617
+ fieldset: ["none", "presentation", "radiogroup"],
2618
+ // `<label>` has no implicit role and no documented alternates — its native labeling
2619
+ // semantics (control association, accessible-name contribution) aren't reproducible via
2620
+ // ARIA, so any explicit `role` should be avoided rather than substituted.
2621
+ label: []
2519
2622
  };
2520
2623
  var ELEMENT_SPECS = {
2521
2624
  input: inputElementSpec,
@@ -2556,7 +2659,10 @@ var roleNotPermittedRule = Object.assign(
2556
2659
  );
2557
2660
 
2558
2661
  // ../core/src/html/aria-rules.ts
2559
- var LANDMARK_TAG_SET = /* @__PURE__ */ new Set(["article", "aside", "footer", "header", "main", "nav"]);
2662
+ function defineAriaRule(tags, rule) {
2663
+ return Object.assign(rule, { tags });
2664
+ }
2665
+ var LANDMARK_TAG_SET = new Set(LANDMARK_TAGS);
2560
2666
  var removeLandmarkRoleOverride = {
2561
2667
  kind: "removeRole",
2562
2668
  apply: ({ props }) => {
@@ -2565,10 +2671,11 @@ var removeLandmarkRoleOverride = {
2565
2671
  return { applied: true, next: rest, previous: props };
2566
2672
  }
2567
2673
  };
2568
- var landmarkRoleRule = Object.assign(
2674
+ var landmarkRoleRule = defineAriaRule(
2675
+ LANDMARK_TAGS,
2569
2676
  ({ tag, props, implicitRole }) => {
2570
2677
  if (!LANDMARK_TAG_SET.has(tag) || !implicitRole) return [];
2571
- const role = props.role;
2678
+ const { role } = props;
2572
2679
  if (!role || role === implicitRole) return [];
2573
2680
  const diagnostic = HtmlDiagnostics.landmarkRoleOverride(tag, implicitRole, role);
2574
2681
  return [
@@ -2580,8 +2687,7 @@ var landmarkRoleRule = Object.assign(
2580
2687
  diagnostic
2581
2688
  }
2582
2689
  ];
2583
- },
2584
- { tags: [...LANDMARK_TAG_SET] }
2690
+ }
2585
2691
  );
2586
2692
  function requireAccessibleName({ tag, props }) {
2587
2693
  if ("aria-label" in props || "aria-labelledby" in props) return [];
@@ -2594,38 +2700,44 @@ function requireAccessibleName({ tag, props }) {
2594
2700
  }
2595
2701
  ];
2596
2702
  }
2597
- var NAMED_LANDMARK_TAGS = /* @__PURE__ */ new Set(["nav", "aside"]);
2598
- var landmarkNameAdvisory = Object.assign(
2703
+ var NAMED_LANDMARK_TAGS = ["nav", "aside"];
2704
+ var NAMED_LANDMARK_TAG_SET = new Set(NAMED_LANDMARK_TAGS);
2705
+ var landmarkAccessibleNameRule = defineAriaRule(
2706
+ NAMED_LANDMARK_TAGS,
2599
2707
  (ctx) => {
2600
- if (!ctx.implicitRole || !NAMED_LANDMARK_TAGS.has(ctx.tag)) return [];
2708
+ if (!ctx.implicitRole || !NAMED_LANDMARK_TAG_SET.has(ctx.tag)) return [];
2601
2709
  return requireAccessibleName(ctx);
2602
- },
2603
- { tags: [...NAMED_LANDMARK_TAGS] }
2710
+ }
2604
2711
  );
2605
2712
  var HTML_ARIA_RULES = [
2606
2713
  landmarkRoleRule,
2607
- landmarkNameAdvisory,
2714
+ landmarkAccessibleNameRule,
2608
2715
  roleNotPermittedRule,
2609
2716
  ...INPUT_RULES
2610
2717
  ];
2611
2718
 
2612
- // ../core/src/html/contracts.ts
2719
+ // ../core/src/html/contracts/helpers.ts
2613
2720
  import { warnDiagnostics } from "../_shared/diagnostics.js";
2721
+ function isVNodeLike(child) {
2722
+ return isObject(child) && "type" in child;
2723
+ }
2614
2724
  function isOpenContent(...blockedTags) {
2615
2725
  const set2 = new Set(blockedTags);
2616
2726
  return (child) => {
2617
- if (!isObject(child) || !("type" in child)) return false;
2727
+ if (!isVNodeLike(child)) return false;
2618
2728
  const tag = getTag(child);
2619
2729
  return tag === void 0 || !set2.has(tag);
2620
2730
  };
2621
2731
  }
2622
- var METADATA_TAGS = ["script", "template"];
2623
2732
  var metadataMatch = isTag(...METADATA_TAGS);
2624
2733
  function metadata(name = "metadata") {
2625
2734
  return { name, match: metadataMatch };
2626
2735
  }
2736
+ function optional(name, tag) {
2737
+ return { name, match: isTag(tag), cardinality: { max: 1 } };
2738
+ }
2627
2739
  function firstOptional(name, tag) {
2628
- return { name, match: isTag(tag), cardinality: { max: 1 }, position: "first" };
2740
+ return { ...optional(name, tag), position: "first" };
2629
2741
  }
2630
2742
  function contract(children, options) {
2631
2743
  return { diagnostics: warnDiagnostics, children, ...options };
@@ -2639,50 +2751,41 @@ function ariaContract(aria) {
2639
2751
  function firstChildContract(name, tag) {
2640
2752
  return contract([firstOptional(name, tag), { name: "content", match: isOpenContent(tag) }]);
2641
2753
  }
2642
- var VOID_TAGS = [
2643
- "area",
2644
- "base",
2645
- "br",
2646
- "col",
2647
- "embed",
2648
- "hr",
2649
- "img",
2650
- "input",
2651
- "link",
2652
- "meta",
2653
- "param",
2654
- "source",
2655
- "track",
2656
- "wbr"
2657
- ];
2658
- var TEXT_ONLY_TAGS = ["option", "script", "style", "textarea", "title"];
2659
- var LANDMARK_TAGS = ["article", "aside", "footer", "header", "main", "nav"];
2660
- var listContract = closedContract([
2661
- { name: "list-item", match: isTag("li", ...METADATA_TAGS) }
2662
- ]);
2663
- var tableContract = closedContract([
2664
- firstOptional("caption", "caption"),
2665
- { name: "colgroup", match: isTag("colgroup") },
2666
- { name: "thead", match: isTag("thead"), cardinality: { max: 1 } },
2667
- { name: "tbody", match: isTag("tbody") },
2668
- { name: "tfoot", match: isTag("tfoot"), cardinality: { max: 1 } },
2669
- { name: "table-row", match: isTag("tr", ...METADATA_TAGS) }
2670
- ]);
2671
- var tableBodyContract = closedContract([
2672
- { name: "table-row", match: isTag("tr", ...METADATA_TAGS) }
2673
- ]);
2674
- var tableRowContract = closedContract([
2675
- { name: "table-cell", match: isTag("td", "th", ...METADATA_TAGS) }
2676
- ]);
2677
- var colgroupContract = closedContract([
2678
- { name: "column", match: isTag("col", "template") }
2754
+ function getChildProp(child, key) {
2755
+ if (!isVNodeLike(child)) return void 0;
2756
+ const { props } = child;
2757
+ if (!isObject(props)) return void 0;
2758
+ return props[key];
2759
+ }
2760
+
2761
+ // ../core/src/html/contracts/aria/landmarks.ts
2762
+ var landmarkContract = ariaContract([landmarkRoleRule, landmarkAccessibleNameRule]);
2763
+
2764
+ // ../core/src/html/contracts/aria/widgets.ts
2765
+ var dialogContract = ariaContract([requireAccessibleName]);
2766
+ var menuContract = ariaContract([requireAccessibleName]);
2767
+ var menubarContract = ariaContract([requireAccessibleName]);
2768
+ var treeContract = ariaContract([requireAccessibleName]);
2769
+ var gridContract = ariaContract([requireAccessibleName]);
2770
+ var listboxContract = ariaContract([requireAccessibleName]);
2771
+ var tablistContract = ariaContract([requireAccessibleName]);
2772
+ var radiogroupContract = ariaContract([requireAccessibleName]);
2773
+
2774
+ // ../core/src/html/contracts/html/document.ts
2775
+ var headContract = closedContract([
2776
+ {
2777
+ name: "metadata",
2778
+ match: isTag("base", "link", "meta", "noscript", "script", "style", "template", "title")
2779
+ }
2679
2780
  ]);
2680
- var dlContract = closedContract([
2681
- { name: "term", match: isTag("dt") },
2682
- { name: "description", match: isTag("dd") },
2683
- { name: "group", match: isTag("div") },
2684
- metadata()
2781
+ var htmlContract = closedContract([
2782
+ { name: "head", match: isTag("head"), cardinality: { min: 1, max: 1 }, position: "first" },
2783
+ { name: "body", match: isTag("body"), cardinality: { min: 1, max: 1 } }
2685
2784
  ]);
2785
+ var voidContract = contract([], { exclusiveChildren: true, allowText: false });
2786
+ var textOnlyContract = contract([], { exclusiveChildren: true });
2787
+
2788
+ // ../core/src/html/contracts/html/forms.ts
2686
2789
  var selectContract = closedContract([
2687
2790
  { name: "option", match: isTag("option", "optgroup", "hr", ...METADATA_TAGS) }
2688
2791
  ]);
@@ -2692,6 +2795,45 @@ var optgroupContract = closedContract([
2692
2795
  var datalistContract = closedContract([
2693
2796
  { name: "option", match: isTag("option", ...METADATA_TAGS) }
2694
2797
  ]);
2798
+ var buttonContract = closedContract([
2799
+ { name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
2800
+ ]);
2801
+ var anchorContract = closedContract([
2802
+ { name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
2803
+ ]);
2804
+ var labelableTagMatch = isTag(...LABELABLE_TAGS);
2805
+ function isLabelableControl(child) {
2806
+ if (!labelableTagMatch(child)) return false;
2807
+ if (getTag(child) !== "input") return true;
2808
+ const type = getChildProp(child, "type");
2809
+ return !isString(type) || type !== "hidden";
2810
+ }
2811
+ var labelContract = contract([
2812
+ { name: "control", match: isLabelableControl, cardinality: { max: 1 } },
2813
+ { name: "nested-label", match: isTag("label"), cardinality: { max: 0 } },
2814
+ {
2815
+ name: "other-interactive-content",
2816
+ match: isTag(...OTHER_INTERACTIVE_TAGS),
2817
+ cardinality: { max: 0 }
2818
+ }
2819
+ ]);
2820
+
2821
+ // ../core/src/html/contracts/html/grouping.ts
2822
+ var detailsContract = firstChildContract("summary", "summary");
2823
+ var fieldsetContract = firstChildContract("legend", "legend");
2824
+
2825
+ // ../core/src/html/contracts/html/lists.ts
2826
+ var listContract = closedContract([
2827
+ { name: "list-item", match: isTag("li", ...METADATA_TAGS) }
2828
+ ]);
2829
+ var dlContract = closedContract([
2830
+ { name: "term", match: isTag("dt") },
2831
+ { name: "description", match: isTag("dd") },
2832
+ { name: "group", match: isTag("div") },
2833
+ metadata()
2834
+ ]);
2835
+
2836
+ // ../core/src/html/contracts/html/media.ts
2695
2837
  var pictureContract = closedContract([
2696
2838
  { name: "source", match: isTag("source", ...METADATA_TAGS) },
2697
2839
  { name: "image", match: isTag("img"), cardinality: { min: 1, max: 1 }, position: "last" }
@@ -2700,91 +2842,49 @@ var figureContract = contract([
2700
2842
  { name: "caption", match: isTag("figcaption"), cardinality: { max: 1 } },
2701
2843
  { name: "content", match: isOpenContent("figcaption") }
2702
2844
  ]);
2703
- var detailsContract = firstChildContract("summary", "summary");
2704
- var fieldsetContract = firstChildContract("legend", "legend");
2705
2845
  var objectContract = contract([
2706
2846
  { name: "param", match: isTag("param") },
2707
2847
  { name: "content", match: isOpenContent("param") }
2708
2848
  ]);
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
- ]);
2761
2849
  var mediaContract = contract([
2762
2850
  { name: "source", match: isTag("source") },
2763
2851
  { name: "track", match: isTag("track") },
2764
2852
  metadata(),
2765
2853
  { name: "content", match: isOpenContent("source", "track", ...METADATA_TAGS) }
2766
2854
  ]);
2767
- var headContract = closedContract([
2768
- {
2769
- name: "metadata",
2770
- match: isTag("base", "link", "meta", "noscript", "script", "style", "template", "title")
2771
- }
2855
+
2856
+ // ../core/src/html/contracts/html/tables.ts
2857
+ var tableContract = closedContract([
2858
+ firstOptional("caption", "caption"),
2859
+ { name: "colgroup", match: isTag("colgroup") },
2860
+ { name: "thead", match: isTag("thead"), cardinality: { max: 1 } },
2861
+ { name: "tbody", match: isTag("tbody") },
2862
+ { name: "tfoot", match: isTag("tfoot"), cardinality: { max: 1 } },
2863
+ { name: "table-row", match: isTag("tr", ...METADATA_TAGS) }
2772
2864
  ]);
2773
- var htmlContract = closedContract([
2774
- { name: "head", match: isTag("head"), cardinality: { min: 1, max: 1 }, position: "first" },
2775
- { name: "body", match: isTag("body"), cardinality: { min: 1, max: 1 } }
2865
+ var tableBodyContract = closedContract([
2866
+ { name: "table-row", match: isTag("tr", ...METADATA_TAGS) }
2776
2867
  ]);
2777
- var voidContract = contract([], { exclusiveChildren: true, allowText: false });
2778
- var textOnlyContract = contract([], { exclusiveChildren: true });
2779
- var landmarkContract = ariaContract([landmarkRoleRule, landmarkNameAdvisory]);
2780
- var dialogContract = ariaContract([requireAccessibleName]);
2781
- var menuContract = ariaContract([requireAccessibleName]);
2782
- var menubarContract = ariaContract([requireAccessibleName]);
2783
- var treeContract = ariaContract([requireAccessibleName]);
2784
- var gridContract = ariaContract([requireAccessibleName]);
2785
- var listboxContract = ariaContract([requireAccessibleName]);
2786
- var tablistContract = ariaContract([requireAccessibleName]);
2787
- var radiogroupContract = ariaContract([requireAccessibleName]);
2868
+ var tableRowContract = closedContract([
2869
+ { name: "table-cell", match: isTag("td", "th", ...METADATA_TAGS) }
2870
+ ]);
2871
+ var colgroupContract = closedContract([
2872
+ { name: "column", match: isTag("col", "template") }
2873
+ ]);
2874
+
2875
+ // ../core/src/html/contracts/html/text.ts
2876
+ var pContract = closedContract([
2877
+ { name: "content", match: isOpenContent(...P_BLOCKED_TAGS) }
2878
+ ]);
2879
+
2880
+ // ../core/src/html/contracts/build-map.ts
2881
+ function buildMap(groups) {
2882
+ return Object.fromEntries(
2883
+ groups.flatMap(([keys2, value]) => keys2.map((key) => [key, value]))
2884
+ );
2885
+ }
2886
+
2887
+ // ../core/src/html/contracts/maps.ts
2788
2888
  var CONTRACT_GROUPS = [
2789
2889
  [VOID_TAGS, voidContract],
2790
2890
  [TEXT_ONLY_TAGS, textOnlyContract],
@@ -2793,13 +2893,8 @@ var CONTRACT_GROUPS = [
2793
2893
  [["audio", "video"], mediaContract],
2794
2894
  [["thead", "tbody", "tfoot"], tableBodyContract]
2795
2895
  ];
2796
- function contractMap(groups) {
2797
- return Object.fromEntries(
2798
- groups.flatMap(([tags, enforcement]) => tags.map((tag) => [tag, enforcement]))
2799
- );
2800
- }
2801
2896
  var htmlContracts = {
2802
- ...contractMap(CONTRACT_GROUPS),
2897
+ ...buildMap(CONTRACT_GROUPS),
2803
2898
  table: tableContract,
2804
2899
  tr: tableRowContract,
2805
2900
  colgroup: colgroupContract,
@@ -3043,6 +3138,11 @@ function composeNormalizers(normalizers, fn) {
3043
3138
  function whenDefined(key, value) {
3044
3139
  return value === void 0 ? {} : { [key]: value };
3045
3140
  }
3141
+ function mergeAriaRules(aria, rules) {
3142
+ if (!aria?.length) return rules;
3143
+ if (!rules?.length) return aria;
3144
+ return [...aria, ...rules];
3145
+ }
3046
3146
  function resolveFactoryOptions(options = {}) {
3047
3147
  const { styling, enforcement } = options;
3048
3148
  const composedNormalizeFn = composeNormalizers(enforcement?.props, options.normalize);
@@ -3063,7 +3163,7 @@ function resolveFactoryOptions(options = {}) {
3063
3163
  ...whenDefined("defaultVariants", styling?.defaults),
3064
3164
  ...whenDefined("compoundVariants", styling?.compounds),
3065
3165
  ...whenDefined("normalizeFn", composedNormalizeFn),
3066
- ...whenDefined("ariaRules", enforcement?.aria),
3166
+ ...whenDefined("ariaRules", mergeAriaRules(enforcement?.aria, enforcement?.rules)),
3067
3167
  ...whenDefined("childRules", enforcement?.children),
3068
3168
  ...whenDefined("exclusiveChildren", enforcement?.exclusiveChildren),
3069
3169
  ...whenDefined("allowText", enforcement?.allowText),
@@ -250,6 +250,16 @@ type EnforcementOptions<TAllowed extends ElementType = ElementType> = {
250
250
  */
251
251
  readonly diagnostics?: Diagnostics | DiagnosticsMode;
252
252
  readonly aria?: readonly AriaRule[];
253
+ /**
254
+ * Rules that need `AriaPolicyEngine`'s fix-application/caching machinery
255
+ * (`AriaRule`'s `readsProps`, fixable `AriaFix` results) but have no
256
+ * relationship to ARIA semantics — an HTML fact or a security check like a
257
+ * dangerous-URL-scheme guard, for example. Evaluated together with `aria`
258
+ * (both run through the same engine, merged into one rule set) — this is a
259
+ * separate bucket purely so a non-ARIA rule doesn't have to sit under the
260
+ * misleading `aria` name to get the machinery it needs.
261
+ */
262
+ readonly rules?: readonly AriaRule[];
253
263
  readonly children?: readonly ChildRuleInput[];
254
264
  /**
255
265
  * When true, only children matching a `children` rule (or text, per `allowText`)