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.
package/dist/vue/index.js CHANGED
@@ -23,6 +23,9 @@ function isNull(value) {
23
23
  function isNonNull(value) {
24
24
  return value != null;
25
25
  }
26
+ function isNullish(value) {
27
+ return isNull(value) || value === void 0;
28
+ }
26
29
 
27
30
  // ../../lib/primitive/src/utils/type-guards.ts
28
31
  function isObject(value, excludeArrays = false) {
@@ -610,20 +613,29 @@ function isAriaAttributeValidForRole(attr, role) {
610
613
  }
611
614
 
612
615
  // ../../lib/primitive/src/guards/aria/is-aria-role.ts
616
+ function lookupImplicitRole(tag) {
617
+ return IMPLICIT_ROLE_RECORD[tag];
618
+ }
613
619
  function isStrongImplicitRole(tag) {
614
- if (!(tag in IMPLICIT_ROLE_RECORD)) return false;
615
- return STRONG_ROLES_SET.has(IMPLICIT_ROLE_RECORD[tag]);
620
+ const role = lookupImplicitRole(tag);
621
+ return !isNullish(role) && STRONG_ROLES_SET.has(role);
616
622
  }
617
- function isStandaloneTag(tag) {
618
- if (!(tag in IMPLICIT_ROLE_RECORD)) return false;
619
- return STANDALONE_ROLES_SET.has(IMPLICIT_ROLE_RECORD[tag]);
623
+ function hasStandaloneRole(tag) {
624
+ const role = lookupImplicitRole(tag);
625
+ return !isNullish(role) && STANDALONE_ROLES_SET.has(role);
620
626
  }
621
- function getInputImplicitRole(type) {
622
- if (!isString(type) || !(type in INPUT_TYPE_ROLE_MAP)) return void 0;
623
- return INPUT_TYPE_ROLE_MAP[type];
627
+ var LIST_ELIGIBLE_INPUT_TYPES = /* @__PURE__ */ new Set(["text", "search", "tel", "url", "email"]);
628
+ function getInputImplicitRole(type, list) {
629
+ if (!isString(type)) return void 0;
630
+ const role = INPUT_TYPE_ROLE_MAP[type];
631
+ if (!role) return void 0;
632
+ if (!isNullish(list) && LIST_ELIGIBLE_INPUT_TYPES.has(type)) {
633
+ return "combobox";
634
+ }
635
+ return role;
624
636
  }
625
637
  function getConditionalImplicitRole(tag, ariaLabel, ariaLabelledBy) {
626
- const isNamed = isString(ariaLabel) || isString(ariaLabelledBy);
638
+ const isNamed = isString(ariaLabel) && ariaLabel.trim().length > 0 || isString(ariaLabelledBy) && ariaLabelledBy.trim().length > 0;
627
639
  if (!isNamed) return void 0;
628
640
  if (tag === "section") return "region";
629
641
  if (tag === "form") return "form";
@@ -692,7 +704,7 @@ function defineContractComponent(options) {
692
704
  // ../../lib/contract/src/aria/aria-role-policy.ts
693
705
  function getImplicitRole(tag, props) {
694
706
  if (tag in IMPLICIT_ROLE_RECORD) return IMPLICIT_ROLE_RECORD[tag];
695
- if (tag === "input") return getInputImplicitRole(props?.type);
707
+ if (tag === "input") return getInputImplicitRole(props?.type, props?.list);
696
708
  if (tag === "img") return props?.alt === "" ? "none" : "img";
697
709
  if (tag === "section" || tag === "form") {
698
710
  return getConditionalImplicitRole(tag, props?.["aria-label"], props?.["aria-labelledby"]);
@@ -1381,7 +1393,6 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1381
1393
  if (!isIntrinsicTag(tag)) return { proceed: false, result: { props, violations: [] } };
1382
1394
  const implicitRole = getImplicitRole(tag, props);
1383
1395
  const hasRole2 = isNonNull(implicitRole) || isString(props.role) && props.role.length > 0;
1384
- if (!hasRole2) return { proceed: false, result: { props, violations: [] } };
1385
1396
  const normalized = _AriaPolicyEngine.#normalizeEmptyRole(tag, props);
1386
1397
  const workingProps = normalized.normalized ? normalized.result.props : props;
1387
1398
  const preExistingViolations = normalized.normalized ? normalized.result.violations : [];
@@ -1391,6 +1402,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1391
1402
  tag,
1392
1403
  implicitRole,
1393
1404
  effectiveRole,
1405
+ hasRole: hasRole2,
1394
1406
  props: workingProps,
1395
1407
  preExistingViolations,
1396
1408
  context: { tag, props: workingProps, implicitRole, effectiveRole }
@@ -1434,6 +1446,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1434
1446
  static evaluate(tag, props) {
1435
1447
  const derived = _AriaPolicyEngine.#deriveContext(tag, props);
1436
1448
  if (!derived.proceed) return derived.result;
1449
+ if (!derived.hasRole)
1450
+ return { props: derived.props, violations: [...derived.preExistingViolations] };
1437
1451
  const {
1438
1452
  tag: narrowedTag,
1439
1453
  implicitRole,
@@ -1458,10 +1472,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1458
1472
  props: workingProps,
1459
1473
  preExistingViolations
1460
1474
  } = derived;
1461
- const { violations, fixes } = _AriaPolicyEngine.#runRules(
1462
- [..._AriaPolicyEngine.#getRules(context), ...extraRules],
1463
- context
1464
- );
1475
+ const rules = derived.hasRole ? [..._AriaPolicyEngine.#getRules(context), ...extraRules] : extraRules;
1476
+ const { violations, fixes } = _AriaPolicyEngine.#runRules(rules, context);
1465
1477
  const next = _AriaPolicyEngine.#applyFixes(narrowedTag, implicitRole, workingProps, fixes);
1466
1478
  return { props: next, violations: [...preExistingViolations, ...violations] };
1467
1479
  }
@@ -1660,7 +1672,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1660
1672
  static #checkStandaloneRegion({ tag, props, implicitRole }) {
1661
1673
  const role = props.role;
1662
1674
  if (role !== "region") return NO_VIOLATIONS2;
1663
- if (!isStandaloneTag(tag)) return NO_VIOLATIONS2;
1675
+ if (!hasStandaloneRole(tag)) return NO_VIOLATIONS2;
1664
1676
  const diagnostic = HtmlDiagnostics.standaloneRegionOverride(tag, implicitRole ?? tag);
1665
1677
  return [
1666
1678
  {
@@ -2243,6 +2255,93 @@ var readonlyProps = ({
2243
2255
  // ../core/src/html/evaluators.ts
2244
2256
  import { warnDiagnostics as warnDiagnostics2 } from "../_shared/diagnostics.js";
2245
2257
 
2258
+ // ../core/src/html/contracts/categories.ts
2259
+ var METADATA_TAGS = ["script", "template"];
2260
+ var VOID_TAGS = [
2261
+ "area",
2262
+ "base",
2263
+ "br",
2264
+ "col",
2265
+ "embed",
2266
+ "hr",
2267
+ "img",
2268
+ "input",
2269
+ "link",
2270
+ "meta",
2271
+ "param",
2272
+ "source",
2273
+ "track",
2274
+ "wbr"
2275
+ ];
2276
+ var TEXT_ONLY_TAGS = [
2277
+ "option",
2278
+ "script",
2279
+ "style",
2280
+ "textarea",
2281
+ "title"
2282
+ ];
2283
+ var LANDMARK_TAGS = [
2284
+ "article",
2285
+ "aside",
2286
+ "footer",
2287
+ "header",
2288
+ "main",
2289
+ "nav"
2290
+ ];
2291
+ var INTERACTIVE_CONTENT_TAGS = [
2292
+ "a",
2293
+ "button",
2294
+ "input",
2295
+ "select",
2296
+ "textarea",
2297
+ "label"
2298
+ ];
2299
+ var LABELABLE_TAGS = [
2300
+ "button",
2301
+ "input",
2302
+ "meter",
2303
+ "output",
2304
+ "progress",
2305
+ "select",
2306
+ "textarea"
2307
+ ];
2308
+ var OTHER_INTERACTIVE_TAGS = [
2309
+ "a",
2310
+ "details",
2311
+ "embed",
2312
+ "iframe"
2313
+ ];
2314
+ var P_BLOCKED_TAGS = [
2315
+ "address",
2316
+ "article",
2317
+ "aside",
2318
+ "blockquote",
2319
+ "details",
2320
+ "dialog",
2321
+ "div",
2322
+ "dl",
2323
+ "fieldset",
2324
+ "figure",
2325
+ "footer",
2326
+ "form",
2327
+ "h1",
2328
+ "h2",
2329
+ "h3",
2330
+ "h4",
2331
+ "h5",
2332
+ "h6",
2333
+ "header",
2334
+ "hr",
2335
+ "main",
2336
+ "nav",
2337
+ "ol",
2338
+ "p",
2339
+ "pre",
2340
+ "section",
2341
+ "table",
2342
+ "ul"
2343
+ ];
2344
+
2246
2345
  // ../core/src/html/spec/vocabulary/input.ts
2247
2346
  var TEXT_INPUT_TYPES = ["text", "search", "url", "tel", "email", "password"];
2248
2347
  var NUMERIC_INPUT_TYPES = [
@@ -2622,7 +2721,11 @@ var ALLOWED_ROLES = {
2622
2721
  "treeitem"
2623
2722
  ],
2624
2723
  dialog: ["alertdialog"],
2625
- fieldset: ["none", "presentation", "radiogroup"]
2724
+ fieldset: ["none", "presentation", "radiogroup"],
2725
+ // `<label>` has no implicit role and no documented alternates — its native labeling
2726
+ // semantics (control association, accessible-name contribution) aren't reproducible via
2727
+ // ARIA, so any explicit `role` should be avoided rather than substituted.
2728
+ label: []
2626
2729
  };
2627
2730
  var ELEMENT_SPECS = {
2628
2731
  input: inputElementSpec,
@@ -2663,7 +2766,10 @@ var roleNotPermittedRule = Object.assign(
2663
2766
  );
2664
2767
 
2665
2768
  // ../core/src/html/aria-rules.ts
2666
- var LANDMARK_TAG_SET = /* @__PURE__ */ new Set(["article", "aside", "footer", "header", "main", "nav"]);
2769
+ function defineAriaRule(tags, rule) {
2770
+ return Object.assign(rule, { tags });
2771
+ }
2772
+ var LANDMARK_TAG_SET = new Set(LANDMARK_TAGS);
2667
2773
  var removeLandmarkRoleOverride = {
2668
2774
  kind: "removeRole",
2669
2775
  apply: ({ props }) => {
@@ -2672,10 +2778,11 @@ var removeLandmarkRoleOverride = {
2672
2778
  return { applied: true, next: rest, previous: props };
2673
2779
  }
2674
2780
  };
2675
- var landmarkRoleRule = Object.assign(
2781
+ var landmarkRoleRule = defineAriaRule(
2782
+ LANDMARK_TAGS,
2676
2783
  ({ tag, props, implicitRole }) => {
2677
2784
  if (!LANDMARK_TAG_SET.has(tag) || !implicitRole) return [];
2678
- const role = props.role;
2785
+ const { role } = props;
2679
2786
  if (!role || role === implicitRole) return [];
2680
2787
  const diagnostic = HtmlDiagnostics.landmarkRoleOverride(tag, implicitRole, role);
2681
2788
  return [
@@ -2687,8 +2794,7 @@ var landmarkRoleRule = Object.assign(
2687
2794
  diagnostic
2688
2795
  }
2689
2796
  ];
2690
- },
2691
- { tags: [...LANDMARK_TAG_SET] }
2797
+ }
2692
2798
  );
2693
2799
  function requireAccessibleName({ tag, props }) {
2694
2800
  if ("aria-label" in props || "aria-labelledby" in props) return [];
@@ -2701,38 +2807,44 @@ function requireAccessibleName({ tag, props }) {
2701
2807
  }
2702
2808
  ];
2703
2809
  }
2704
- var NAMED_LANDMARK_TAGS = /* @__PURE__ */ new Set(["nav", "aside"]);
2705
- var landmarkNameAdvisory = Object.assign(
2810
+ var NAMED_LANDMARK_TAGS = ["nav", "aside"];
2811
+ var NAMED_LANDMARK_TAG_SET = new Set(NAMED_LANDMARK_TAGS);
2812
+ var landmarkAccessibleNameRule = defineAriaRule(
2813
+ NAMED_LANDMARK_TAGS,
2706
2814
  (ctx) => {
2707
- if (!ctx.implicitRole || !NAMED_LANDMARK_TAGS.has(ctx.tag)) return [];
2815
+ if (!ctx.implicitRole || !NAMED_LANDMARK_TAG_SET.has(ctx.tag)) return [];
2708
2816
  return requireAccessibleName(ctx);
2709
- },
2710
- { tags: [...NAMED_LANDMARK_TAGS] }
2817
+ }
2711
2818
  );
2712
2819
  var HTML_ARIA_RULES = [
2713
2820
  landmarkRoleRule,
2714
- landmarkNameAdvisory,
2821
+ landmarkAccessibleNameRule,
2715
2822
  roleNotPermittedRule,
2716
2823
  ...INPUT_RULES
2717
2824
  ];
2718
2825
 
2719
- // ../core/src/html/contracts.ts
2826
+ // ../core/src/html/contracts/helpers.ts
2720
2827
  import { warnDiagnostics } from "../_shared/diagnostics.js";
2828
+ function isVNodeLike(child) {
2829
+ return isObject(child) && "type" in child;
2830
+ }
2721
2831
  function isOpenContent(...blockedTags) {
2722
2832
  const set2 = new Set(blockedTags);
2723
2833
  return (child) => {
2724
- if (!isObject(child) || !("type" in child)) return false;
2834
+ if (!isVNodeLike(child)) return false;
2725
2835
  const tag = getTag(child);
2726
2836
  return tag === void 0 || !set2.has(tag);
2727
2837
  };
2728
2838
  }
2729
- var METADATA_TAGS = ["script", "template"];
2730
2839
  var metadataMatch = isTag(...METADATA_TAGS);
2731
2840
  function metadata(name = "metadata") {
2732
2841
  return { name, match: metadataMatch };
2733
2842
  }
2843
+ function optional(name, tag) {
2844
+ return { name, match: isTag(tag), cardinality: { max: 1 } };
2845
+ }
2734
2846
  function firstOptional(name, tag) {
2735
- return { name, match: isTag(tag), cardinality: { max: 1 }, position: "first" };
2847
+ return { ...optional(name, tag), position: "first" };
2736
2848
  }
2737
2849
  function contract(children, options) {
2738
2850
  return { diagnostics: warnDiagnostics, children, ...options };
@@ -2746,50 +2858,41 @@ function ariaContract(aria) {
2746
2858
  function firstChildContract(name, tag) {
2747
2859
  return contract([firstOptional(name, tag), { name: "content", match: isOpenContent(tag) }]);
2748
2860
  }
2749
- var VOID_TAGS = [
2750
- "area",
2751
- "base",
2752
- "br",
2753
- "col",
2754
- "embed",
2755
- "hr",
2756
- "img",
2757
- "input",
2758
- "link",
2759
- "meta",
2760
- "param",
2761
- "source",
2762
- "track",
2763
- "wbr"
2764
- ];
2765
- var TEXT_ONLY_TAGS = ["option", "script", "style", "textarea", "title"];
2766
- var LANDMARK_TAGS = ["article", "aside", "footer", "header", "main", "nav"];
2767
- var listContract = closedContract([
2768
- { name: "list-item", match: isTag("li", ...METADATA_TAGS) }
2769
- ]);
2770
- var tableContract = closedContract([
2771
- firstOptional("caption", "caption"),
2772
- { name: "colgroup", match: isTag("colgroup") },
2773
- { name: "thead", match: isTag("thead"), cardinality: { max: 1 } },
2774
- { name: "tbody", match: isTag("tbody") },
2775
- { name: "tfoot", match: isTag("tfoot"), cardinality: { max: 1 } },
2776
- { name: "table-row", match: isTag("tr", ...METADATA_TAGS) }
2777
- ]);
2778
- var tableBodyContract = closedContract([
2779
- { name: "table-row", match: isTag("tr", ...METADATA_TAGS) }
2780
- ]);
2781
- var tableRowContract = closedContract([
2782
- { name: "table-cell", match: isTag("td", "th", ...METADATA_TAGS) }
2783
- ]);
2784
- var colgroupContract = closedContract([
2785
- { name: "column", match: isTag("col", "template") }
2861
+ function getChildProp(child, key) {
2862
+ if (!isVNodeLike(child)) return void 0;
2863
+ const { props } = child;
2864
+ if (!isObject(props)) return void 0;
2865
+ return props[key];
2866
+ }
2867
+
2868
+ // ../core/src/html/contracts/aria/landmarks.ts
2869
+ var landmarkContract = ariaContract([landmarkRoleRule, landmarkAccessibleNameRule]);
2870
+
2871
+ // ../core/src/html/contracts/aria/widgets.ts
2872
+ var dialogContract = ariaContract([requireAccessibleName]);
2873
+ var menuContract = ariaContract([requireAccessibleName]);
2874
+ var menubarContract = ariaContract([requireAccessibleName]);
2875
+ var treeContract = ariaContract([requireAccessibleName]);
2876
+ var gridContract = ariaContract([requireAccessibleName]);
2877
+ var listboxContract = ariaContract([requireAccessibleName]);
2878
+ var tablistContract = ariaContract([requireAccessibleName]);
2879
+ var radiogroupContract = ariaContract([requireAccessibleName]);
2880
+
2881
+ // ../core/src/html/contracts/html/document.ts
2882
+ var headContract = closedContract([
2883
+ {
2884
+ name: "metadata",
2885
+ match: isTag("base", "link", "meta", "noscript", "script", "style", "template", "title")
2886
+ }
2786
2887
  ]);
2787
- var dlContract = closedContract([
2788
- { name: "term", match: isTag("dt") },
2789
- { name: "description", match: isTag("dd") },
2790
- { name: "group", match: isTag("div") },
2791
- metadata()
2888
+ var htmlContract = closedContract([
2889
+ { name: "head", match: isTag("head"), cardinality: { min: 1, max: 1 }, position: "first" },
2890
+ { name: "body", match: isTag("body"), cardinality: { min: 1, max: 1 } }
2792
2891
  ]);
2892
+ var voidContract = contract([], { exclusiveChildren: true, allowText: false });
2893
+ var textOnlyContract = contract([], { exclusiveChildren: true });
2894
+
2895
+ // ../core/src/html/contracts/html/forms.ts
2793
2896
  var selectContract = closedContract([
2794
2897
  { name: "option", match: isTag("option", "optgroup", "hr", ...METADATA_TAGS) }
2795
2898
  ]);
@@ -2799,6 +2902,45 @@ var optgroupContract = closedContract([
2799
2902
  var datalistContract = closedContract([
2800
2903
  { name: "option", match: isTag("option", ...METADATA_TAGS) }
2801
2904
  ]);
2905
+ var buttonContract = closedContract([
2906
+ { name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
2907
+ ]);
2908
+ var anchorContract = closedContract([
2909
+ { name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
2910
+ ]);
2911
+ var labelableTagMatch = isTag(...LABELABLE_TAGS);
2912
+ function isLabelableControl(child) {
2913
+ if (!labelableTagMatch(child)) return false;
2914
+ if (getTag(child) !== "input") return true;
2915
+ const type = getChildProp(child, "type");
2916
+ return !isString(type) || type !== "hidden";
2917
+ }
2918
+ var labelContract = contract([
2919
+ { name: "control", match: isLabelableControl, cardinality: { max: 1 } },
2920
+ { name: "nested-label", match: isTag("label"), cardinality: { max: 0 } },
2921
+ {
2922
+ name: "other-interactive-content",
2923
+ match: isTag(...OTHER_INTERACTIVE_TAGS),
2924
+ cardinality: { max: 0 }
2925
+ }
2926
+ ]);
2927
+
2928
+ // ../core/src/html/contracts/html/grouping.ts
2929
+ var detailsContract = firstChildContract("summary", "summary");
2930
+ var fieldsetContract = firstChildContract("legend", "legend");
2931
+
2932
+ // ../core/src/html/contracts/html/lists.ts
2933
+ var listContract = closedContract([
2934
+ { name: "list-item", match: isTag("li", ...METADATA_TAGS) }
2935
+ ]);
2936
+ var dlContract = closedContract([
2937
+ { name: "term", match: isTag("dt") },
2938
+ { name: "description", match: isTag("dd") },
2939
+ { name: "group", match: isTag("div") },
2940
+ metadata()
2941
+ ]);
2942
+
2943
+ // ../core/src/html/contracts/html/media.ts
2802
2944
  var pictureContract = closedContract([
2803
2945
  { name: "source", match: isTag("source", ...METADATA_TAGS) },
2804
2946
  { name: "image", match: isTag("img"), cardinality: { min: 1, max: 1 }, position: "last" }
@@ -2807,91 +2949,49 @@ var figureContract = contract([
2807
2949
  { name: "caption", match: isTag("figcaption"), cardinality: { max: 1 } },
2808
2950
  { name: "content", match: isOpenContent("figcaption") }
2809
2951
  ]);
2810
- var detailsContract = firstChildContract("summary", "summary");
2811
- var fieldsetContract = firstChildContract("legend", "legend");
2812
2952
  var objectContract = contract([
2813
2953
  { name: "param", match: isTag("param") },
2814
2954
  { name: "content", match: isOpenContent("param") }
2815
2955
  ]);
2816
- var INTERACTIVE_CONTENT_TAGS = ["a", "button", "input", "select", "textarea", "label"];
2817
- var buttonContract = closedContract([
2818
- { name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
2819
- ]);
2820
- var anchorContract = closedContract([
2821
- { name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
2822
- ]);
2823
- var LABELABLE_TAGS = [
2824
- "button",
2825
- "input",
2826
- "meter",
2827
- "output",
2828
- "progress",
2829
- "select",
2830
- "textarea"
2831
- ];
2832
- var labelContract = contract([
2833
- { name: "control", match: isTag(...LABELABLE_TAGS), cardinality: { max: 1 } }
2834
- ]);
2835
- var P_BLOCKED_TAGS = [
2836
- "address",
2837
- "article",
2838
- "aside",
2839
- "blockquote",
2840
- "details",
2841
- "dialog",
2842
- "div",
2843
- "dl",
2844
- "fieldset",
2845
- "figure",
2846
- "footer",
2847
- "form",
2848
- "h1",
2849
- "h2",
2850
- "h3",
2851
- "h4",
2852
- "h5",
2853
- "h6",
2854
- "header",
2855
- "hr",
2856
- "main",
2857
- "nav",
2858
- "ol",
2859
- "p",
2860
- "pre",
2861
- "section",
2862
- "table",
2863
- "ul"
2864
- ];
2865
- var pContract = closedContract([
2866
- { name: "content", match: isOpenContent(...P_BLOCKED_TAGS) }
2867
- ]);
2868
2956
  var mediaContract = contract([
2869
2957
  { name: "source", match: isTag("source") },
2870
2958
  { name: "track", match: isTag("track") },
2871
2959
  metadata(),
2872
2960
  { name: "content", match: isOpenContent("source", "track", ...METADATA_TAGS) }
2873
2961
  ]);
2874
- var headContract = closedContract([
2875
- {
2876
- name: "metadata",
2877
- match: isTag("base", "link", "meta", "noscript", "script", "style", "template", "title")
2878
- }
2962
+
2963
+ // ../core/src/html/contracts/html/tables.ts
2964
+ var tableContract = closedContract([
2965
+ firstOptional("caption", "caption"),
2966
+ { name: "colgroup", match: isTag("colgroup") },
2967
+ { name: "thead", match: isTag("thead"), cardinality: { max: 1 } },
2968
+ { name: "tbody", match: isTag("tbody") },
2969
+ { name: "tfoot", match: isTag("tfoot"), cardinality: { max: 1 } },
2970
+ { name: "table-row", match: isTag("tr", ...METADATA_TAGS) }
2879
2971
  ]);
2880
- var htmlContract = closedContract([
2881
- { name: "head", match: isTag("head"), cardinality: { min: 1, max: 1 }, position: "first" },
2882
- { name: "body", match: isTag("body"), cardinality: { min: 1, max: 1 } }
2972
+ var tableBodyContract = closedContract([
2973
+ { name: "table-row", match: isTag("tr", ...METADATA_TAGS) }
2883
2974
  ]);
2884
- var voidContract = contract([], { exclusiveChildren: true, allowText: false });
2885
- var textOnlyContract = contract([], { exclusiveChildren: true });
2886
- var landmarkContract = ariaContract([landmarkRoleRule, landmarkNameAdvisory]);
2887
- var dialogContract = ariaContract([requireAccessibleName]);
2888
- var menuContract = ariaContract([requireAccessibleName]);
2889
- var menubarContract = ariaContract([requireAccessibleName]);
2890
- var treeContract = ariaContract([requireAccessibleName]);
2891
- var gridContract = ariaContract([requireAccessibleName]);
2892
- var listboxContract = ariaContract([requireAccessibleName]);
2893
- var tablistContract = ariaContract([requireAccessibleName]);
2894
- var radiogroupContract = ariaContract([requireAccessibleName]);
2975
+ var tableRowContract = closedContract([
2976
+ { name: "table-cell", match: isTag("td", "th", ...METADATA_TAGS) }
2977
+ ]);
2978
+ var colgroupContract = closedContract([
2979
+ { name: "column", match: isTag("col", "template") }
2980
+ ]);
2981
+
2982
+ // ../core/src/html/contracts/html/text.ts
2983
+ var pContract = closedContract([
2984
+ { name: "content", match: isOpenContent(...P_BLOCKED_TAGS) }
2985
+ ]);
2986
+
2987
+ // ../core/src/html/contracts/build-map.ts
2988
+ function buildMap(groups) {
2989
+ return Object.fromEntries(
2990
+ groups.flatMap(([keys2, value]) => keys2.map((key) => [key, value]))
2991
+ );
2992
+ }
2993
+
2994
+ // ../core/src/html/contracts/maps.ts
2895
2995
  var CONTRACT_GROUPS = [
2896
2996
  [VOID_TAGS, voidContract],
2897
2997
  [TEXT_ONLY_TAGS, textOnlyContract],
@@ -2900,13 +3000,8 @@ var CONTRACT_GROUPS = [
2900
3000
  [["audio", "video"], mediaContract],
2901
3001
  [["thead", "tbody", "tfoot"], tableBodyContract]
2902
3002
  ];
2903
- function contractMap(groups) {
2904
- return Object.fromEntries(
2905
- groups.flatMap(([tags, enforcement]) => tags.map((tag) => [tag, enforcement]))
2906
- );
2907
- }
2908
3003
  var htmlContracts = {
2909
- ...contractMap(CONTRACT_GROUPS),
3004
+ ...buildMap(CONTRACT_GROUPS),
2910
3005
  table: tableContract,
2911
3006
  tr: tableRowContract,
2912
3007
  colgroup: colgroupContract,
@@ -3150,6 +3245,11 @@ function composeNormalizers(normalizers, fn) {
3150
3245
  function whenDefined(key, value) {
3151
3246
  return value === void 0 ? {} : { [key]: value };
3152
3247
  }
3248
+ function mergeAriaRules(aria, rules) {
3249
+ if (!aria?.length) return rules;
3250
+ if (!rules?.length) return aria;
3251
+ return [...aria, ...rules];
3252
+ }
3153
3253
  function resolveFactoryOptions(options = {}) {
3154
3254
  const { styling, enforcement } = options;
3155
3255
  const composedNormalizeFn = composeNormalizers(enforcement?.props, options.normalize);
@@ -3170,7 +3270,7 @@ function resolveFactoryOptions(options = {}) {
3170
3270
  ...whenDefined("defaultVariants", styling?.defaults),
3171
3271
  ...whenDefined("compoundVariants", styling?.compounds),
3172
3272
  ...whenDefined("normalizeFn", composedNormalizeFn),
3173
- ...whenDefined("ariaRules", enforcement?.aria),
3273
+ ...whenDefined("ariaRules", mergeAriaRules(enforcement?.aria, enforcement?.rules)),
3174
3274
  ...whenDefined("childRules", enforcement?.children),
3175
3275
  ...whenDefined("exclusiveChildren", enforcement?.exclusiveChildren),
3176
3276
  ...whenDefined("allowText", enforcement?.allowText),
@@ -231,6 +231,16 @@ type EnforcementOptions<TAllowed extends ElementType = ElementType> = {
231
231
  */
232
232
  readonly diagnostics?: Diagnostics | DiagnosticsMode;
233
233
  readonly aria?: readonly AriaRule[];
234
+ /**
235
+ * Rules that need `AriaPolicyEngine`'s fix-application/caching machinery
236
+ * (`AriaRule`'s `readsProps`, fixable `AriaFix` results) but have no
237
+ * relationship to ARIA semantics — an HTML fact or a security check like a
238
+ * dangerous-URL-scheme guard, for example. Evaluated together with `aria`
239
+ * (both run through the same engine, merged into one rule set) — this is a
240
+ * separate bucket purely so a non-ARIA rule doesn't have to sit under the
241
+ * misleading `aria` name to get the machinery it needs.
242
+ */
243
+ readonly rules?: readonly AriaRule[];
234
244
  readonly children?: readonly ChildRuleInput[];
235
245
  /**
236
246
  * When true, only children matching a `children` rule (or text, per `allowText`)