@tenphi/tasty 2.11.1 → 3.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.
Files changed (76) hide show
  1. package/README.md +32 -7
  2. package/dist/astro-B53trJyX.js +156 -0
  3. package/dist/astro-B53trJyX.js.map +1 -0
  4. package/dist/babel-HlEpSZrj.d.ts +83 -0
  5. package/dist/{collector-DYA5AOwr.d.ts → collector-BPzZezAG.d.ts} +44 -7
  6. package/dist/{collector-AHZaBSv8.js → collector-D14DcZx9.js} +75 -14
  7. package/dist/collector-D14DcZx9.js.map +1 -0
  8. package/dist/{config-YDAcLaVf.js → config-CCcE_tqx.js} +2312 -1131
  9. package/dist/config-CCcE_tqx.js.map +1 -0
  10. package/dist/{config-BtK9fUaz.d.ts → config-D2INrjr_.d.ts} +252 -25
  11. package/dist/core/index.d.ts +5 -5
  12. package/dist/core/index.js +6 -6
  13. package/dist/{core-CS4bzqGu.js → core-B5d0IPi2.js} +85 -133
  14. package/dist/core-B5d0IPi2.js.map +1 -0
  15. package/dist/{css-writer-BXSANVrq.js → css-writer-B5DBoxhf.js} +54 -15
  16. package/dist/css-writer-B5DBoxhf.js.map +1 -0
  17. package/dist/{format-rules-CYriCDwq.js → format-rules-BbKDjsfO.js} +2 -2
  18. package/dist/{format-rules-CYriCDwq.js.map → format-rules-BbKDjsfO.js.map} +1 -1
  19. package/dist/{hydrate-GVTorHpU.js → hydrate-CwALW3J9.js} +3 -11
  20. package/dist/{hydrate-GVTorHpU.js.map → hydrate-CwALW3J9.js.map} +1 -1
  21. package/dist/{index-BYtnj_gA.d.ts → index-BAAbdfRs.d.ts} +228 -153
  22. package/dist/{index-B9ih23uv.d.ts → index-l_NdJSUS.d.ts} +172 -61
  23. package/dist/index.d.ts +5 -5
  24. package/dist/index.js +191 -56
  25. package/dist/index.js.map +1 -1
  26. package/dist/{keyframes-CtcSlw4k.js → keyframes-B8M1FmtE.js} +10 -181
  27. package/dist/keyframes-B8M1FmtE.js.map +1 -0
  28. package/dist/{merge-styles-D4ITH4bc.js → merge-styles--1zOSFlS.js} +2 -2
  29. package/dist/{merge-styles-D4ITH4bc.js.map → merge-styles--1zOSFlS.js.map} +1 -1
  30. package/dist/{merge-styles-BZV-XAwX.d.ts → merge-styles-NEvqayL_.d.ts} +2 -2
  31. package/dist/{resolve-recipes-llr0COs8.js → resolve-recipes-Caix5NnH.js} +3 -3
  32. package/dist/{resolve-recipes-llr0COs8.js.map → resolve-recipes-Caix5NnH.js.map} +1 -1
  33. package/dist/ssr/astro-client.js +1 -1
  34. package/dist/ssr/astro-middleware.js +1 -1
  35. package/dist/ssr/astro-middleware.js.map +1 -1
  36. package/dist/ssr/astro.d.ts +1 -5
  37. package/dist/ssr/astro.js +2 -147
  38. package/dist/ssr/index.d.ts +2 -9
  39. package/dist/ssr/index.js +4 -4
  40. package/dist/ssr/index.js.map +1 -1
  41. package/dist/ssr/next.d.ts +1 -1
  42. package/dist/ssr/next.js +5 -6
  43. package/dist/ssr/next.js.map +1 -1
  44. package/dist/static/index.d.ts +2 -2
  45. package/dist/static/index.js +1 -1
  46. package/dist/zero/babel.d.ts +2 -80
  47. package/dist/zero/babel.js +27 -23
  48. package/dist/zero/babel.js.map +1 -1
  49. package/dist/zero/index.d.ts +16 -2
  50. package/dist/zero/index.js +2 -2
  51. package/dist/zero/next.d.ts +1 -1
  52. package/dist/zero/next.js +1 -1
  53. package/dist/zero/next.js.map +1 -1
  54. package/docs/README.md +3 -1
  55. package/docs/ai-agents.md +21 -7
  56. package/docs/configuration.md +220 -30
  57. package/docs/debug.md +7 -7
  58. package/docs/design-system.md +2 -2
  59. package/docs/dsl.md +68 -8
  60. package/docs/injector.md +46 -4
  61. package/docs/methodology.md +1 -1
  62. package/docs/migration-v3.md +285 -0
  63. package/docs/pipeline.md +3 -3
  64. package/docs/plugins.md +349 -0
  65. package/docs/react-api.md +82 -3
  66. package/docs/ssr.md +40 -33
  67. package/docs/styles.md +27 -9
  68. package/docs/tasty-static.md +8 -6
  69. package/package.json +7 -7
  70. package/tasty.config.ts +4 -0
  71. package/dist/collector-AHZaBSv8.js.map +0 -1
  72. package/dist/config-YDAcLaVf.js.map +0 -1
  73. package/dist/core-CS4bzqGu.js.map +0 -1
  74. package/dist/css-writer-BXSANVrq.js.map +0 -1
  75. package/dist/keyframes-CtcSlw4k.js.map +0 -1
  76. package/dist/ssr/astro.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
- import { $ as parseColor, A as StyleInjector, B as styleHandlers, C as parseStateKey, Dt as hslToRgbValues, Et as hexToRgb, F as fontFaceContentHash, G as CUSTOM_UNITS, H as warn, I as formatFontFaceRule, J as filterMods, K as DIRECTIONS, M as formatCounterStyleRule, O as getGlobalPredefinedStates, Ot as strToRgb, Q as normalizeColorTokenValue, R as SheetManager, S as renderStyles, T as createStateParserContext, Tt as getRgbValuesFromRgbaString, V as deprecationWarning, X as getGlobalParser, Y as getGlobalFuncs, Z as getGlobalPredefinedTokens, a as getGlobalCounterStyle, at as okhstPlugin, c as getGlobalKeyframes, ct as StyleParser, d as getNamePrefix, dt as DEFAULT_ZERO_NAME_PREFIX, et as parseStyle, f as hasGlobalKeyframes, g as isTestEnvironment, h as isConfigLocked, it as okhstFunc, k as setGlobalPredefinedStates, l as getGlobalRecipes, lt as Bucket, m as hasStylesGenerated, mt as makeKeyframeName, n as getConfig, nt as setGlobalPredefinedTokens, o as getGlobalFontFace, ot as okhslFunc, p as hasGlobalRecipes, pt as makeCounterStyleName, q as customFunc, rt as stringifyStyles, s as getGlobalInjector, st as okhslPlugin, t as configure, tt as resetGlobalPredefinedTokens, ut as DEFAULT_NAME_PREFIX, v as resetConfig, vt as hashString, wt as getNamedColorHex, x as isSelector, y as generateTypographyTokens } from "./config-YDAcLaVf.js";
2
- import { _ as categorizeStyleKeys, d as DIMENSION_CHUNK_STYLES, f as DISPLAY_CHUNK_STYLES, g as STYLE_TO_CHUNK, h as POSITION_CHUNK_STYLES, l as APPEARANCE_CHUNK_STYLES, m as LAYOUT_CHUNK_STYLES, p as FONT_CHUNK_STYLES, u as CHUNK_NAMES } from "./keyframes-CtcSlw4k.js";
3
- import { A as property, B as DIMENSION_STYLES, C as getRawCSSText, D as injector, E as injectRawCSS, F as BLOCK_INNER_STYLES, G as TEXT_STYLES, H as INNER_STYLES, I as BLOCK_OUTER_STYLES, L as BLOCK_STYLES, M as ChunkSheetRegistry, N as chunkSheetRegistry, O as isPropertyDefined, P as BASE_STYLES, R as COLOR_STYLES, S as getIsTestEnvironment, T as injectGlobal, U as OUTER_STYLES, V as FLOW_STYLES, W as POSITION_STYLES, _ as destroy, a as color, b as getCssText, c as hasKeys, d as collectAutoInferredPropertiesRSC, f as getStyleTarget, g as createInjector, h as counterStyle, i as _modAttrs, j as touch, k as keyframes, l as formatKeyframesCSS, m as cleanup, n as processTokens, o as filterBaseProps, p as pushRSCCSS, r as dotize, s as computeStyles, t as tastyDebug, u as collectAutoInferredProperties, v as fontFace, w as inject, x as getCssTextForNode, y as gc, z as CONTAINER_STYLES } from "./core-CS4bzqGu.js";
4
- import { n as formatPropertyCSS } from "./format-rules-CYriCDwq.js";
5
- import { t as mergeStyles } from "./merge-styles-D4ITH4bc.js";
6
- import { t as resolveRecipes } from "./resolve-recipes-llr0COs8.js";
1
+ import { $ as SheetManager, $t as getNamedColorHex, A as FLOW_STYLES, At as registerFunctionPolyfill, B as createStateParserContext, Bt as stringifyStyles, C as BASE_STYLES, Ct as LAYOUT_CHUNK_STYLES, D as COLOR_STYLES, Dt as formatFunctionRule, E as BLOCK_STYLES, Ft as getGlobalParser, G as StyleInjector, Gt as createColorFunc, Ht as okhstPlugin, I as isSelector, It as getGlobalPredefinedTokens, L as renderStyles, Lt as normalizeColorTokenValue, M as OUTER_STYLES, Mt as CUSTOM_UNITS, N as POSITION_STYLES, Nt as DIRECTIONS, O as CONTAINER_STYLES, P as TEXT_STYLES, Pt as filterMods, R as parseStateKey, Rt as parseColor, S as baseStylePropsRegistry, St as FONT_CHUNK_STYLES, T as BLOCK_OUTER_STYLES, Tt as STYLE_TO_CHUNK, U as getGlobalPredefinedStates, Ut as okhslFunction, Vt as okhstFunction, W as setGlobalPredefinedStates, Wt as okhslPlugin, X as fontFaceContentHash, Z as formatFontFaceRule, _ as isFunctionsPolyfillEnabled, _t as propHandlerRegistry, a as getGlobalCounterStyles, at as DEFAULT_NAME_PREFIX, b as resetConfig, bt as DIMENSION_CHUNK_STYLES, c as getGlobalInjector, ct as makeCounterStyleName, en as getRgbValuesFromRgbaString, f as getNamePrefix, ft as hashString, g as isConfigLocked, h as hasStylesGenerated, in as resolveFunctionColor, j as INNER_STYLES, k as DIMENSION_STYLES, kt as parseFunctionName, l as getGlobalKeyframes, lt as makeKeyframeName, m as hasGlobalRecipes, n as getConfig, nn as hslToRgbValues, nt as styleHandlers, o as getGlobalFontFaces, ot as DEFAULT_ZERO_NAME_PREFIX, p as hasGlobalKeyframes, q as formatCounterStyleRule, qt as StyleParser, rn as strToRgb, s as getGlobalFunctions, t as configure, tn as hexToRgb, tt as defineHandler, u as getGlobalRecipes, v as isTestEnvironment, vt as APPEARANCE_CHUNK_STYLES, w as BLOCK_INNER_STYLES, wt as POSITION_CHUNK_STYLES, x as generateTypographyTokens, xt as DISPLAY_CHUNK_STYLES, yt as CHUNK_NAMES, zt as parseStyle } from "./config-CCcE_tqx.js";
2
+ import { l as categorizeStyleKeys } from "./keyframes-B8M1FmtE.js";
3
+ import { A as property, C as getRawCSSText, D as injector, E as injectRawCSS, M as ChunkSheetRegistry, N as chunkSheetRegistry, O as isPropertyDefined, S as getCSSTextForNode, T as injectGlobal, _ as destroy, a as color, b as gc, c as hasKeys, d as collectAutoInferredPropertiesRSC, f as getStyleTarget, g as createInjector, h as counterStyle, i as _modAttrs, j as touch, k as keyframes, l as formatKeyframesCSS, m as cleanup, n as processTokens, o as filterBaseProps, p as pushRSCCSS, r as dotize, s as computeStyles, t as tastyDebug, u as collectAutoInferredProperties, v as fontFace, w as inject, x as getCSSText, y as func } from "./core-B5d0IPi2.js";
4
+ import { n as formatPropertyCSS } from "./format-rules-BbKDjsfO.js";
5
+ import { t as mergeStyles } from "./merge-styles--1zOSFlS.js";
6
+ import { t as resolveRecipes } from "./resolve-recipes-Caix5NnH.js";
7
7
  import { t as getTastySSRContext } from "./context-CA8YKeMn.js";
8
8
  import { t as formatGlobalRules } from "./format-global-rules-DklyaXv-.js";
9
9
  import { Fragment, createElement, forwardRef, useContext } from "react";
@@ -106,7 +106,7 @@ function tasty(Component, options) {
106
106
  return tastyElement(Component);
107
107
  }
108
108
  function tastyWrap(Component, options) {
109
- const { as: extendTag, element: extendElement, ...defaultProps } = options ?? {};
109
+ const { as: extendTag, element: extendElement, styleProps: _styleProps, modProps: _modProps, tokenProps: _tokenProps, variants: _variants, elements: _elements, ...defaultProps } = options ?? {};
110
110
  const propsWithStyles = ["styles"].concat(Object.keys(defaultProps).filter((prop) => prop.endsWith("Styles")));
111
111
  const _WrappedComponent = forwardRef((props, ref) => {
112
112
  const { as, element, ...restProps } = props;
@@ -154,14 +154,22 @@ function tastyElement(tastyOptions) {
154
154
  if (!variantStylesMap["default"]) variantStylesMap["default"] = defaultStyles;
155
155
  }
156
156
  const { qa: defaultQa, qaVal: defaultQaVal, ...otherDefaultProps } = defaultProps ?? {};
157
- const propsToCheck = styleProps ? styleProps.concat(BASE_STYLES) : BASE_STYLES;
157
+ const ownPropsToCheck = styleProps ? styleProps.concat(BASE_STYLES) : BASE_STYLES;
158
+ let propsToCheck = ownPropsToCheck;
159
+ let propsToCheckVersion = -1;
158
160
  const modPropsKeys = modPropsDef ? Array.isArray(modPropsDef) ? modPropsDef : Object.keys(modPropsDef) : void 0;
159
161
  const tokenPropsMapping = tokenPropsDef ? buildTokenPropsMapping(tokenPropsDef) : void 0;
160
162
  const classNameCache = /* @__PURE__ */ new Map();
161
- const _TastyComponent = forwardRef((allProps, ref) => {
162
- const { as, styles: rawStyles, variant, mods, element, qa, qaVal, className: userClassName, tokens, style, ...otherProps } = allProps;
163
+ const _TastyComponent = forwardRef((incomingProps, ref) => {
164
+ const applyPropHandlers = propHandlerRegistry.apply;
165
+ const { as, styles: rawStyles, variant, mods, element, qa, qaVal, className: userClassName, tokens, style, theme, ...otherProps } = applyPropHandlers ? applyPropHandlers(incomingProps) : incomingProps;
163
166
  let styles = rawStyles;
164
167
  let propStyles = null;
168
+ if (propsToCheckVersion !== baseStylePropsRegistry.version) {
169
+ const promoted = baseStylePropsRegistry.list;
170
+ propsToCheck = promoted.length === 0 ? ownPropsToCheck : ownPropsToCheck.concat(promoted);
171
+ propsToCheckVersion = baseStylePropsRegistry.version;
172
+ }
165
173
  for (const prop of propsToCheck) {
166
174
  const key = prop;
167
175
  if (key in otherProps) {
@@ -228,6 +236,7 @@ function tastyElement(tastyOptions) {
228
236
  "data-element": element || defaultElement,
229
237
  "data-qa": qa || defaultQa,
230
238
  "data-qaval": qaVal || defaultQaVal,
239
+ "data-theme": theme,
231
240
  ...otherDefaultProps,
232
241
  ...modDataAttrs || {},
233
242
  ...otherProps,
@@ -236,6 +245,7 @@ function tastyElement(tastyOptions) {
236
245
  ref
237
246
  };
238
247
  handleIsProperties(elementProps);
248
+ if (elementProps["data-theme"] === void 0) delete elementProps["data-theme"];
239
249
  const el = createElement(as ?? originalAs, elementProps);
240
250
  if (stylesResult.css) {
241
251
  const nonce = getConfig().nonce;
@@ -287,8 +297,41 @@ function useStyles(styles, options) {
287
297
  });
288
298
  }
289
299
  //#endregion
300
+ //#region src/utils/client-state.ts
301
+ /**
302
+ * Build a per-(injector, root) client state cache for the standalone style
303
+ * functions (`useGlobalStyles`, `useRawCSS`, `useKeyframes`, `useCounterStyle`).
304
+ *
305
+ * Two levels, both weak:
306
+ *
307
+ * - **injector** — `configure()` replaces the global injector, and every dispose
308
+ * handle and generated name we cache belongs to the one that produced it.
309
+ * Keying by injector makes stale state fall away with it, instead of letting
310
+ * change-detection keys suppress re-injection into the new sheets.
311
+ * - **root** — the same selector or slot name can be used in several shadow
312
+ * roots, and each holds its own injection.
313
+ */
314
+ function createClientState(create) {
315
+ const byInjector = /* @__PURE__ */ new WeakMap();
316
+ return (root) => {
317
+ const injector = getGlobalInjector();
318
+ let byRoot = byInjector.get(injector);
319
+ if (!byRoot) {
320
+ byRoot = /* @__PURE__ */ new WeakMap();
321
+ byInjector.set(injector, byRoot);
322
+ }
323
+ let state = byRoot.get(root);
324
+ if (!state) {
325
+ state = create();
326
+ byRoot.set(root, state);
327
+ }
328
+ return state;
329
+ };
330
+ }
331
+ //#endregion
290
332
  //#region src/hooks/useGlobalStyles.ts
291
- const clientGlobalEntries = /* @__PURE__ */ new Map();
333
+ const getClientGlobalSlots = createClientState(() => /* @__PURE__ */ new Map());
334
+ const noop = () => {};
292
335
  /**
293
336
  * Inject global styles for a given selector.
294
337
  * Useful for styling elements by selector without generating classNames.
@@ -302,6 +345,10 @@ const clientGlobalEntries = /* @__PURE__ */ new Map();
302
345
  * Use the `id` option for update tracking when styles change over the
303
346
  * component lifecycle.
304
347
  *
348
+ * Update tracking is per-slot and per-root: a slot (`id`, or the selector when
349
+ * no `id` is given) holds exactly one injection per `root`. Changing the styles
350
+ * replaces it; rendering styles that produce no CSS clears it.
351
+ *
305
352
  * @param selector - CSS selector to apply styles to (e.g., '.my-class', ':root', 'body')
306
353
  * @param styles - Tasty styles object
307
354
  * @param options - Optional settings including `id` for update tracking
@@ -326,21 +373,29 @@ function useGlobalStyles(selector, styles, options) {
326
373
  return;
327
374
  }
328
375
  const target = getStyleTarget();
329
- if (target.mode === "client") {
330
- const slotKey = options?.id ?? selector;
331
- const stylesKey = JSON.stringify(styles);
332
- const existing = clientGlobalEntries.get(slotKey);
333
- if (existing && existing.stylesKey === stylesKey) return;
334
- }
376
+ const slots = target.mode === "client" ? getClientGlobalSlots(options?.root ?? document) : null;
377
+ const slotKey = options?.id ?? selector;
378
+ const stylesKey = slots ? JSON.stringify(styles) : "";
379
+ const existing = slots?.get(slotKey);
380
+ if (existing && existing.stylesKey === stylesKey) return;
335
381
  const resolvedStyles = resolveRecipes(styles);
336
382
  const styleResults = renderStyles(resolvedStyles, selector);
337
- if (styleResults.length === 0) return;
383
+ if (styleResults.length === 0) {
384
+ if (slots) {
385
+ existing?.dispose();
386
+ slots.set(slotKey, {
387
+ stylesKey,
388
+ dispose: noop
389
+ });
390
+ }
391
+ return;
392
+ }
338
393
  if (target.mode === "ssr") {
339
394
  target.collector.collectInternals();
340
395
  const css = formatGlobalRules(styleResults);
341
396
  if (css) {
342
397
  const key = options?.id ? `global:${options.id}` : `global:${selector}:${hashString(css)}`;
343
- target.collector.collectGlobalStyles(key, css);
398
+ target.collector.collectGlobalStyles(key, css, options?.id != null);
344
399
  }
345
400
  if (getConfig().autoPropertyTypes !== false) collectAutoInferredProperties(styleResults, target.collector, resolvedStyles);
346
401
  return;
@@ -349,19 +404,19 @@ function useGlobalStyles(selector, styles, options) {
349
404
  const css = formatGlobalRules(styleResults);
350
405
  if (css) {
351
406
  const key = options?.id ? `__global:${options.id}` : `__global:${selector}:${hashString(css)}`;
352
- pushRSCCSS(target.cache, key, css);
407
+ pushRSCCSS(target.cache, key, css, options?.id != null);
353
408
  }
354
409
  if (getConfig().autoPropertyTypes !== false) collectAutoInferredPropertiesRSC(styleResults, target.cache, resolvedStyles);
355
410
  return;
356
411
  }
357
- const slotKey = options?.id ?? selector;
358
- const existing = clientGlobalEntries.get(slotKey);
359
- if (existing) existing.dispose();
360
- const { dispose } = injectGlobal(styleResults, { root: options?.root });
361
- clientGlobalEntries.set(slotKey, {
362
- stylesKey: JSON.stringify(styles),
363
- dispose
364
- });
412
+ if (slots) {
413
+ existing?.dispose();
414
+ const { dispose } = injectGlobal(styleResults, { root: options?.root });
415
+ slots.set(slotKey, {
416
+ stylesKey,
417
+ dispose
418
+ });
419
+ }
365
420
  }
366
421
  //#endregion
367
422
  //#region src/utils/deps-equal.ts
@@ -377,9 +432,11 @@ function depsEqual(a, b) {
377
432
  }
378
433
  //#endregion
379
434
  //#region src/hooks/useRawCSS.ts
380
- const clientEntries = /* @__PURE__ */ new Map();
381
- const clientContentDedup = /* @__PURE__ */ new Set();
382
- const factoryDepsCache$1 = /* @__PURE__ */ new Map();
435
+ const getClientState$1 = createClientState(() => ({
436
+ entries: /* @__PURE__ */ new Map(),
437
+ contentDedup: /* @__PURE__ */ new Set(),
438
+ factoryDeps: /* @__PURE__ */ new Map()
439
+ }));
383
440
  /**
384
441
  * Inject raw CSS text directly without parsing.
385
442
  * This is a low-overhead alternative for injecting global CSS that doesn't need tasty processing.
@@ -435,53 +492,59 @@ function useRawCSS(cssOrFactory, depsOrOptions, options) {
435
492
  const deps = isFactory && Array.isArray(depsOrOptions) ? depsOrOptions : void 0;
436
493
  const opts = isFactory ? options : depsOrOptions;
437
494
  const target = getStyleTarget();
438
- if (isFactory && deps && opts?.id && target.mode === "client") {
439
- const cachedDeps = factoryDepsCache$1.get(opts.id);
495
+ const state = target.mode === "client" ? getClientState$1(opts?.root ?? document) : null;
496
+ if (isFactory && deps && opts?.id && state) {
497
+ const cachedDeps = state.factoryDeps.get(opts.id);
440
498
  if (cachedDeps && depsEqual(cachedDeps, deps)) return;
441
499
  }
442
500
  const css = isFactory ? cssOrFactory() : cssOrFactory;
443
501
  if (!css.trim()) return;
444
502
  if (target.mode === "ssr") {
445
503
  const key = opts?.id ? `raw:${opts.id}` : `raw:${hashString(css)}`;
446
- target.collector.collectRawCSS(key, css);
504
+ target.collector.collectRawCSS(key, css, opts?.id != null);
447
505
  return;
448
506
  }
449
507
  if (target.mode === "rsc") {
450
508
  const key = opts?.id ? `__raw:${opts.id}` : `__raw:${hashString(css)}`;
451
- pushRSCCSS(target.cache, key, css);
509
+ pushRSCCSS(target.cache, key, css, opts?.id != null);
452
510
  return;
453
511
  }
512
+ if (!state) return;
454
513
  const id = opts?.id;
455
514
  if (id) {
456
- const existing = clientEntries.get(id);
515
+ const existing = state.entries.get(id);
457
516
  if (existing) {
458
517
  if (existing.contentKey === css) return;
459
518
  existing.dispose();
460
519
  }
461
520
  const { dispose } = injectRawCSS(css, opts);
462
- clientEntries.set(id, {
521
+ state.entries.set(id, {
463
522
  contentKey: css,
464
523
  dispose
465
524
  });
466
- if (deps) factoryDepsCache$1.set(id, deps);
525
+ if (deps) state.factoryDeps.set(id, deps);
467
526
  } else {
468
527
  const contentKey = hashString(css);
469
- if (clientContentDedup.has(contentKey)) return;
470
- clientContentDedup.add(contentKey);
528
+ if (state.contentDedup.has(contentKey)) return;
529
+ state.contentDedup.add(contentKey);
471
530
  injectRawCSS(css, opts);
472
531
  }
473
532
  }
474
533
  //#endregion
475
534
  //#region src/hooks/useKeyframes.ts
476
- const clientContentToName$1 = /* @__PURE__ */ new Map();
477
- const factoryDepsCache = /* @__PURE__ */ new Map();
535
+ const getClientState = createClientState(() => ({
536
+ contentToName: /* @__PURE__ */ new Map(),
537
+ namedSlots: /* @__PURE__ */ new Map(),
538
+ factoryDeps: /* @__PURE__ */ new Map()
539
+ }));
478
540
  function useKeyframes(stepsOrFactory, depsOrOptions, options) {
479
541
  const isFactory = typeof stepsOrFactory === "function";
480
542
  const deps = isFactory && Array.isArray(depsOrOptions) ? depsOrOptions : void 0;
481
543
  const opts = isFactory ? options : depsOrOptions;
482
544
  const target = getStyleTarget();
483
- if (isFactory && deps && opts?.name && target.mode === "client") {
484
- const cached = factoryDepsCache.get(opts.name);
545
+ const clientState = target.mode === "client" ? getClientState(opts?.root ?? document) : null;
546
+ if (isFactory && deps && opts?.name && clientState) {
547
+ const cached = clientState.factoryDeps.get(opts.name);
485
548
  if (cached && depsEqual(cached.deps, deps)) return cached.name;
486
549
  }
487
550
  const steps = isFactory ? stepsOrFactory() : stepsOrFactory;
@@ -503,19 +566,36 @@ function useKeyframes(stepsOrFactory, depsOrOptions, options) {
503
566
  target.cache.generatedNames.set(key, actualName);
504
567
  return actualName;
505
568
  }
569
+ const state = clientState ?? getClientState(opts?.root ?? document);
506
570
  const serializedContent = JSON.stringify(steps);
507
571
  const cacheKey = `${opts?.name ?? ""}:${serializedContent}`;
508
- const cachedName = clientContentToName$1.get(cacheKey);
572
+ const cachedName = state.contentToName.get(cacheKey);
509
573
  if (cachedName) return cachedName;
510
- const name = keyframes(steps, {
511
- name: opts?.name,
574
+ const providedName = opts?.name;
575
+ if (providedName) {
576
+ const slot = state.namedSlots.get(providedName);
577
+ if (slot && slot.cacheKey !== cacheKey) {
578
+ slot.dispose();
579
+ state.contentToName.delete(slot.cacheKey);
580
+ state.namedSlots.delete(providedName);
581
+ }
582
+ }
583
+ const result = keyframes(steps, {
584
+ name: providedName,
512
585
  root: opts?.root
513
- }).toString();
514
- clientContentToName$1.set(cacheKey, name);
515
- if (deps && opts?.name) factoryDepsCache.set(opts.name, {
516
- deps,
517
- name
518
586
  });
587
+ const name = result.toString();
588
+ state.contentToName.set(cacheKey, name);
589
+ if (providedName) {
590
+ state.namedSlots.set(providedName, {
591
+ cacheKey,
592
+ dispose: result.dispose
593
+ });
594
+ if (deps) state.factoryDeps.set(providedName, {
595
+ deps,
596
+ name
597
+ });
598
+ }
519
599
  return name;
520
600
  }
521
601
  //#endregion
@@ -671,7 +751,7 @@ function useFontFace(family, input, options) {
671
751
  //#endregion
672
752
  //#region src/hooks/useCounterStyle.ts
673
753
  let clientCounterStyleCounter = 0;
674
- const clientContentToName = /* @__PURE__ */ new Map();
754
+ const getClientContentToName = createClientState(() => /* @__PURE__ */ new Map());
675
755
  /**
676
756
  * Inject a CSS @counter-style rule and return the generated name.
677
757
  * Permanent — no cleanup on unmount. Deduplicates by name.
@@ -717,16 +797,71 @@ function useCounterStyle(descriptors, options) {
717
797
  target.cache.generatedNames.set(key, actualName);
718
798
  return actualName;
719
799
  }
800
+ const contentToName = getClientContentToName(options?.root ?? document);
720
801
  const serializedContent = JSON.stringify(descriptors);
721
802
  const cacheKey = `${options?.name ?? ""}:${serializedContent}`;
722
- const existingName = clientContentToName.get(cacheKey);
803
+ const existingName = contentToName.get(cacheKey);
723
804
  if (existingName) return existingName;
724
805
  const name = options?.name ?? makeCounterStyleName(getNamePrefix(), String(clientCounterStyleCounter++));
725
- clientContentToName.set(cacheKey, name);
806
+ contentToName.set(cacheKey, name);
726
807
  getGlobalInjector().counterStyle(name, descriptors, { root: options?.root });
727
808
  return name;
728
809
  }
729
810
  //#endregion
730
- export { APPEARANCE_CHUNK_STYLES, BASE_STYLES, BLOCK_INNER_STYLES, BLOCK_OUTER_STYLES, BLOCK_STYLES, Bucket, CHUNK_NAMES, COLOR_STYLES, CONTAINER_STYLES, CUSTOM_UNITS, ChunkSheetRegistry, DEFAULT_NAME_PREFIX, DEFAULT_ZERO_NAME_PREFIX, DIMENSION_CHUNK_STYLES, DIMENSION_STYLES, DIRECTIONS, DISPLAY_CHUNK_STYLES, Element, FLOW_STYLES, FONT_CHUNK_STYLES, INNER_STYLES, LAYOUT_CHUNK_STYLES, OUTER_STYLES, POSITION_CHUNK_STYLES, POSITION_STYLES, STYLE_TO_CHUNK, SheetManager, StyleInjector, StyleParser, TEXT_STYLES, categorizeStyleKeys, chunkSheetRegistry, cleanup, color, computeStyles, configure, counterStyle, createInjector, createStateParserContext, customFunc, deprecationWarning, destroy, dotize, filterBaseProps, filterMods, fontFace, gc, generateTypographyTokens, getConfig, getCssText, getCssTextForNode, getDisplayName, getGlobalCounterStyle, getGlobalFontFace, getGlobalFuncs, getGlobalKeyframes, getGlobalParser, getGlobalPredefinedStates, getGlobalPredefinedTokens, getGlobalRecipes, getIsTestEnvironment, getNamePrefix, getNamedColorHex, getRawCSSText, getRgbValuesFromRgbaString, hasGlobalKeyframes, hasGlobalRecipes, hasStylesGenerated, hexToRgb, hslToRgbValues, inject, injectGlobal, injectRawCSS, injector, isConfigLocked, isPropertyDefined, isSelector, isTestEnvironment, keyframes, mergeStyles, _modAttrs as modAttrs, normalizeColorTokenValue, okhslFunc, okhslPlugin, okhstFunc, okhstPlugin, parseColor, parseStateKey, parseStyle, processTokens, property, renderStyles, resetConfig, resetGlobalPredefinedTokens, resolveRecipes, setGlobalPredefinedStates, setGlobalPredefinedTokens, strToRgb, stringifyStyles, styleHandlers, tasty, tastyDebug, touch, useCounterStyle, useFontFace, useGlobalStyles, useKeyframes, useProperty, useRawCSS, useStyles, warn };
811
+ //#region src/hooks/useFunction.ts
812
+ /**
813
+ * Register a CSS @function (custom function).
814
+ *
815
+ * @function rules are global and persistent once defined. The hook ensures the
816
+ * function is only registered once per root (deduplicated by function name).
817
+ *
818
+ * Accepts tasty token syntax for the function name:
819
+ * - `$$name` → defines `--name` (matches the call site `$$name(...)`)
820
+ * - `$name` / `--name` → also accepted
821
+ *
822
+ * Works in all environments: client, SSR with collector, and React Server Components.
823
+ *
824
+ * @param name - The function name token (`$$name`, `$name`, or `--name`)
825
+ * @param definition - Function definition (args, returns, result, local vars)
826
+ *
827
+ * Call the function through the Tasty DSL, not a raw `style` prop: an inline
828
+ * `style` value reaches the browser unparsed, so the `$$name(...)` sugar is never
829
+ * expanded and `polyfills.functions` cannot rewrite it either.
830
+ *
831
+ * @example
832
+ * ```tsx
833
+ * const Box = tasty({ styles: { marginTop: '$$negative(10px)' } });
834
+ *
835
+ * function Layout() {
836
+ * useFunction('$$negative', { args: ['$value'], result: '(-1 * $value)' });
837
+ * return <Box />;
838
+ * }
839
+ * ```
840
+ */
841
+ function useFunction(name, definition, options) {
842
+ if (!name) {
843
+ console.warn(`[Tasty] useFunction: function name is required`);
844
+ return;
845
+ }
846
+ if (isFunctionsPolyfillEnabled()) {
847
+ registerFunctionPolyfill(name, definition);
848
+ return;
849
+ }
850
+ const target = getStyleTarget();
851
+ if (target.mode === "ssr") {
852
+ target.collector.collectInternals();
853
+ const css = formatFunctionRule(name, definition);
854
+ if (css) target.collector.collectFunction(parseFunctionName(name), css);
855
+ return;
856
+ }
857
+ if (target.mode === "rsc") {
858
+ const css = formatFunctionRule(name, definition);
859
+ if (css) pushRSCCSS(target.cache, `__func:${parseFunctionName(name)}`, css);
860
+ return;
861
+ }
862
+ getGlobalInjector().func(name, definition, { root: options?.root });
863
+ }
864
+ //#endregion
865
+ export { APPEARANCE_CHUNK_STYLES, BASE_STYLES, BLOCK_INNER_STYLES, BLOCK_OUTER_STYLES, BLOCK_STYLES, CHUNK_NAMES, COLOR_STYLES, CONTAINER_STYLES, CUSTOM_UNITS, ChunkSheetRegistry, DEFAULT_NAME_PREFIX, DEFAULT_ZERO_NAME_PREFIX, DIMENSION_CHUNK_STYLES, DIMENSION_STYLES, DIRECTIONS, DISPLAY_CHUNK_STYLES, Element, FLOW_STYLES, FONT_CHUNK_STYLES, INNER_STYLES, LAYOUT_CHUNK_STYLES, OUTER_STYLES, POSITION_CHUNK_STYLES, POSITION_STYLES, STYLE_TO_CHUNK, SheetManager, StyleInjector, StyleParser, TEXT_STYLES, categorizeStyleKeys, chunkSheetRegistry, cleanup, color, computeStyles, configure, counterStyle, createColorFunc, createInjector, createStateParserContext, defineHandler, destroy, dotize, filterBaseProps, filterMods, fontFace, func, gc, generateTypographyTokens, getCSSText, getCSSTextForNode, getConfig, getDisplayName, getGlobalCounterStyles, getGlobalFontFaces, getGlobalFunctions, getGlobalKeyframes, getGlobalParser, getGlobalPredefinedStates, getGlobalPredefinedTokens, getGlobalRecipes, getNamePrefix, getNamedColorHex, getRawCSSText, getRgbValuesFromRgbaString, hasGlobalKeyframes, hasGlobalRecipes, hasStylesGenerated, hexToRgb, hslToRgbValues, inject, injectGlobal, injectRawCSS, injector, isConfigLocked, isPropertyDefined, isSelector, isTestEnvironment, keyframes, mergeStyles, _modAttrs as modAttrs, normalizeColorTokenValue, okhslFunction, okhslPlugin, okhstFunction, okhstPlugin, parseColor, parseStateKey, parseStyle, processTokens, property, renderStyles, resetConfig, resolveFunctionColor, resolveRecipes, setGlobalPredefinedStates, strToRgb, stringifyStyles, styleHandlers, tasty, tastyDebug, touch, useCounterStyle, useFontFace, useFunction, useGlobalStyles, useKeyframes, useProperty, useRawCSS, useStyles };
731
866
 
732
867
  //# sourceMappingURL=index.js.map