praxis-kit 6.1.0 → 6.1.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
@@ -4,7 +4,20 @@ import { computed, defineComponent as defineComponent2 } from "vue";
4
4
  // ../../lib/primitive/src/guards/children/component-id.ts
5
5
  var COMPONENT_DEFAULT_TAG = /* @__PURE__ */ Symbol.for("praxis.component-default-tag");
6
6
 
7
- // ../../lib/primitive/src/utils/is-object.ts
7
+ // ../../lib/primitive/src/guards/foundational/is-defined.ts
8
+ function isUndefined(value) {
9
+ return value === void 0;
10
+ }
11
+
12
+ // ../../lib/primitive/src/guards/foundational/is-null.ts
13
+ function isNull(value) {
14
+ return value === null;
15
+ }
16
+ function isNonNull(value) {
17
+ return value != null;
18
+ }
19
+
20
+ // ../../lib/primitive/src/utils/type-guards.ts
8
21
  function isObject(value, excludeArrays = false) {
9
22
  if (value === null || typeof value !== "object") return false;
10
23
  return excludeArrays ? !Array.isArray(value) : true;
@@ -16,16 +29,6 @@ function isNumber(value) {
16
29
  return typeof value === "number";
17
30
  }
18
31
 
19
- // ../../lib/primitive/src/guards/foundational/is-defined.ts
20
- function isUndefined(value) {
21
- return value === void 0;
22
- }
23
-
24
- // ../../lib/primitive/src/guards/foundational/is-null.ts
25
- function isNull(value) {
26
- return value === null;
27
- }
28
-
29
32
  // ../../lib/primitive/src/guards/children/is-tag.ts
30
33
  function getAsProp(child) {
31
34
  if (!isObject(child) || !("props" in child)) return void 0;
@@ -1076,7 +1079,7 @@ var InvariantBase = class {
1076
1079
  };
1077
1080
 
1078
1081
  // ../../lib/contract/src/aria/polymorphic-validator.ts
1079
- var VALID = [{ valid: true }];
1082
+ var NO_VIOLATIONS = [{ valid: true }];
1080
1083
  function isIntrinsicTag(tag) {
1081
1084
  return isString(tag);
1082
1085
  }
@@ -1118,17 +1121,20 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1118
1121
  static #deriveContext(tag, props) {
1119
1122
  if (!isIntrinsicTag(tag)) return { proceed: false, result: { props, violations: [] } };
1120
1123
  const implicitRole = getImplicitRole(tag, props);
1121
- const hasRole2 = implicitRole != null || isString(props.role) && props.role.length > 0;
1124
+ const hasRole2 = isNonNull(implicitRole) || isString(props.role) && props.role.length > 0;
1122
1125
  if (!hasRole2) return { proceed: false, result: { props, violations: [] } };
1123
1126
  const normalized = _AriaPolicyEngine.#normalizeEmptyRole(tag, props);
1124
- if (normalized.normalized) return { proceed: false, result: normalized.result };
1125
- const effectiveRole = props.role ?? implicitRole;
1127
+ const workingProps = normalized.normalized ? normalized.result.props : props;
1128
+ const preExistingViolations = normalized.normalized ? normalized.result.violations : [];
1129
+ const effectiveRole = workingProps.role ?? implicitRole;
1126
1130
  return {
1127
1131
  proceed: true,
1128
1132
  tag,
1129
1133
  implicitRole,
1130
1134
  effectiveRole,
1131
- context: { tag, props, implicitRole, effectiveRole }
1135
+ props: workingProps,
1136
+ preExistingViolations,
1137
+ context: { tag, props: workingProps, implicitRole, effectiveRole }
1132
1138
  };
1133
1139
  }
1134
1140
  static #runRules(rules, context) {
@@ -1143,7 +1149,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1143
1149
  } = context;
1144
1150
  const { message, attribute, severity } = result;
1145
1151
  const resolvedMessage = message ?? result.diagnostic?.message;
1146
- const fallbackDiag = resolvedMessage == null ? AriaDiagnostics.invalidRole(role, tag) : void 0;
1152
+ const fallbackDiag = isNonNull(resolvedMessage) ? void 0 : AriaDiagnostics.invalidRole(role, tag);
1147
1153
  violations.push({
1148
1154
  message: resolvedMessage ?? fallbackDiag.message,
1149
1155
  tag,
@@ -1151,8 +1157,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1151
1157
  attribute,
1152
1158
  severity,
1153
1159
  phase: "evaluate",
1154
- ...result.diagnostic != null && { diagnostic: result.diagnostic },
1155
- ...fallbackDiag != null && { diagnostic: fallbackDiag }
1160
+ ...isNonNull(result.diagnostic) && { diagnostic: result.diagnostic },
1161
+ ...isNonNull(fallbackDiag) && { diagnostic: fallbackDiag }
1156
1162
  });
1157
1163
  if (result.fixable) fixes.push(result.fix);
1158
1164
  });
@@ -1160,7 +1166,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1160
1166
  return { violations, fixes };
1161
1167
  }
1162
1168
  static #getRules(context) {
1163
- if (_AriaPolicyEngine.#hasRole(context.props) || context.effectiveRole != null && _AriaPolicyEngine.#LIVE_REGION_ROLES.has(context.effectiveRole)) {
1169
+ if (_AriaPolicyEngine.#hasRole(context.props) || isNonNull(context.effectiveRole) && _AriaPolicyEngine.#LIVE_REGION_ROLES.has(context.effectiveRole)) {
1164
1170
  return _AriaPolicyEngine.#pipeline;
1165
1171
  }
1166
1172
  return _AriaPolicyEngine.#implicitOnlyRules;
@@ -1168,24 +1174,36 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1168
1174
  static evaluate(tag, props) {
1169
1175
  const derived = _AriaPolicyEngine.#deriveContext(tag, props);
1170
1176
  if (!derived.proceed) return derived.result;
1171
- const { tag: narrowedTag, implicitRole, context } = derived;
1177
+ const {
1178
+ tag: narrowedTag,
1179
+ implicitRole,
1180
+ context,
1181
+ props: workingProps,
1182
+ preExistingViolations
1183
+ } = derived;
1172
1184
  const { violations, fixes } = _AriaPolicyEngine.#runRules(
1173
1185
  _AriaPolicyEngine.#getRules(context),
1174
1186
  context
1175
1187
  );
1176
- const next = _AriaPolicyEngine.#applyFixes(narrowedTag, implicitRole, props, fixes);
1177
- return { props: next, violations };
1188
+ const next = _AriaPolicyEngine.#applyFixes(narrowedTag, implicitRole, workingProps, fixes);
1189
+ return { props: next, violations: [...preExistingViolations, ...violations] };
1178
1190
  }
1179
1191
  static #evaluateWithRules(tag, props, extraRules) {
1180
1192
  const derived = _AriaPolicyEngine.#deriveContext(tag, props);
1181
1193
  if (!derived.proceed) return derived.result;
1182
- const { tag: narrowedTag, implicitRole, context } = derived;
1194
+ const {
1195
+ tag: narrowedTag,
1196
+ implicitRole,
1197
+ context,
1198
+ props: workingProps,
1199
+ preExistingViolations
1200
+ } = derived;
1183
1201
  const { violations, fixes } = _AriaPolicyEngine.#runRules(
1184
1202
  [..._AriaPolicyEngine.#getRules(context), ...extraRules],
1185
1203
  context
1186
1204
  );
1187
- const next = _AriaPolicyEngine.#applyFixes(narrowedTag, implicitRole, props, fixes);
1188
- return { props: next, violations };
1205
+ const next = _AriaPolicyEngine.#applyFixes(narrowedTag, implicitRole, workingProps, fixes);
1206
+ return { props: next, violations: [...preExistingViolations, ...violations] };
1189
1207
  }
1190
1208
  report(violations) {
1191
1209
  iterate.forEach(violations, (v) => {
@@ -1194,12 +1212,12 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1194
1212
  else this.warn(d);
1195
1213
  });
1196
1214
  }
1197
- // Cache key covers only the aria-relevant subset of props (tag + role + aria-* attrs).
1198
- // Non-aria props (className, onClick, etc.) do not affect ARIA decisions and are excluded
1199
- // so cache hits survive re-renders that only change non-aria props.
1200
- // Note: #extraRules are NOT included in the key each engine instance has its own Map,
1201
- // so two engines with different rules never share cache entries. If caching ever becomes
1202
- // static/shared, rule identity would need to be folded into the key.
1215
+ // Cache key covers only the aria-relevant subset of props (tag + role + aria-* attrs)
1216
+ // exactly what the built-in pipeline reads. Non-aria props (className, onClick, etc.) do
1217
+ // not affect built-in ARIA decisions and are excluded so cache hits survive re-renders
1218
+ // that only change non-aria props. This key alone is unsound for #extraRules, which may
1219
+ // read arbitrary props outside this set validate() extends it with #extraRulesKeySuffix,
1220
+ // or bypasses the cache, to account for that.
1203
1221
  static #createPlanKey(tag, props) {
1204
1222
  if (!isIntrinsicTag(tag)) return null;
1205
1223
  const parts = [tag];
@@ -1215,6 +1233,23 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1215
1233
  if (ariaEntries.length > 0) parts.push(...ariaEntries.sort());
1216
1234
  return parts.join("|");
1217
1235
  }
1236
+ // Extends the base cache key with the props each extra rule declares it reads (`readsProps`).
1237
+ // Returns null — meaning "don't cache" — if any extra rule omits `readsProps` (it may read
1238
+ // arbitrary props the key can't account for) or if a declared prop's value isn't a primitive
1239
+ // (object/array identity isn't stably representable in a string key).
1240
+ static #extraRulesKeySuffix(extraRules, props) {
1241
+ const parts = [];
1242
+ for (const rule of extraRules) {
1243
+ const readsProps = rule.readsProps;
1244
+ if (!isNonNull(readsProps)) return null;
1245
+ for (const propKey of readsProps) {
1246
+ const v = props[propKey];
1247
+ if (v !== void 0 && !isString(v) && !isNumber(v) && typeof v !== "boolean") return null;
1248
+ parts.push(`x:${propKey}:${String(v)}`);
1249
+ }
1250
+ }
1251
+ return parts.sort().join("|");
1252
+ }
1218
1253
  static #computePlan(inputProps, resultProps) {
1219
1254
  const removals = /* @__PURE__ */ new Set();
1220
1255
  const updates = {};
@@ -1238,7 +1273,12 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1238
1273
  return next;
1239
1274
  }
1240
1275
  validate(tag, props) {
1241
- const key = _AriaPolicyEngine.#createPlanKey(tag, props);
1276
+ const baseKey = _AriaPolicyEngine.#createPlanKey(tag, props);
1277
+ let key = baseKey;
1278
+ if (this.#extraRules.length > 0) {
1279
+ const suffix = _AriaPolicyEngine.#extraRulesKeySuffix(this.#extraRules, props);
1280
+ key = isNonNull(baseKey) && isNonNull(suffix) ? `${baseKey}|${suffix}` : null;
1281
+ }
1242
1282
  if (!isNull(key)) {
1243
1283
  const cached = this.#planCache.get(key);
1244
1284
  if (cached !== void 0) {
@@ -1328,7 +1368,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1328
1368
  implicitRole
1329
1369
  }) {
1330
1370
  const role = props.role;
1331
- if (!implicitRole || !role || role === implicitRole) return VALID;
1371
+ if (!implicitRole || !role || role === implicitRole) return NO_VIOLATIONS;
1332
1372
  if (isStrongImplicitRole(tag) && role === "region") {
1333
1373
  return [
1334
1374
  {
@@ -1340,11 +1380,11 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1340
1380
  }
1341
1381
  ];
1342
1382
  }
1343
- return VALID;
1383
+ return NO_VIOLATIONS;
1344
1384
  }
1345
1385
  static #checkRedundantRole({ tag, props, implicitRole }) {
1346
1386
  const role = props.role;
1347
- if (!implicitRole || !role || role !== implicitRole) return VALID;
1387
+ if (!implicitRole || !role || role !== implicitRole) return NO_VIOLATIONS;
1348
1388
  return [
1349
1389
  {
1350
1390
  valid: false,
@@ -1357,8 +1397,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1357
1397
  }
1358
1398
  static #checkStandaloneRegion({ tag, props, implicitRole }) {
1359
1399
  const role = props.role;
1360
- if (role !== "region") return VALID;
1361
- if (!isStandaloneTag(tag)) return VALID;
1400
+ if (role !== "region") return NO_VIOLATIONS;
1401
+ if (!isStandaloneTag(tag)) return NO_VIOLATIONS;
1362
1402
  return [
1363
1403
  {
1364
1404
  valid: false,
@@ -1374,7 +1414,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1374
1414
  props,
1375
1415
  effectiveRole
1376
1416
  }) {
1377
- if (effectiveRole === "none" || effectiveRole === "presentation") return VALID;
1417
+ if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS;
1378
1418
  const results = [];
1379
1419
  iterate.forEachEntry(props, (key) => {
1380
1420
  if (!key.startsWith("aria-")) return;
@@ -1492,12 +1532,12 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1492
1532
  }
1493
1533
  }
1494
1534
  static #checkAriaAttributeValues({ props, effectiveRole }) {
1495
- if (effectiveRole === "none" || effectiveRole === "presentation") return VALID;
1535
+ if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS;
1496
1536
  const results = [];
1497
1537
  iterate.forEachEntry(props, (key, value) => {
1498
1538
  if (!key.startsWith("aria-")) return;
1499
1539
  const type = _AriaPolicyEngine.#ARIA_VALUE_TYPES.get(key);
1500
- if (type == null) return;
1540
+ if (!isNonNull(type)) return;
1501
1541
  if (_AriaPolicyEngine.#isValidAriaValue(value, type)) return;
1502
1542
  results.push({
1503
1543
  valid: false,
@@ -1528,13 +1568,13 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1528
1568
  props,
1529
1569
  effectiveRole
1530
1570
  }) {
1531
- if (effectiveRole === "none" || effectiveRole === "presentation") return VALID;
1571
+ if (effectiveRole === "none" || effectiveRole === "presentation") return NO_VIOLATIONS;
1532
1572
  const implicitLevel = _AriaPolicyEngine.#HEADING_IMPLICIT_LEVELS.get(tag);
1533
- if (implicitLevel == null) return VALID;
1573
+ if (!isNonNull(implicitLevel)) return NO_VIOLATIONS;
1534
1574
  const raw = props["aria-level"];
1535
- if (raw == null) return VALID;
1575
+ if (!isNonNull(raw)) return NO_VIOLATIONS;
1536
1576
  const n = typeof raw === "number" ? raw : typeof raw === "string" ? parseInt(raw, 10) : NaN;
1537
- if (!Number.isFinite(n) || n !== implicitLevel) return VALID;
1577
+ if (!Number.isFinite(n) || n !== implicitLevel) return NO_VIOLATIONS;
1538
1578
  return [
1539
1579
  {
1540
1580
  valid: false,
@@ -1557,9 +1597,10 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1557
1597
  props,
1558
1598
  effectiveRole
1559
1599
  }) {
1560
- if (!effectiveRole || !_AriaPolicyEngine.#NAME_REQUIRED_ROLES.has(effectiveRole)) return VALID;
1561
- if ("aria-label" in props || "aria-labelledby" in props) return VALID;
1562
- if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return VALID;
1600
+ if (!effectiveRole || !_AriaPolicyEngine.#NAME_REQUIRED_ROLES.has(effectiveRole))
1601
+ return NO_VIOLATIONS;
1602
+ if ("aria-label" in props || "aria-labelledby" in props) return NO_VIOLATIONS;
1603
+ if (tag === "img" && typeof props.alt === "string" && props.alt.length > 0) return NO_VIOLATIONS;
1563
1604
  return [
1564
1605
  {
1565
1606
  valid: false,
@@ -1582,9 +1623,9 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1582
1623
  props,
1583
1624
  effectiveRole
1584
1625
  }) {
1585
- if (!effectiveRole) return VALID;
1626
+ if (!effectiveRole) return NO_VIOLATIONS;
1586
1627
  const required = _AriaPolicyEngine.#REQUIRED_PROPERTIES.get(effectiveRole);
1587
- if (required == null) return VALID;
1628
+ if (!isNonNull(required)) return NO_VIOLATIONS;
1588
1629
  const results = [];
1589
1630
  iterate.forEach(required, (attr) => {
1590
1631
  if (attr in props) return;
@@ -1608,12 +1649,12 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1608
1649
  ]);
1609
1650
  // WAI-ARIA 1.2 §6.6: aria-hidden="true" must not be placed on focusable elements.
1610
1651
  static #checkAriaHiddenOnFocusable({ tag, props }) {
1611
- if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return VALID;
1652
+ if (props["aria-hidden"] !== "true" && props["aria-hidden"] !== true) return NO_VIOLATIONS;
1612
1653
  const isInteractive = _AriaPolicyEngine.#INTERACTIVE_TAGS.has(tag);
1613
1654
  if (!isInteractive) {
1614
1655
  const tabindex = props.tabindex;
1615
1656
  const n = typeof tabindex === "number" ? tabindex : typeof tabindex === "string" ? parseInt(tabindex, 10) : NaN;
1616
- if (!Number.isFinite(n) || n < 0) return VALID;
1657
+ if (!Number.isFinite(n) || n < 0) return NO_VIOLATIONS;
1617
1658
  }
1618
1659
  return [
1619
1660
  {
@@ -1632,7 +1673,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1632
1673
  props,
1633
1674
  effectiveRole
1634
1675
  }) {
1635
- if (effectiveRole !== "none" && effectiveRole !== "presentation") return VALID;
1676
+ if (effectiveRole !== "none" && effectiveRole !== "presentation") return NO_VIOLATIONS;
1636
1677
  const results = [];
1637
1678
  iterate.forEachEntry(props, (key) => {
1638
1679
  if (!key.startsWith("aria-")) return;
@@ -1656,10 +1697,10 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1656
1697
  ["timer", "off"]
1657
1698
  ]);
1658
1699
  static #checkMissingLiveRegion({ effectiveRole, props }) {
1659
- if (!effectiveRole) return VALID;
1700
+ if (!effectiveRole) return NO_VIOLATIONS;
1660
1701
  const impliedLive = _AriaPolicyEngine.#LIVE_REGION_ROLES.get(effectiveRole);
1661
- if (!impliedLive) return VALID;
1662
- if ("aria-live" in props) return VALID;
1702
+ if (!impliedLive) return NO_VIOLATIONS;
1703
+ if ("aria-live" in props) return NO_VIOLATIONS;
1663
1704
  const injectLive = {
1664
1705
  kind: `injectLive:${effectiveRole}`,
1665
1706
  apply: (ctx) => ({
@@ -1679,8 +1720,9 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1679
1720
  ];
1680
1721
  }
1681
1722
  static #checkMissingAtomic({ effectiveRole, props }) {
1682
- if (!effectiveRole || !_AriaPolicyEngine.#LIVE_REGION_ROLES.has(effectiveRole)) return VALID;
1683
- if ("aria-atomic" in props) return VALID;
1723
+ if (!effectiveRole || !_AriaPolicyEngine.#LIVE_REGION_ROLES.has(effectiveRole))
1724
+ return NO_VIOLATIONS;
1725
+ if ("aria-atomic" in props) return NO_VIOLATIONS;
1684
1726
  return [
1685
1727
  {
1686
1728
  valid: false,
@@ -1704,8 +1746,8 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1704
1746
  };
1705
1747
  static #checkInvalidAriaRelevant({ props }) {
1706
1748
  const relevant = props["aria-relevant"];
1707
- if (relevant === void 0) return VALID;
1708
- if (typeof relevant !== "string") return VALID;
1749
+ if (relevant === void 0) return NO_VIOLATIONS;
1750
+ if (typeof relevant !== "string") return NO_VIOLATIONS;
1709
1751
  const tokens = relevant.trim().split(/\s+/);
1710
1752
  const invalid = tokens.filter((t) => !_AriaPolicyEngine.#VALID_RELEVANT_TOKENS.has(t));
1711
1753
  if (invalid.length > 0) {
@@ -1732,7 +1774,7 @@ var AriaPolicyEngine = class _AriaPolicyEngine extends InvariantBase {
1732
1774
  }
1733
1775
  ];
1734
1776
  }
1735
- return VALID;
1777
+ return NO_VIOLATIONS;
1736
1778
  }
1737
1779
  };
1738
1780
 
@@ -2433,7 +2475,7 @@ function createClassPipeline(resolved) {
2433
2475
  const staticClasses = staticResolver.resolve(tag, recipe !== void 0);
2434
2476
  const variantClasses = variantResolver.resolve({ props, recipe });
2435
2477
  if (!className)
2436
- return staticClasses && variantClasses ? `${staticClasses} ${variantClasses}` : staticClasses || variantClasses;
2478
+ return (staticClasses && variantClasses ? `${staticClasses} ${variantClasses}` : staticClasses || variantClasses) || void 0;
2437
2479
  return cn(staticClasses, variantClasses, className);
2438
2480
  };
2439
2481
  }
@@ -2644,7 +2686,7 @@ function createPolymorphic2(options = {}) {
2644
2686
  if (process.env.NODE_ENV !== "production") {
2645
2687
  validateRenderProps(resolved.diagnostics, resolved, props, recipe);
2646
2688
  }
2647
- return classPipeline(tag, props, className, recipe);
2689
+ return classPipeline(tag, props, className, recipe) || void 0;
2648
2690
  },
2649
2691
  resolveAria(tag, props) {
2650
2692
  return resolveAriaFn(tag, props);
@@ -2861,7 +2903,10 @@ function buildElementProps(props, className) {
2861
2903
  const { role, ...rest } = props;
2862
2904
  return {
2863
2905
  ...normalizeListenerKeys(rest),
2864
- class: className,
2906
+ // Vue's SSR renderer and its client-side patcher disagree on `class: undefined` — SSR's
2907
+ // normalizeClass turns it into '' and still emits class="", while the client patcher
2908
+ // removes the attribute outright. Omitting the key entirely keeps both paths consistent.
2909
+ ...className !== void 0 && { class: className },
2865
2910
  ...isKnownAriaRole(role) && { role }
2866
2911
  };
2867
2912
  }
@@ -2882,7 +2927,10 @@ function validateSlotDirectives(directives, validator) {
2882
2927
  function tryRenderAsChild(state, children, discarded, validator) {
2883
2928
  if (!validateSlotDirectives(state.directives, validator)) return null;
2884
2929
  if (discarded > 0) validator.warnDiscardedChildren(discarded);
2885
- const attrs = { ...pickAttributes(state.props), class: state.className };
2930
+ const attrs = {
2931
+ ...pickAttributes(state.props),
2932
+ ...state.className !== void 0 && { class: state.className }
2933
+ };
2886
2934
  const slottable = extractSlottable(children);
2887
2935
  if (slottable) return slottable.rebuild(cloneVNode(slottable.child, attrs));
2888
2936
  if (children.length === 1 && children[0] !== void 0) {
@@ -138,7 +138,7 @@ interface BaseClassOptions {
138
138
  baseClassName?: ClassName;
139
139
  }
140
140
 
141
- type ClassPipelineFn = (tag: unknown, props: AnyRecord, className?: ClassName, recipe?: string) => string;
141
+ type ClassPipelineFn = (tag: unknown, props: AnyRecord, className?: ClassName, recipe?: string) => string | undefined;
142
142
 
143
143
  interface RecipeOptions<TVariants extends VariantMap = VariantMap> {
144
144
  recipeMap?: Record<string, RecipeTarget<TVariants>>;
@@ -216,7 +216,9 @@ type AriaInvalidWithoutFix<M extends string = string> = AriaInvalidBase<M> & {
216
216
  type AriaInvalidResult<M extends string = string> = AriaInvalidWithFix<M> | AriaInvalidWithoutFix<M>;
217
217
  type AriaResult = ValidResult | AriaInvalidResult;
218
218
 
219
- type AriaRule<C extends AriaContext = AriaContext> = (context: C) => readonly AriaResult[];
219
+ type AriaRule<C extends AriaContext = AriaContext> = ((context: C) => readonly AriaResult[]) & {
220
+ readonly readsProps?: readonly string[];
221
+ };
220
222
 
221
223
  type PropNormalizer = (props: Readonly<AnyRecord & IntrinsicProps>) => Partial<AnyRecord & IntrinsicProps>;
222
224