praxis-kit 6.6.0 → 7.0.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.
@@ -92,6 +92,7 @@ declare enum DiagnosticCode {
92
92
  TailwindMultipleDisplayProps = "CSS6001",
93
93
  TailwindReservedLayoutLiteral = "CSS6002",
94
94
  TailwindDeadVariantClass = "CSS6003",
95
+ TailwindLayoutOnVoidTag = "CSS6004",
95
96
  PluginInvalidShape = "PLUGIN7001",
96
97
  PluginPipelineReturnType = "PLUGIN7002",
97
98
  InternalError = "INTERNAL9000"
@@ -93,6 +93,7 @@ var DiagnosticCode = /* @__PURE__ */ ((DiagnosticCode2) => {
93
93
  DiagnosticCode2["TailwindMultipleDisplayProps"] = "CSS6001";
94
94
  DiagnosticCode2["TailwindReservedLayoutLiteral"] = "CSS6002";
95
95
  DiagnosticCode2["TailwindDeadVariantClass"] = "CSS6003";
96
+ DiagnosticCode2["TailwindLayoutOnVoidTag"] = "CSS6004";
96
97
  DiagnosticCode2["PluginInvalidShape"] = "PLUGIN7001";
97
98
  DiagnosticCode2["PluginPipelineReturnType"] = "PLUGIN7002";
98
99
  DiagnosticCode2["InternalError"] = "INTERNAL9000";
@@ -16,6 +16,11 @@ var EVENT_HANDLER_RE = /^on[A-Z]/;
16
16
  // ../../lib/primitive/src/rule/rule-brand.ts
17
17
  var RULE_BRAND = /* @__PURE__ */ Symbol("praxis-kit/dynamic-rule");
18
18
 
19
+ // ../../lib/primitive/src/rule/dynamic.ts
20
+ function dynamic(resolve) {
21
+ return { [RULE_BRAND]: true, resolve };
22
+ }
23
+
19
24
  // ../../lib/primitive/src/guards/foundational/is-defined.ts
20
25
  function isUndefined(value) {
21
26
  return value === void 0;
@@ -29,7 +34,7 @@ function isNonNull(value) {
29
34
  return value != null;
30
35
  }
31
36
  function isNullish(value) {
32
- return isNull(value) || value === void 0;
37
+ return isNull(value) || isUndefined(value);
33
38
  }
34
39
 
35
40
  // ../../lib/primitive/src/utils/type-guards.ts
@@ -643,6 +648,24 @@ var ROLE_RESTRICTED_ATTRIBUTES = /* @__PURE__ */ new Map([
643
648
  ]
644
649
  ]);
645
650
 
651
+ // ../../lib/primitive/src/constants/html/void-tags.ts
652
+ var VOID_TAGS = [
653
+ "area",
654
+ "base",
655
+ "br",
656
+ "col",
657
+ "embed",
658
+ "hr",
659
+ "img",
660
+ "input",
661
+ "link",
662
+ "meta",
663
+ "param",
664
+ "source",
665
+ "track",
666
+ "wbr"
667
+ ];
668
+
646
669
  // ../../lib/primitive/src/constants/primitive/slot-name.ts
647
670
  var SLOT_NAME = "Slot";
648
671
 
@@ -1627,7 +1650,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1627
1650
  const cached = _AriaPolicyEngine.#removeAttributeFixCache.get(attr);
1628
1651
  if (cached) return cached;
1629
1652
  const fix = {
1630
- kind: `removeAttribute:${attr}`,
1653
+ kind: "removeAttribute",
1654
+ attribute: attr,
1631
1655
  apply: ({ props }) => {
1632
1656
  if (!(attr in props)) return { applied: false, next: props };
1633
1657
  return { applied: true, next: omitProp(props, attr), previous: props };
@@ -1898,7 +1922,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1898
1922
  if (!impliedLive) return NO_VIOLATIONS2;
1899
1923
  if ("aria-live" in props) return NO_VIOLATIONS2;
1900
1924
  const injectLive = {
1901
- kind: `injectLive:${effectiveRole}`,
1925
+ kind: "injectLive",
1926
+ attribute: "aria-live",
1902
1927
  apply: (ctx) => ({
1903
1928
  applied: true,
1904
1929
  next: { ...ctx.props, "aria-live": impliedLive },
@@ -1967,6 +1992,23 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1967
1992
  }
1968
1993
  };
1969
1994
 
1995
+ // ../../lib/contract/src/aria/factories.ts
1996
+ function removeProp(props, key) {
1997
+ const next = { ...props };
1998
+ delete next[key];
1999
+ return next;
2000
+ }
2001
+ function removeAttributeFix(attribute) {
2002
+ return Object.freeze({
2003
+ kind: "removeAttribute",
2004
+ attribute,
2005
+ apply: ({ props }) => {
2006
+ if (!(attribute in props)) return { applied: false, next: props };
2007
+ return { applied: true, next: removeProp(props, attribute), previous: props };
2008
+ }
2009
+ });
2010
+ }
2011
+
1970
2012
  // ../../lib/contract/src/children/get-type-name.ts
1971
2013
  function getTypeName(value) {
1972
2014
  if (value === null) return "null";
@@ -2283,6 +2325,77 @@ var readonlyProps = ({
2283
2325
  // ../core/src/html/evaluators.ts
2284
2326
  import { warnDiagnostics as warnDiagnostics2 } from "./_shared/diagnostics.js";
2285
2327
 
2328
+ // ../core/src/html/contracts/categories.ts
2329
+ var METADATA_TAGS = ["script", "template"];
2330
+ var TEXT_ONLY_TAGS = [
2331
+ "option",
2332
+ "script",
2333
+ "style",
2334
+ "textarea",
2335
+ "title"
2336
+ ];
2337
+ var LANDMARK_TAGS = [
2338
+ "article",
2339
+ "aside",
2340
+ "footer",
2341
+ "header",
2342
+ "main",
2343
+ "nav"
2344
+ ];
2345
+ var INTERACTIVE_CONTENT_TAGS = [
2346
+ "a",
2347
+ "button",
2348
+ "input",
2349
+ "select",
2350
+ "textarea",
2351
+ "label"
2352
+ ];
2353
+ var LABELABLE_TAGS = [
2354
+ "button",
2355
+ "input",
2356
+ "meter",
2357
+ "output",
2358
+ "progress",
2359
+ "select",
2360
+ "textarea"
2361
+ ];
2362
+ var OTHER_INTERACTIVE_TAGS = [
2363
+ "a",
2364
+ "details",
2365
+ "embed",
2366
+ "iframe"
2367
+ ];
2368
+ var P_BLOCKED_TAGS = [
2369
+ "address",
2370
+ "article",
2371
+ "aside",
2372
+ "blockquote",
2373
+ "details",
2374
+ "dialog",
2375
+ "div",
2376
+ "dl",
2377
+ "fieldset",
2378
+ "figure",
2379
+ "footer",
2380
+ "form",
2381
+ "h1",
2382
+ "h2",
2383
+ "h3",
2384
+ "h4",
2385
+ "h5",
2386
+ "h6",
2387
+ "header",
2388
+ "hr",
2389
+ "main",
2390
+ "nav",
2391
+ "ol",
2392
+ "p",
2393
+ "pre",
2394
+ "section",
2395
+ "table",
2396
+ "ul"
2397
+ ];
2398
+
2286
2399
  // ../core/src/html/spec/vocabulary/input.ts
2287
2400
  var TEXT_INPUT_TYPES = ["text", "search", "url", "tel", "email", "password"];
2288
2401
  var NUMERIC_INPUT_TYPES = [
@@ -2337,20 +2450,6 @@ var INPUT_MUTUALLY_EXCLUSIVE_POLICIES = [
2337
2450
 
2338
2451
  // ../core/src/html/spec/validators/attribute-type-validator.ts
2339
2452
  var DEFAULT_INPUT_TYPE = "text";
2340
- function omit(props, key) {
2341
- const next = { ...props };
2342
- delete next[key];
2343
- return next;
2344
- }
2345
- function removeAttributeFix(attribute) {
2346
- return {
2347
- kind: `removeAttribute:${attribute}`,
2348
- apply: ({ props }) => {
2349
- if (!(attribute in props)) return { applied: false, next: props };
2350
- return { applied: true, next: omit(props, attribute), previous: props };
2351
- }
2352
- };
2353
- }
2354
2453
  function createInputAttributeTypeRule({
2355
2454
  attribute,
2356
2455
  allowedTypes
@@ -2662,7 +2761,11 @@ var ALLOWED_ROLES = {
2662
2761
  "treeitem"
2663
2762
  ],
2664
2763
  dialog: ["alertdialog"],
2665
- fieldset: ["none", "presentation", "radiogroup"]
2764
+ fieldset: ["none", "presentation", "radiogroup"],
2765
+ // `<label>` has no implicit role and no documented alternates — its native labeling
2766
+ // semantics (control association, accessible-name contribution) aren't reproducible via
2767
+ // ARIA, so any explicit `role` should be avoided rather than substituted.
2768
+ label: []
2666
2769
  };
2667
2770
  var ELEMENT_SPECS = {
2668
2771
  input: inputElementSpec,
@@ -2703,7 +2806,10 @@ var roleNotPermittedRule = Object.assign(
2703
2806
  );
2704
2807
 
2705
2808
  // ../core/src/html/aria-rules.ts
2706
- var LANDMARK_TAG_SET = /* @__PURE__ */ new Set(["article", "aside", "footer", "header", "main", "nav"]);
2809
+ function defineAriaRule(tags, rule) {
2810
+ return Object.assign(rule, { tags });
2811
+ }
2812
+ var LANDMARK_TAG_SET = new Set(LANDMARK_TAGS);
2707
2813
  var removeLandmarkRoleOverride = {
2708
2814
  kind: "removeRole",
2709
2815
  apply: ({ props }) => {
@@ -2712,10 +2818,11 @@ var removeLandmarkRoleOverride = {
2712
2818
  return { applied: true, next: rest, previous: props };
2713
2819
  }
2714
2820
  };
2715
- var landmarkRoleRule = Object.assign(
2821
+ var landmarkRoleRule = defineAriaRule(
2822
+ LANDMARK_TAGS,
2716
2823
  ({ tag, props, implicitRole }) => {
2717
2824
  if (!LANDMARK_TAG_SET.has(tag) || !implicitRole) return [];
2718
- const role = props.role;
2825
+ const { role } = props;
2719
2826
  if (!role || role === implicitRole) return [];
2720
2827
  const diagnostic = HtmlDiagnostics.landmarkRoleOverride(tag, implicitRole, role);
2721
2828
  return [
@@ -2727,8 +2834,7 @@ var landmarkRoleRule = Object.assign(
2727
2834
  diagnostic
2728
2835
  }
2729
2836
  ];
2730
- },
2731
- { tags: [...LANDMARK_TAG_SET] }
2837
+ }
2732
2838
  );
2733
2839
  function requireAccessibleName({ tag, props }) {
2734
2840
  if ("aria-label" in props || "aria-labelledby" in props) return [];
@@ -2741,38 +2847,44 @@ function requireAccessibleName({ tag, props }) {
2741
2847
  }
2742
2848
  ];
2743
2849
  }
2744
- var NAMED_LANDMARK_TAGS = /* @__PURE__ */ new Set(["nav", "aside"]);
2745
- var landmarkNameAdvisory = Object.assign(
2850
+ var NAMED_LANDMARK_TAGS = ["nav", "aside"];
2851
+ var NAMED_LANDMARK_TAG_SET = new Set(NAMED_LANDMARK_TAGS);
2852
+ var landmarkAccessibleNameRule = defineAriaRule(
2853
+ NAMED_LANDMARK_TAGS,
2746
2854
  (ctx) => {
2747
- if (!ctx.implicitRole || !NAMED_LANDMARK_TAGS.has(ctx.tag)) return [];
2855
+ if (!ctx.implicitRole || !NAMED_LANDMARK_TAG_SET.has(ctx.tag)) return [];
2748
2856
  return requireAccessibleName(ctx);
2749
- },
2750
- { tags: [...NAMED_LANDMARK_TAGS] }
2857
+ }
2751
2858
  );
2752
2859
  var HTML_ARIA_RULES = [
2753
2860
  landmarkRoleRule,
2754
- landmarkNameAdvisory,
2861
+ landmarkAccessibleNameRule,
2755
2862
  roleNotPermittedRule,
2756
2863
  ...INPUT_RULES
2757
2864
  ];
2758
2865
 
2759
- // ../core/src/html/contracts.ts
2866
+ // ../core/src/html/contracts/helpers.ts
2760
2867
  import { warnDiagnostics } from "./_shared/diagnostics.js";
2868
+ function isVNodeLike(child) {
2869
+ return isObject(child) && "type" in child;
2870
+ }
2761
2871
  function isOpenContent(...blockedTags) {
2762
2872
  const set2 = new Set(blockedTags);
2763
2873
  return (child) => {
2764
- if (!isObject(child) || !("type" in child)) return false;
2874
+ if (!isVNodeLike(child)) return false;
2765
2875
  const tag = getTag(child);
2766
2876
  return tag === void 0 || !set2.has(tag);
2767
2877
  };
2768
2878
  }
2769
- var METADATA_TAGS = ["script", "template"];
2770
2879
  var metadataMatch = isTag(...METADATA_TAGS);
2771
2880
  function metadata(name = "metadata") {
2772
2881
  return { name, match: metadataMatch };
2773
2882
  }
2883
+ function optional(name, tag) {
2884
+ return { name, match: isTag(tag), cardinality: { max: 1 } };
2885
+ }
2774
2886
  function firstOptional(name, tag) {
2775
- return { name, match: isTag(tag), cardinality: { max: 1 }, position: "first" };
2887
+ return { ...optional(name, tag), position: "first" };
2776
2888
  }
2777
2889
  function contract(children, options) {
2778
2890
  return { diagnostics: warnDiagnostics, children, ...options };
@@ -2786,50 +2898,41 @@ function ariaContract(aria) {
2786
2898
  function firstChildContract(name, tag) {
2787
2899
  return contract([firstOptional(name, tag), { name: "content", match: isOpenContent(tag) }]);
2788
2900
  }
2789
- var VOID_TAGS = [
2790
- "area",
2791
- "base",
2792
- "br",
2793
- "col",
2794
- "embed",
2795
- "hr",
2796
- "img",
2797
- "input",
2798
- "link",
2799
- "meta",
2800
- "param",
2801
- "source",
2802
- "track",
2803
- "wbr"
2804
- ];
2805
- var TEXT_ONLY_TAGS = ["option", "script", "style", "textarea", "title"];
2806
- var LANDMARK_TAGS = ["article", "aside", "footer", "header", "main", "nav"];
2807
- var listContract = closedContract([
2808
- { name: "list-item", match: isTag("li", ...METADATA_TAGS) }
2809
- ]);
2810
- var tableContract = closedContract([
2811
- firstOptional("caption", "caption"),
2812
- { name: "colgroup", match: isTag("colgroup") },
2813
- { name: "thead", match: isTag("thead"), cardinality: { max: 1 } },
2814
- { name: "tbody", match: isTag("tbody") },
2815
- { name: "tfoot", match: isTag("tfoot"), cardinality: { max: 1 } },
2816
- { name: "table-row", match: isTag("tr", ...METADATA_TAGS) }
2817
- ]);
2818
- var tableBodyContract = closedContract([
2819
- { name: "table-row", match: isTag("tr", ...METADATA_TAGS) }
2820
- ]);
2821
- var tableRowContract = closedContract([
2822
- { name: "table-cell", match: isTag("td", "th", ...METADATA_TAGS) }
2823
- ]);
2824
- var colgroupContract = closedContract([
2825
- { name: "column", match: isTag("col", "template") }
2901
+ function getChildProp(child, key) {
2902
+ if (!isVNodeLike(child)) return void 0;
2903
+ const { props } = child;
2904
+ if (!isObject(props)) return void 0;
2905
+ return props[key];
2906
+ }
2907
+
2908
+ // ../core/src/html/contracts/aria/landmarks.ts
2909
+ var landmarkContract = ariaContract([landmarkRoleRule, landmarkAccessibleNameRule]);
2910
+
2911
+ // ../core/src/html/contracts/aria/widgets.ts
2912
+ var dialogContract = ariaContract([requireAccessibleName]);
2913
+ var menuContract = ariaContract([requireAccessibleName]);
2914
+ var menubarContract = ariaContract([requireAccessibleName]);
2915
+ var treeContract = ariaContract([requireAccessibleName]);
2916
+ var gridContract = ariaContract([requireAccessibleName]);
2917
+ var listboxContract = ariaContract([requireAccessibleName]);
2918
+ var tablistContract = ariaContract([requireAccessibleName]);
2919
+ var radiogroupContract = ariaContract([requireAccessibleName]);
2920
+
2921
+ // ../core/src/html/contracts/html/document.ts
2922
+ var headContract = closedContract([
2923
+ {
2924
+ name: "metadata",
2925
+ match: isTag("base", "link", "meta", "noscript", "script", "style", "template", "title")
2926
+ }
2826
2927
  ]);
2827
- var dlContract = closedContract([
2828
- { name: "term", match: isTag("dt") },
2829
- { name: "description", match: isTag("dd") },
2830
- { name: "group", match: isTag("div") },
2831
- metadata()
2928
+ var htmlContract = closedContract([
2929
+ { name: "head", match: isTag("head"), cardinality: { min: 1, max: 1 }, position: "first" },
2930
+ { name: "body", match: isTag("body"), cardinality: { min: 1, max: 1 } }
2832
2931
  ]);
2932
+ var voidContract = contract([], { exclusiveChildren: true, allowText: false });
2933
+ var textOnlyContract = contract([], { exclusiveChildren: true });
2934
+
2935
+ // ../core/src/html/contracts/html/forms.ts
2833
2936
  var selectContract = closedContract([
2834
2937
  { name: "option", match: isTag("option", "optgroup", "hr", ...METADATA_TAGS) }
2835
2938
  ]);
@@ -2839,6 +2942,59 @@ var optgroupContract = closedContract([
2839
2942
  var datalistContract = closedContract([
2840
2943
  { name: "option", match: isTag("option", ...METADATA_TAGS) }
2841
2944
  ]);
2945
+ var buttonContract = closedContract([
2946
+ { name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
2947
+ ]);
2948
+ var anchorContract = closedContract([
2949
+ { name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
2950
+ ]);
2951
+ var labelableTagMatch = isTag(...LABELABLE_TAGS);
2952
+ function isLabelableControl(child) {
2953
+ if (!labelableTagMatch(child)) return false;
2954
+ if (getTag(child) !== "input") return true;
2955
+ const type = getChildProp(child, "type");
2956
+ return !isString(type) || type !== "hidden";
2957
+ }
2958
+ function hasAccessibleNameProp(props) {
2959
+ return "aria-label" in props || "aria-labelledby" in props;
2960
+ }
2961
+ function isNonEmptyTextChild(child) {
2962
+ if (isNumber(child)) return true;
2963
+ return isString(child) && child.trim().length > 0;
2964
+ }
2965
+ var labelContract = contract([
2966
+ { name: "control", match: isLabelableControl, cardinality: { max: 1 } },
2967
+ { name: "nested-label", match: isTag("label"), cardinality: { max: 0 } },
2968
+ {
2969
+ name: "other-interactive-content",
2970
+ match: isTag(...OTHER_INTERACTIVE_TAGS),
2971
+ cardinality: { max: 0 }
2972
+ },
2973
+ {
2974
+ name: "accessible-name",
2975
+ match: isNonEmptyTextChild,
2976
+ cardinality: dynamic(
2977
+ (ctx) => hasAccessibleNameProp(ctx.props) ? { min: 0 } : { min: 1 }
2978
+ )
2979
+ }
2980
+ ]);
2981
+
2982
+ // ../core/src/html/contracts/html/grouping.ts
2983
+ var detailsContract = firstChildContract("summary", "summary");
2984
+ var fieldsetContract = firstChildContract("legend", "legend");
2985
+
2986
+ // ../core/src/html/contracts/html/lists.ts
2987
+ var listContract = closedContract([
2988
+ { name: "list-item", match: isTag("li", ...METADATA_TAGS) }
2989
+ ]);
2990
+ var dlContract = closedContract([
2991
+ { name: "term", match: isTag("dt") },
2992
+ { name: "description", match: isTag("dd") },
2993
+ { name: "group", match: isTag("div") },
2994
+ metadata()
2995
+ ]);
2996
+
2997
+ // ../core/src/html/contracts/html/media.ts
2842
2998
  var pictureContract = closedContract([
2843
2999
  { name: "source", match: isTag("source", ...METADATA_TAGS) },
2844
3000
  { name: "image", match: isTag("img"), cardinality: { min: 1, max: 1 }, position: "last" }
@@ -2847,91 +3003,49 @@ var figureContract = contract([
2847
3003
  { name: "caption", match: isTag("figcaption"), cardinality: { max: 1 } },
2848
3004
  { name: "content", match: isOpenContent("figcaption") }
2849
3005
  ]);
2850
- var detailsContract = firstChildContract("summary", "summary");
2851
- var fieldsetContract = firstChildContract("legend", "legend");
2852
3006
  var objectContract = contract([
2853
3007
  { name: "param", match: isTag("param") },
2854
3008
  { name: "content", match: isOpenContent("param") }
2855
3009
  ]);
2856
- var INTERACTIVE_CONTENT_TAGS = ["a", "button", "input", "select", "textarea", "label"];
2857
- var buttonContract = closedContract([
2858
- { name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
2859
- ]);
2860
- var anchorContract = closedContract([
2861
- { name: "content", match: isOpenContent(...INTERACTIVE_CONTENT_TAGS) }
2862
- ]);
2863
- var LABELABLE_TAGS = [
2864
- "button",
2865
- "input",
2866
- "meter",
2867
- "output",
2868
- "progress",
2869
- "select",
2870
- "textarea"
2871
- ];
2872
- var labelContract = contract([
2873
- { name: "control", match: isTag(...LABELABLE_TAGS), cardinality: { max: 1 } }
2874
- ]);
2875
- var P_BLOCKED_TAGS = [
2876
- "address",
2877
- "article",
2878
- "aside",
2879
- "blockquote",
2880
- "details",
2881
- "dialog",
2882
- "div",
2883
- "dl",
2884
- "fieldset",
2885
- "figure",
2886
- "footer",
2887
- "form",
2888
- "h1",
2889
- "h2",
2890
- "h3",
2891
- "h4",
2892
- "h5",
2893
- "h6",
2894
- "header",
2895
- "hr",
2896
- "main",
2897
- "nav",
2898
- "ol",
2899
- "p",
2900
- "pre",
2901
- "section",
2902
- "table",
2903
- "ul"
2904
- ];
2905
- var pContract = closedContract([
2906
- { name: "content", match: isOpenContent(...P_BLOCKED_TAGS) }
2907
- ]);
2908
3010
  var mediaContract = contract([
2909
3011
  { name: "source", match: isTag("source") },
2910
3012
  { name: "track", match: isTag("track") },
2911
3013
  metadata(),
2912
3014
  { name: "content", match: isOpenContent("source", "track", ...METADATA_TAGS) }
2913
3015
  ]);
2914
- var headContract = closedContract([
2915
- {
2916
- name: "metadata",
2917
- match: isTag("base", "link", "meta", "noscript", "script", "style", "template", "title")
2918
- }
3016
+
3017
+ // ../core/src/html/contracts/html/tables.ts
3018
+ var tableContract = closedContract([
3019
+ firstOptional("caption", "caption"),
3020
+ { name: "colgroup", match: isTag("colgroup") },
3021
+ { name: "thead", match: isTag("thead"), cardinality: { max: 1 } },
3022
+ { name: "tbody", match: isTag("tbody") },
3023
+ { name: "tfoot", match: isTag("tfoot"), cardinality: { max: 1 } },
3024
+ { name: "table-row", match: isTag("tr", ...METADATA_TAGS) }
2919
3025
  ]);
2920
- var htmlContract = closedContract([
2921
- { name: "head", match: isTag("head"), cardinality: { min: 1, max: 1 }, position: "first" },
2922
- { name: "body", match: isTag("body"), cardinality: { min: 1, max: 1 } }
3026
+ var tableBodyContract = closedContract([
3027
+ { name: "table-row", match: isTag("tr", ...METADATA_TAGS) }
2923
3028
  ]);
2924
- var voidContract = contract([], { exclusiveChildren: true, allowText: false });
2925
- var textOnlyContract = contract([], { exclusiveChildren: true });
2926
- var landmarkContract = ariaContract([landmarkRoleRule, landmarkNameAdvisory]);
2927
- var dialogContract = ariaContract([requireAccessibleName]);
2928
- var menuContract = ariaContract([requireAccessibleName]);
2929
- var menubarContract = ariaContract([requireAccessibleName]);
2930
- var treeContract = ariaContract([requireAccessibleName]);
2931
- var gridContract = ariaContract([requireAccessibleName]);
2932
- var listboxContract = ariaContract([requireAccessibleName]);
2933
- var tablistContract = ariaContract([requireAccessibleName]);
2934
- var radiogroupContract = ariaContract([requireAccessibleName]);
3029
+ var tableRowContract = closedContract([
3030
+ { name: "table-cell", match: isTag("td", "th", ...METADATA_TAGS) }
3031
+ ]);
3032
+ var colgroupContract = closedContract([
3033
+ { name: "column", match: isTag("col", "template") }
3034
+ ]);
3035
+
3036
+ // ../core/src/html/contracts/html/text.ts
3037
+ var pContract = closedContract([
3038
+ { name: "content", match: isOpenContent(...P_BLOCKED_TAGS) }
3039
+ ]);
3040
+
3041
+ // ../core/src/html/contracts/build-map.ts
3042
+ function buildMap(groups) {
3043
+ return Object.fromEntries(
3044
+ groups.flatMap(([keys2, value]) => keys2.map((key) => [key, value]))
3045
+ );
3046
+ }
3047
+
3048
+ // ../core/src/html/contracts/maps.ts
2935
3049
  var CONTRACT_GROUPS = [
2936
3050
  [VOID_TAGS, voidContract],
2937
3051
  [TEXT_ONLY_TAGS, textOnlyContract],
@@ -2940,13 +3054,8 @@ var CONTRACT_GROUPS = [
2940
3054
  [["audio", "video"], mediaContract],
2941
3055
  [["thead", "tbody", "tfoot"], tableBodyContract]
2942
3056
  ];
2943
- function contractMap(groups) {
2944
- return Object.fromEntries(
2945
- groups.flatMap(([tags, enforcement]) => tags.map((tag) => [tag, enforcement]))
2946
- );
2947
- }
2948
3057
  var htmlContracts = {
2949
- ...contractMap(CONTRACT_GROUPS),
3058
+ ...buildMap(CONTRACT_GROUPS),
2950
3059
  table: tableContract,
2951
3060
  tr: tableRowContract,
2952
3061
  colgroup: colgroupContract,
@@ -3741,7 +3850,10 @@ function render({
3741
3850
  tag: state.tag,
3742
3851
  props: state.normalizedProps
3743
3852
  });
3744
- runtime.options.htmlChildrenEvaluatorFn?.(state.tag)?.evaluate(getNormalizedChildren());
3853
+ runtime.options.htmlChildrenEvaluatorFn?.(state.tag)?.evaluate(getNormalizedChildren(), {
3854
+ tag: state.tag,
3855
+ props: state.normalizedProps
3856
+ });
3745
3857
  }
3746
3858
  if (typeof props.render === "function") {
3747
3859
  return props.render({ ...state.props, className: state.className, ref });
@@ -177,8 +177,8 @@ type AriaContext = {
177
177
  readonly props: ReadonlyDeep<IntrinsicProps>;
178
178
  };
179
179
 
180
- type RemoveAttributeFixKind = `removeAttribute:${string}`;
181
- type InjectLiveFixKind = `injectLive:${string}`;
180
+ type RemoveAttributeFixKind = 'removeAttribute';
181
+ type InjectLiveFixKind = 'injectLive';
182
182
  type FixKind = 'removeRole' | 'setRole' | 'normalizeRelevantAll' | RemoveAttributeFixKind | InjectLiveFixKind;
183
183
 
184
184
  type AriaFixResult = {
@@ -191,6 +191,9 @@ type AriaFixResult = {
191
191
  };
192
192
  type AriaFix = {
193
193
  readonly kind: FixKind;
194
+ /** The attribute a `'removeAttribute'`/`'injectLive'` fix targets — always set for those
195
+ * kinds, absent for kinds with no single-attribute target (`'removeRole'`, etc.). */
196
+ readonly attribute?: string;
194
197
  readonly priority?: number;
195
198
  readonly source?: string;
196
199
  readonly apply: (context: AriaContext) => AriaFixResult;
@@ -287,6 +290,56 @@ type FactoryOptions<TDefault extends ElementType = ElementType, Props extends An
287
290
  readonly diagnostics?: Diagnostics$1;
288
291
  };
289
292
 
293
+ /** Shared input shape for `invalidWithFix`/`invalidWithoutFix`. */
294
+ type InvalidResultInput = {
295
+ readonly severity: Severity;
296
+ readonly attribute?: string;
297
+ readonly message?: string;
298
+ readonly diagnostic?: DiagnosticInput;
299
+ };
300
+
301
+ /** Options for `createRemoveAttributeRule`. */
302
+ type RemoveAttributeRuleOptions = {
303
+ /** Returns true when `attribute` should be stripped for the given render. */
304
+ readonly when: (context: AriaContext) => boolean;
305
+ readonly severity?: Severity;
306
+ readonly message?: string;
307
+ /** Receives the same context `when` did, so the diagnostic can reference the offending value. */
308
+ readonly diagnostic?: (context: AriaContext) => DiagnosticInput;
309
+ readonly readsProps?: readonly string[];
310
+ readonly tags?: readonly string[];
311
+ };
312
+
313
+ /**
314
+ * Builds a correctly-literal-typed `fixable: false` `AriaResult`. Exists so a rule author can
315
+ * extract shared branch logic (severity/attribute/message computed once, reused across multiple
316
+ * `return`s) without TypeScript silently widening `valid: false`/`fixable: false` to `boolean`
317
+ * the moment those values leave an object-literal-in-return-position — the widening only happens
318
+ * on plain object literals; a function's declared return type narrows unconditionally.
319
+ */
320
+ declare function invalidWithoutFix(input: InvalidResultInput): AriaInvalidWithoutFix;
321
+ /** Same as {@link invalidWithoutFix}, for the `fixable: true` branch — requires a `fix`. */
322
+ declare function invalidWithFix(input: InvalidResultInput & {
323
+ readonly fix: AriaFix;
324
+ }): AriaInvalidWithFix;
325
+ /**
326
+ * Builds an `AriaFix` that strips a single attribute — the shape `dangerousHrefRule`-style
327
+ * "strip this attribute when it's dangerous/redundant" rules need. A no-op (`applied: false`) when
328
+ * the attribute isn't present, so applying the fix twice (or applying it when nothing triggered it)
329
+ * is always safe. Frozen — a fix is a value object; nothing should mutate `kind`/`attribute`/`apply`
330
+ * after construction.
331
+ */
332
+ declare function removeAttributeFix(attribute: string): AriaFix;
333
+ /**
334
+ * Convenience factory for the single most common `enforcement.aria`/`enforcement.rules` shape:
335
+ * "strip this attribute when some condition on the element's own props holds" — covers
336
+ * security-style guards (a dangerous URL scheme on `href`) and redundant-attribute rules alike,
337
+ * without hand-writing the rule function, the `AriaFix`, and the `invalidWithFix` call each time.
338
+ * A rule with no fix (a warn-only advisory) still needs the raw `AriaRule` shape directly — this
339
+ * factory is deliberately scoped to the strip-on-match case, not a general rule builder.
340
+ */
341
+ declare function createRemoveAttributeRule(attribute: string, options: RemoveAttributeRuleOptions): AriaRule;
342
+
290
343
  declare const activeProps: PropNormalizer;
291
344
 
292
345
  declare const disabledProps: PropNormalizer;
@@ -316,4 +369,4 @@ declare function mergeContracts(...contracts: readonly EnforcementOptions[]): En
316
369
 
317
370
  type Diagnostics = Diagnostics$1;
318
371
 
319
- export { type AnyFactoryOptions, type AriaContext, type AriaFix, type AriaFixResult, type AriaPhase, type AriaResult, type AriaRule, type Diagnostics, type FixKind, type AriaInvalidResult as InvalidResult, type AriaInvalidWithFix as InvalidWithFix, type AriaInvalidWithoutFix as InvalidWithoutFix, type PropNormalizer, type RemoveAttributeFixKind, type Severity, type ValidResult, activeContract, activeProps, disabledContract, disabledProps, expandedContract, expandedProps, invalidContract, invalidProps, loadingContract, loadingProps, mergeContracts, pressedContract, pressedProps, readonlyContract, readonlyProps, selectedContract, selectedProps };
372
+ export { type AnyFactoryOptions, type AriaContext, type AriaFix, type AriaFixResult, type AriaPhase, type AriaResult, type AriaRule, type Diagnostics, type FixKind, type IntrinsicProps, type AriaInvalidResult as InvalidResult, type AriaInvalidWithFix as InvalidWithFix, type AriaInvalidWithoutFix as InvalidWithoutFix, type NormalizeFn, type PropNormalizer, type RemoveAttributeFixKind, type Severity, type ValidResult, activeContract, activeProps, createRemoveAttributeRule, disabledContract, disabledProps, expandedContract, expandedProps, invalidContract, invalidProps, invalidWithFix, invalidWithoutFix, loadingContract, loadingProps, mergeContracts, pressedContract, pressedProps, readonlyContract, readonlyProps, removeAttributeFix, selectedContract, selectedProps };