@tenphi/tasty 0.0.0-snapshot.cfcf770 → 0.0.0-snapshot.d2dcdeb

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 (101) hide show
  1. package/README.md +25 -19
  2. package/dist/compute-styles.js +6 -28
  3. package/dist/compute-styles.js.map +1 -1
  4. package/dist/config.d.ts +41 -1
  5. package/dist/config.js +92 -7
  6. package/dist/config.js.map +1 -1
  7. package/dist/core/index.d.ts +2 -2
  8. package/dist/core/index.js +1 -1
  9. package/dist/debug.js +4 -4
  10. package/dist/debug.js.map +1 -1
  11. package/dist/hooks/useCounterStyle.d.ts +3 -17
  12. package/dist/hooks/useCounterStyle.js +55 -35
  13. package/dist/hooks/useCounterStyle.js.map +1 -1
  14. package/dist/hooks/useFontFace.d.ts +3 -1
  15. package/dist/hooks/useFontFace.js +21 -24
  16. package/dist/hooks/useFontFace.js.map +1 -1
  17. package/dist/hooks/useGlobalStyles.d.ts +18 -2
  18. package/dist/hooks/useGlobalStyles.js +51 -40
  19. package/dist/hooks/useGlobalStyles.js.map +1 -1
  20. package/dist/hooks/useKeyframes.d.ts +4 -2
  21. package/dist/hooks/useKeyframes.js +42 -50
  22. package/dist/hooks/useKeyframes.js.map +1 -1
  23. package/dist/hooks/useProperty.d.ts +4 -2
  24. package/dist/hooks/useProperty.js +29 -41
  25. package/dist/hooks/useProperty.js.map +1 -1
  26. package/dist/hooks/useRawCSS.d.ts +13 -44
  27. package/dist/hooks/useRawCSS.js +90 -21
  28. package/dist/hooks/useRawCSS.js.map +1 -1
  29. package/dist/hooks/useStyles.d.ts +4 -4
  30. package/dist/hooks/useStyles.js +7 -5
  31. package/dist/hooks/useStyles.js.map +1 -1
  32. package/dist/index.d.ts +2 -2
  33. package/dist/index.js +1 -1
  34. package/dist/injector/index.js +1 -1
  35. package/dist/injector/index.js.map +1 -1
  36. package/dist/injector/injector.d.ts +9 -7
  37. package/dist/injector/injector.js +126 -38
  38. package/dist/injector/injector.js.map +1 -1
  39. package/dist/injector/sheet-manager.js +4 -2
  40. package/dist/injector/sheet-manager.js.map +1 -1
  41. package/dist/injector/types.d.ts +11 -2
  42. package/dist/pipeline/parseStateKey.js +4 -4
  43. package/dist/pipeline/parseStateKey.js.map +1 -1
  44. package/dist/plugins/types.d.ts +12 -1
  45. package/dist/rsc-cache.js +79 -0
  46. package/dist/rsc-cache.js.map +1 -0
  47. package/dist/ssr/astro-client.d.ts +1 -0
  48. package/dist/ssr/astro-client.js +19 -0
  49. package/dist/ssr/astro-client.js.map +1 -0
  50. package/dist/ssr/astro-middleware.d.ts +15 -0
  51. package/dist/ssr/astro-middleware.js +19 -0
  52. package/dist/ssr/astro-middleware.js.map +1 -0
  53. package/dist/ssr/astro.d.ts +89 -10
  54. package/dist/ssr/astro.js +112 -27
  55. package/dist/ssr/astro.js.map +1 -1
  56. package/dist/ssr/async-storage.js +14 -4
  57. package/dist/ssr/async-storage.js.map +1 -1
  58. package/dist/ssr/collect-auto-properties.js +28 -9
  59. package/dist/ssr/collect-auto-properties.js.map +1 -1
  60. package/dist/ssr/collector.d.ts +5 -13
  61. package/dist/ssr/collector.js +27 -15
  62. package/dist/ssr/collector.js.map +1 -1
  63. package/dist/ssr/context.js +16 -0
  64. package/dist/ssr/context.js.map +1 -0
  65. package/dist/ssr/hydrate.d.ts +20 -13
  66. package/dist/ssr/hydrate.js +24 -28
  67. package/dist/ssr/hydrate.js.map +1 -1
  68. package/dist/ssr/index.d.ts +3 -3
  69. package/dist/ssr/index.js +4 -4
  70. package/dist/ssr/index.js.map +1 -1
  71. package/dist/ssr/next.d.ts +7 -4
  72. package/dist/ssr/next.js +7 -6
  73. package/dist/ssr/next.js.map +1 -1
  74. package/dist/ssr/ssr-collector-ref.js +19 -2
  75. package/dist/ssr/ssr-collector-ref.js.map +1 -1
  76. package/dist/tasty.d.ts +1 -1
  77. package/dist/tasty.js +9 -4
  78. package/dist/tasty.js.map +1 -1
  79. package/dist/utils/deps-equal.js +15 -0
  80. package/dist/utils/deps-equal.js.map +1 -0
  81. package/dist/utils/hash.js +14 -0
  82. package/dist/utils/hash.js.map +1 -0
  83. package/dist/utils/typography.d.ts +21 -10
  84. package/dist/utils/typography.js +1 -1
  85. package/dist/utils/typography.js.map +1 -1
  86. package/dist/zero/babel.d.ts +7 -108
  87. package/dist/zero/babel.js +36 -12
  88. package/dist/zero/babel.js.map +1 -1
  89. package/docs/README.md +2 -2
  90. package/docs/adoption.md +5 -3
  91. package/docs/comparison.md +24 -25
  92. package/docs/configuration.md +69 -1
  93. package/docs/design-system.md +22 -10
  94. package/docs/dsl.md +3 -3
  95. package/docs/getting-started.md +10 -10
  96. package/docs/injector.md +9 -7
  97. package/docs/methodology.md +2 -2
  98. package/docs/{runtime.md → react-api.md} +17 -32
  99. package/docs/ssr.md +125 -39
  100. package/docs/tasty-static.md +14 -2
  101. package/package.json +9 -3
@@ -1,20 +1,21 @@
1
1
  import { getGlobalInjector } from "../config.js";
2
+ import { getStyleTarget, pushRSCCSS } from "../rsc-cache.js";
2
3
  import { formatPropertyCSS } from "../ssr/format-property.js";
3
- import { getRegisteredSSRCollector } from "../ssr/ssr-collector-ref.js";
4
- import { useInsertionEffect, useMemo } from "react";
5
4
  //#region src/hooks/useProperty.ts
6
5
  /**
7
- * Hook to register a CSS @property custom property.
6
+ * Register a CSS @property custom property.
8
7
  * This enables advanced features like animating custom properties.
9
8
  *
10
9
  * Note: @property rules are global and persistent once defined.
11
- * The hook ensures the property is only registered once per root.
10
+ * The function ensures the property is only registered once per root.
12
11
  *
13
12
  * Accepts tasty token syntax for the property name:
14
13
  * - `$name` → defines `--name`
15
14
  * - `#name` → defines `--name-color` (auto-sets syntax: '<color>', defaults initialValue: 'transparent')
16
15
  * - `--name` → defines `--name` (legacy format)
17
16
  *
17
+ * Works in all environments: client, SSR with collector, and React Server Components.
18
+ *
18
19
  * @param name - The property token ($name, #name) or CSS property name (--name)
19
20
  * @param options - Property configuration
20
21
  *
@@ -56,51 +57,38 @@ import { useInsertionEffect, useMemo } from "react";
56
57
  * ```
57
58
  */
58
59
  function useProperty(name, options) {
59
- const ssrCollector = getRegisteredSSRCollector();
60
- const optionsKey = useMemo(() => {
61
- if (!options) return "";
62
- return JSON.stringify({
63
- syntax: options.syntax,
64
- inherits: options.inherits,
65
- initialValue: options.initialValue
66
- });
67
- }, [
68
- options?.syntax,
69
- options?.inherits,
70
- options?.initialValue
71
- ]);
72
- useMemo(() => {
73
- if (!ssrCollector || !name) return;
74
- ssrCollector.collectInternals();
60
+ if (!name) {
61
+ console.warn(`[Tasty] useProperty: property name is required`);
62
+ return;
63
+ }
64
+ const target = getStyleTarget();
65
+ if (target.mode === "ssr") {
66
+ target.collector.collectInternals();
75
67
  const css = formatPropertyCSS(name, {
76
68
  syntax: options?.syntax,
77
69
  inherits: options?.inherits,
78
70
  initialValue: options?.initialValue
79
71
  });
80
- if (css) ssrCollector.collectProperty(name, css);
81
- }, [
82
- ssrCollector,
83
- name,
84
- optionsKey
85
- ]);
86
- useInsertionEffect(() => {
87
- if (!name) {
88
- console.warn(`[Tasty] useProperty: property name is required`);
89
- return;
90
- }
91
- const injector = getGlobalInjector();
92
- if (injector.isPropertyDefined(name, { root: options?.root })) return;
93
- injector.property(name, {
72
+ if (css) target.collector.collectProperty(name, css);
73
+ return;
74
+ }
75
+ if (target.mode === "rsc") {
76
+ const css = formatPropertyCSS(name, {
94
77
  syntax: options?.syntax,
95
78
  inherits: options?.inherits,
96
- initialValue: options?.initialValue,
97
- root: options?.root
79
+ initialValue: options?.initialValue
98
80
  });
99
- }, [
100
- name,
101
- optionsKey,
102
- options?.root
103
- ]);
81
+ if (css) pushRSCCSS(target.cache, `__prop:${name}`, css);
82
+ return;
83
+ }
84
+ const injector = getGlobalInjector();
85
+ if (injector.isPropertyDefined(name, { root: options?.root })) return;
86
+ injector.property(name, {
87
+ syntax: options?.syntax,
88
+ inherits: options?.inherits,
89
+ initialValue: options?.initialValue,
90
+ root: options?.root
91
+ });
104
92
  }
105
93
  //#endregion
106
94
  export { useProperty };
@@ -1 +1 @@
1
- {"version":3,"file":"useProperty.js","names":[],"sources":["../../src/hooks/useProperty.ts"],"sourcesContent":["import { useInsertionEffect, useMemo } from 'react';\n\nimport { getGlobalInjector } from '../config';\nimport { formatPropertyCSS } from '../ssr/format-property';\nimport { getRegisteredSSRCollector } from '../ssr/ssr-collector-ref';\n\nexport interface UsePropertyOptions {\n /**\n * CSS syntax string for the property (e.g., '<color>', '<length>', '<angle>').\n * For color tokens (#name), this is auto-set to '<color>' and cannot be overridden.\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/@property/syntax\n */\n syntax?: string;\n /**\n * Whether the property inherits from parent elements\n * @default true\n */\n inherits?: boolean;\n /**\n * Initial value for the property.\n * For color tokens (#name), this defaults to 'transparent' if not specified.\n */\n initialValue?: string | number;\n /**\n * Shadow root or document to inject into\n */\n root?: Document | ShadowRoot;\n}\n\n/**\n * Hook to register a CSS @property custom property.\n * This enables advanced features like animating custom properties.\n *\n * Note: @property rules are global and persistent once defined.\n * The hook ensures the property is only registered once per root.\n *\n * Accepts tasty token syntax for the property name:\n * - `$name` → defines `--name`\n * - `#name` → defines `--name-color` (auto-sets syntax: '<color>', defaults initialValue: 'transparent')\n * - `--name` → defines `--name` (legacy format)\n *\n * @param name - The property token ($name, #name) or CSS property name (--name)\n * @param options - Property configuration\n *\n * @example Basic property with token syntax\n * ```tsx\n * function Spinner() {\n * useProperty('$rotation', {\n * syntax: '<angle>',\n * inherits: false,\n * initialValue: '0deg',\n * });\n *\n * return <div className=\"spinner\" />;\n * }\n * ```\n *\n * @example Color property with token syntax (auto-sets syntax)\n * ```tsx\n * function MyComponent() {\n * useProperty('#theme', {\n * initialValue: 'red', // syntax: '<color>' is auto-set\n * });\n *\n * // Now --theme-color can be animated with CSS transitions\n * return <div style={{ '--theme-color': 'blue' } as React.CSSProperties}>Colored</div>;\n * }\n * ```\n *\n * @example Legacy format (still supported)\n * ```tsx\n * function ResizableBox() {\n * useProperty('--box-size', {\n * syntax: '<length>',\n * initialValue: '100px',\n * });\n *\n * return <div style={{ width: 'var(--box-size)' }} />;\n * }\n * ```\n */\nexport function useProperty(name: string, options?: UsePropertyOptions): void {\n const ssrCollector = getRegisteredSSRCollector();\n\n // Memoize the options to create a stable dependency\n const optionsKey = useMemo(() => {\n if (!options) return '';\n return JSON.stringify({\n syntax: options.syntax,\n inherits: options.inherits,\n initialValue: options.initialValue,\n });\n }, [options?.syntax, options?.inherits, options?.initialValue]);\n\n // SSR path: collect @property CSS during render\n useMemo(() => {\n if (!ssrCollector || !name) return;\n\n ssrCollector.collectInternals();\n\n const css = formatPropertyCSS(name, {\n syntax: options?.syntax,\n inherits: options?.inherits,\n initialValue: options?.initialValue,\n });\n if (css) {\n ssrCollector.collectProperty(name, css);\n }\n }, [ssrCollector, name, optionsKey]);\n\n // Client path: inject via DOM\n useInsertionEffect(() => {\n if (!name) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(`[Tasty] useProperty: property name is required`);\n }\n return;\n }\n\n const injector = getGlobalInjector();\n\n if (injector.isPropertyDefined(name, { root: options?.root })) {\n return;\n }\n\n injector.property(name, {\n syntax: options?.syntax,\n inherits: options?.inherits,\n initialValue: options?.initialValue,\n root: options?.root,\n });\n }, [name, optionsKey, options?.root]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiFA,SAAgB,YAAY,MAAc,SAAoC;CAC5E,MAAM,eAAe,2BAA2B;CAGhD,MAAM,aAAa,cAAc;AAC/B,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,KAAK,UAAU;GACpB,QAAQ,QAAQ;GAChB,UAAU,QAAQ;GAClB,cAAc,QAAQ;GACvB,CAAC;IACD;EAAC,SAAS;EAAQ,SAAS;EAAU,SAAS;EAAa,CAAC;AAG/D,eAAc;AACZ,MAAI,CAAC,gBAAgB,CAAC,KAAM;AAE5B,eAAa,kBAAkB;EAE/B,MAAM,MAAM,kBAAkB,MAAM;GAClC,QAAQ,SAAS;GACjB,UAAU,SAAS;GACnB,cAAc,SAAS;GACxB,CAAC;AACF,MAAI,IACF,cAAa,gBAAgB,MAAM,IAAI;IAExC;EAAC;EAAc;EAAM;EAAW,CAAC;AAGpC,0BAAyB;AACvB,MAAI,CAAC,MAAM;AAEP,WAAQ,KAAK,iDAAiD;AAEhE;;EAGF,MAAM,WAAW,mBAAmB;AAEpC,MAAI,SAAS,kBAAkB,MAAM,EAAE,MAAM,SAAS,MAAM,CAAC,CAC3D;AAGF,WAAS,SAAS,MAAM;GACtB,QAAQ,SAAS;GACjB,UAAU,SAAS;GACnB,cAAc,SAAS;GACvB,MAAM,SAAS;GAChB,CAAC;IACD;EAAC;EAAM;EAAY,SAAS;EAAK,CAAC"}
1
+ {"version":3,"file":"useProperty.js","names":[],"sources":["../../src/hooks/useProperty.ts"],"sourcesContent":["import { getGlobalInjector } from '../config';\nimport { getStyleTarget, pushRSCCSS } from '../rsc-cache';\nimport { formatPropertyCSS } from '../ssr/format-property';\n\nexport interface UsePropertyOptions {\n /**\n * CSS syntax string for the property (e.g., '<color>', '<length>', '<angle>').\n * For color tokens (#name), this is auto-set to '<color>' and cannot be overridden.\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/@property/syntax\n */\n syntax?: string;\n /**\n * Whether the property inherits from parent elements\n * @default true\n */\n inherits?: boolean;\n /**\n * Initial value for the property.\n * For color tokens (#name), this defaults to 'transparent' if not specified.\n */\n initialValue?: string | number;\n /**\n * Shadow root or document to inject into\n */\n root?: Document | ShadowRoot;\n}\n\n/**\n * Register a CSS @property custom property.\n * This enables advanced features like animating custom properties.\n *\n * Note: @property rules are global and persistent once defined.\n * The function ensures the property is only registered once per root.\n *\n * Accepts tasty token syntax for the property name:\n * - `$name` → defines `--name`\n * - `#name` → defines `--name-color` (auto-sets syntax: '<color>', defaults initialValue: 'transparent')\n * - `--name` → defines `--name` (legacy format)\n *\n * Works in all environments: client, SSR with collector, and React Server Components.\n *\n * @param name - The property token ($name, #name) or CSS property name (--name)\n * @param options - Property configuration\n *\n * @example Basic property with token syntax\n * ```tsx\n * function Spinner() {\n * useProperty('$rotation', {\n * syntax: '<angle>',\n * inherits: false,\n * initialValue: '0deg',\n * });\n *\n * return <div className=\"spinner\" />;\n * }\n * ```\n *\n * @example Color property with token syntax (auto-sets syntax)\n * ```tsx\n * function MyComponent() {\n * useProperty('#theme', {\n * initialValue: 'red', // syntax: '<color>' is auto-set\n * });\n *\n * // Now --theme-color can be animated with CSS transitions\n * return <div style={{ '--theme-color': 'blue' } as React.CSSProperties}>Colored</div>;\n * }\n * ```\n *\n * @example Legacy format (still supported)\n * ```tsx\n * function ResizableBox() {\n * useProperty('--box-size', {\n * syntax: '<length>',\n * initialValue: '100px',\n * });\n *\n * return <div style={{ width: 'var(--box-size)' }} />;\n * }\n * ```\n */\nexport function useProperty(name: string, options?: UsePropertyOptions): void {\n if (!name) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(`[Tasty] useProperty: property name is required`);\n }\n return;\n }\n\n const target = getStyleTarget();\n\n if (target.mode === 'ssr') {\n target.collector.collectInternals();\n\n const css = formatPropertyCSS(name, {\n syntax: options?.syntax,\n inherits: options?.inherits,\n initialValue: options?.initialValue,\n });\n if (css) {\n target.collector.collectProperty(name, css);\n }\n return;\n }\n\n if (target.mode === 'rsc') {\n const css = formatPropertyCSS(name, {\n syntax: options?.syntax,\n inherits: options?.inherits,\n initialValue: options?.initialValue,\n });\n if (css) {\n pushRSCCSS(target.cache, `__prop:${name}`, css);\n }\n return;\n }\n\n const injector = getGlobalInjector();\n\n if (injector.isPropertyDefined(name, { root: options?.root })) {\n return;\n }\n\n injector.property(name, {\n syntax: options?.syntax,\n inherits: options?.inherits,\n initialValue: options?.initialValue,\n root: options?.root,\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiFA,SAAgB,YAAY,MAAc,SAAoC;AAC5E,KAAI,CAAC,MAAM;AAEP,UAAQ,KAAK,iDAAiD;AAEhE;;CAGF,MAAM,SAAS,gBAAgB;AAE/B,KAAI,OAAO,SAAS,OAAO;AACzB,SAAO,UAAU,kBAAkB;EAEnC,MAAM,MAAM,kBAAkB,MAAM;GAClC,QAAQ,SAAS;GACjB,UAAU,SAAS;GACnB,cAAc,SAAS;GACxB,CAAC;AACF,MAAI,IACF,QAAO,UAAU,gBAAgB,MAAM,IAAI;AAE7C;;AAGF,KAAI,OAAO,SAAS,OAAO;EACzB,MAAM,MAAM,kBAAkB,MAAM;GAClC,QAAQ,SAAS;GACjB,UAAU,SAAS;GACnB,cAAc,SAAS;GACxB,CAAC;AACF,MAAI,IACF,YAAW,OAAO,OAAO,UAAU,QAAQ,IAAI;AAEjD;;CAGF,MAAM,WAAW,mBAAmB;AAEpC,KAAI,SAAS,kBAAkB,MAAM,EAAE,MAAM,SAAS,MAAM,CAAC,CAC3D;AAGF,UAAS,SAAS,MAAM;EACtB,QAAQ,SAAS;EACjB,UAAU,SAAS;EACnB,cAAc,SAAS;EACvB,MAAM,SAAS;EAChB,CAAC"}
@@ -1,51 +1,20 @@
1
1
  //#region src/hooks/useRawCSS.d.ts
2
2
  interface UseRawCSSOptions {
3
+ /**
4
+ * Shadow root or document to inject into.
5
+ * Note: `root` is not part of the update-tracking comparison — changing
6
+ * only the root for the same id/content will not re-inject.
7
+ */
3
8
  root?: Document | ShadowRoot;
9
+ /**
10
+ * Stable identifier for update tracking (client-only). When provided,
11
+ * changing the CSS content will dispose the previous injection and inject
12
+ * the new one. Without an id, deduplication is purely content-based (same
13
+ * CSS is injected only once). In RSC mode, renders are single-pass so
14
+ * update tracking does not apply.
15
+ */
16
+ id?: string;
4
17
  }
5
- /**
6
- * Hook to inject raw CSS text directly without parsing.
7
- * This is a low-overhead alternative for injecting global CSS that doesn't need tasty processing.
8
- *
9
- * The CSS is inserted into a separate style element (data-tasty-raw) to avoid conflicts
10
- * with tasty's chunked style sheets.
11
- *
12
- * @example Static CSS string
13
- * ```tsx
14
- * function GlobalStyles() {
15
- * useRawCSS(`
16
- * body {
17
- * margin: 0;
18
- * padding: 0;
19
- * font-family: sans-serif;
20
- * }
21
- * `);
22
- *
23
- * return null;
24
- * }
25
- * ```
26
- *
27
- * @example Factory function with dependencies (like useMemo)
28
- * ```tsx
29
- * function ThemeStyles({ theme }: { theme: 'light' | 'dark' }) {
30
- * useRawCSS(() => `
31
- * :root {
32
- * --bg-color: ${theme === 'dark' ? '#1a1a1a' : '#ffffff'};
33
- * --text-color: ${theme === 'dark' ? '#ffffff' : '#1a1a1a'};
34
- * }
35
- * `, [theme]);
36
- *
37
- * return null;
38
- * }
39
- * ```
40
- *
41
- * @example With options
42
- * ```tsx
43
- * function ShadowStyles({ shadowRoot }) {
44
- * useRawCSS(() => `.scoped { color: red; }`, [], { root: shadowRoot });
45
- * return null;
46
- * }
47
- * ```
48
- */
49
18
  declare function useRawCSS(css: string, options?: UseRawCSSOptions): void;
50
19
  declare function useRawCSS(factory: () => string, deps: readonly unknown[], options?: UseRawCSSOptions): void;
51
20
  //#endregion
@@ -1,32 +1,101 @@
1
+ import { hashString } from "../utils/hash.js";
1
2
  import { injectRawCSS } from "../injector/index.js";
2
- import { getRegisteredSSRCollector } from "../ssr/ssr-collector-ref.js";
3
- import { useInsertionEffect, useMemo, useRef } from "react";
3
+ import { getStyleTarget, pushRSCCSS } from "../rsc-cache.js";
4
+ import { depsEqual } from "../utils/deps-equal.js";
4
5
  //#region src/hooks/useRawCSS.ts
6
+ const clientEntries = /* @__PURE__ */ new Map();
7
+ const clientContentDedup = /* @__PURE__ */ new Set();
8
+ const factoryDepsCache = /* @__PURE__ */ new Map();
9
+ /**
10
+ * Inject raw CSS text directly without parsing.
11
+ * This is a low-overhead alternative for injecting global CSS that doesn't need tasty processing.
12
+ *
13
+ * The CSS is inserted into a separate style element (data-tasty-raw) to avoid conflicts
14
+ * with tasty's chunked style sheets.
15
+ *
16
+ * Works in all environments: client, SSR with collector, and React Server Components.
17
+ *
18
+ * Injected styles are permanent — they are not cleaned up on component unmount.
19
+ * Use the `id` option for update tracking when styles change over the
20
+ * component lifecycle.
21
+ *
22
+ * @example Static CSS string
23
+ * ```tsx
24
+ * function GlobalStyles() {
25
+ * useRawCSS(`
26
+ * body {
27
+ * margin: 0;
28
+ * padding: 0;
29
+ * font-family: sans-serif;
30
+ * }
31
+ * `);
32
+ *
33
+ * return null;
34
+ * }
35
+ * ```
36
+ *
37
+ * @example Factory function with dependencies
38
+ * ```tsx
39
+ * function ThemeStyles({ theme }: { theme: 'light' | 'dark' }) {
40
+ * useRawCSS(() => `
41
+ * :root {
42
+ * --bg-color: ${theme === 'dark' ? '#1a1a1a' : '#ffffff'};
43
+ * --text-color: ${theme === 'dark' ? '#ffffff' : '#1a1a1a'};
44
+ * }
45
+ * `, [theme], { id: 'theme-vars' });
46
+ *
47
+ * return null;
48
+ * }
49
+ * ```
50
+ *
51
+ * @example With options
52
+ * ```tsx
53
+ * function ShadowStyles({ shadowRoot }) {
54
+ * useRawCSS(() => `.scoped { color: red; }`, [], { root: shadowRoot });
55
+ * return null;
56
+ * }
57
+ * ```
58
+ */
5
59
  function useRawCSS(cssOrFactory, depsOrOptions, options) {
6
- const ssrCollector = getRegisteredSSRCollector();
7
60
  const isFactory = typeof cssOrFactory === "function";
8
61
  const deps = isFactory && Array.isArray(depsOrOptions) ? depsOrOptions : void 0;
9
62
  const opts = isFactory ? options : depsOrOptions;
10
- const css = useMemo(() => isFactory ? cssOrFactory() : cssOrFactory, isFactory ? deps ?? [] : [cssOrFactory]);
11
- useMemo(() => {
12
- if (!ssrCollector || !css.trim()) return;
13
- const key = `raw:${css.length}:${css.slice(0, 64)}`;
14
- ssrCollector.collectRawCSS(key, css);
15
- }, [ssrCollector, css]);
16
- const disposeRef = useRef(null);
17
- useInsertionEffect(() => {
18
- disposeRef.current?.();
19
- if (!css.trim()) {
20
- disposeRef.current = null;
21
- return;
63
+ const target = getStyleTarget();
64
+ if (isFactory && deps && opts?.id && target.mode === "client") {
65
+ const cachedDeps = factoryDepsCache.get(opts.id);
66
+ if (cachedDeps && depsEqual(cachedDeps, deps)) return;
67
+ }
68
+ const css = isFactory ? cssOrFactory() : cssOrFactory;
69
+ if (!css.trim()) return;
70
+ if (target.mode === "ssr") {
71
+ const key = opts?.id ? `raw:${opts.id}` : `raw:${hashString(css)}`;
72
+ target.collector.collectRawCSS(key, css);
73
+ return;
74
+ }
75
+ if (target.mode === "rsc") {
76
+ const key = opts?.id ? `__raw:${opts.id}` : `__raw:${hashString(css)}`;
77
+ pushRSCCSS(target.cache, key, css);
78
+ return;
79
+ }
80
+ const id = opts?.id;
81
+ if (id) {
82
+ const existing = clientEntries.get(id);
83
+ if (existing) {
84
+ if (existing.contentKey === css) return;
85
+ existing.dispose();
22
86
  }
23
87
  const { dispose } = injectRawCSS(css, opts);
24
- disposeRef.current = dispose;
25
- return () => {
26
- disposeRef.current?.();
27
- disposeRef.current = null;
28
- };
29
- }, [css, opts?.root]);
88
+ clientEntries.set(id, {
89
+ contentKey: css,
90
+ dispose
91
+ });
92
+ if (deps) factoryDepsCache.set(id, deps);
93
+ } else {
94
+ const contentKey = hashString(css);
95
+ if (clientContentDedup.has(contentKey)) return;
96
+ clientContentDedup.add(contentKey);
97
+ injectRawCSS(css, opts);
98
+ }
30
99
  }
31
100
  //#endregion
32
101
  export { useRawCSS };
@@ -1 +1 @@
1
- {"version":3,"file":"useRawCSS.js","names":[],"sources":["../../src/hooks/useRawCSS.ts"],"sourcesContent":["import { useInsertionEffect, useMemo, useRef } from 'react';\n\nimport { injectRawCSS } from '../injector';\nimport { getRegisteredSSRCollector } from '../ssr/ssr-collector-ref';\n\ninterface UseRawCSSOptions {\n root?: Document | ShadowRoot;\n}\n\n/**\n * Hook to inject raw CSS text directly without parsing.\n * This is a low-overhead alternative for injecting global CSS that doesn't need tasty processing.\n *\n * The CSS is inserted into a separate style element (data-tasty-raw) to avoid conflicts\n * with tasty's chunked style sheets.\n *\n * @example Static CSS string\n * ```tsx\n * function GlobalStyles() {\n * useRawCSS(`\n * body {\n * margin: 0;\n * padding: 0;\n * font-family: sans-serif;\n * }\n * `);\n *\n * return null;\n * }\n * ```\n *\n * @example Factory function with dependencies (like useMemo)\n * ```tsx\n * function ThemeStyles({ theme }: { theme: 'light' | 'dark' }) {\n * useRawCSS(() => `\n * :root {\n * --bg-color: ${theme === 'dark' ? '#1a1a1a' : '#ffffff'};\n * --text-color: ${theme === 'dark' ? '#ffffff' : '#1a1a1a'};\n * }\n * `, [theme]);\n *\n * return null;\n * }\n * ```\n *\n * @example With options\n * ```tsx\n * function ShadowStyles({ shadowRoot }) {\n * useRawCSS(() => `.scoped { color: red; }`, [], { root: shadowRoot });\n * return null;\n * }\n * ```\n */\n\n// Overload 1: Static CSS string\nexport function useRawCSS(css: string, options?: UseRawCSSOptions): void;\n\n// Overload 2: Factory function with dependencies\nexport function useRawCSS(\n factory: () => string,\n deps: readonly unknown[],\n options?: UseRawCSSOptions,\n): void;\n\n// Implementation\nexport function useRawCSS(\n cssOrFactory: string | (() => string),\n depsOrOptions?: readonly unknown[] | UseRawCSSOptions,\n options?: UseRawCSSOptions,\n): void {\n const ssrCollector = getRegisteredSSRCollector();\n\n // Detect which overload is being used\n const isFactory = typeof cssOrFactory === 'function';\n\n // Parse arguments based on overload\n const deps =\n isFactory && Array.isArray(depsOrOptions) ? depsOrOptions : undefined;\n const opts = isFactory\n ? options\n : (depsOrOptions as UseRawCSSOptions | undefined);\n\n // Memoize CSS - for factory functions, use provided deps; for strings, use the string itself\n const css = useMemo(\n () =>\n isFactory ? (cssOrFactory as () => string)() : (cssOrFactory as string),\n\n isFactory ? (deps ?? []) : [cssOrFactory],\n );\n\n // SSR path: collect raw CSS during render\n useMemo(() => {\n if (!ssrCollector || !css.trim()) return;\n\n const key = `raw:${css.length}:${css.slice(0, 64)}`;\n ssrCollector.collectRawCSS(key, css);\n }, [ssrCollector, css]);\n\n const disposeRef = useRef<(() => void) | null>(null);\n\n // Client path: inject via DOM\n useInsertionEffect(() => {\n disposeRef.current?.();\n\n if (!css.trim()) {\n disposeRef.current = null;\n return;\n }\n\n const { dispose } = injectRawCSS(css, opts);\n disposeRef.current = dispose;\n\n return () => {\n disposeRef.current?.();\n disposeRef.current = null;\n };\n }, [css, opts?.root]);\n}\n"],"mappings":";;;;AAiEA,SAAgB,UACd,cACA,eACA,SACM;CACN,MAAM,eAAe,2BAA2B;CAGhD,MAAM,YAAY,OAAO,iBAAiB;CAG1C,MAAM,OACJ,aAAa,MAAM,QAAQ,cAAc,GAAG,gBAAgB,KAAA;CAC9D,MAAM,OAAO,YACT,UACC;CAGL,MAAM,MAAM,cAER,YAAa,cAA+B,GAAI,cAElD,YAAa,QAAQ,EAAE,GAAI,CAAC,aAAa,CAC1C;AAGD,eAAc;AACZ,MAAI,CAAC,gBAAgB,CAAC,IAAI,MAAM,CAAE;EAElC,MAAM,MAAM,OAAO,IAAI,OAAO,GAAG,IAAI,MAAM,GAAG,GAAG;AACjD,eAAa,cAAc,KAAK,IAAI;IACnC,CAAC,cAAc,IAAI,CAAC;CAEvB,MAAM,aAAa,OAA4B,KAAK;AAGpD,0BAAyB;AACvB,aAAW,WAAW;AAEtB,MAAI,CAAC,IAAI,MAAM,EAAE;AACf,cAAW,UAAU;AACrB;;EAGF,MAAM,EAAE,YAAY,aAAa,KAAK,KAAK;AAC3C,aAAW,UAAU;AAErB,eAAa;AACX,cAAW,WAAW;AACtB,cAAW,UAAU;;IAEtB,CAAC,KAAK,MAAM,KAAK,CAAC"}
1
+ {"version":3,"file":"useRawCSS.js","names":[],"sources":["../../src/hooks/useRawCSS.ts"],"sourcesContent":["import { injectRawCSS } from '../injector';\nimport { getStyleTarget, pushRSCCSS } from '../rsc-cache';\nimport { depsEqual } from '../utils/deps-equal';\nimport { hashString } from '../utils/hash';\n\ninterface UseRawCSSOptions {\n /**\n * Shadow root or document to inject into.\n * Note: `root` is not part of the update-tracking comparison — changing\n * only the root for the same id/content will not re-inject.\n */\n root?: Document | ShadowRoot;\n /**\n * Stable identifier for update tracking (client-only). When provided,\n * changing the CSS content will dispose the previous injection and inject\n * the new one. Without an id, deduplication is purely content-based (same\n * CSS is injected only once). In RSC mode, renders are single-pass so\n * update tracking does not apply.\n */\n id?: string;\n}\n\ninterface ClientEntry {\n contentKey: string;\n dispose: () => void;\n}\n\nconst clientEntries = new Map<string, ClientEntry>();\nconst clientContentDedup = new Set<string>();\nconst factoryDepsCache = new Map<string, readonly unknown[]>();\n\n/* @internal — used only for tests */\nexport function _resetRawCSSCache(): void {\n clientEntries.clear();\n clientContentDedup.clear();\n factoryDepsCache.clear();\n}\n\n// Overload 1: Static CSS string\nexport function useRawCSS(css: string, options?: UseRawCSSOptions): void;\n\n// Overload 2: Factory function with dependencies\nexport function useRawCSS(\n factory: () => string,\n deps: readonly unknown[],\n options?: UseRawCSSOptions,\n): void;\n\n/**\n * Inject raw CSS text directly without parsing.\n * This is a low-overhead alternative for injecting global CSS that doesn't need tasty processing.\n *\n * The CSS is inserted into a separate style element (data-tasty-raw) to avoid conflicts\n * with tasty's chunked style sheets.\n *\n * Works in all environments: client, SSR with collector, and React Server Components.\n *\n * Injected styles are permanent — they are not cleaned up on component unmount.\n * Use the `id` option for update tracking when styles change over the\n * component lifecycle.\n *\n * @example Static CSS string\n * ```tsx\n * function GlobalStyles() {\n * useRawCSS(`\n * body {\n * margin: 0;\n * padding: 0;\n * font-family: sans-serif;\n * }\n * `);\n *\n * return null;\n * }\n * ```\n *\n * @example Factory function with dependencies\n * ```tsx\n * function ThemeStyles({ theme }: { theme: 'light' | 'dark' }) {\n * useRawCSS(() => `\n * :root {\n * --bg-color: ${theme === 'dark' ? '#1a1a1a' : '#ffffff'};\n * --text-color: ${theme === 'dark' ? '#ffffff' : '#1a1a1a'};\n * }\n * `, [theme], { id: 'theme-vars' });\n *\n * return null;\n * }\n * ```\n *\n * @example With options\n * ```tsx\n * function ShadowStyles({ shadowRoot }) {\n * useRawCSS(() => `.scoped { color: red; }`, [], { root: shadowRoot });\n * return null;\n * }\n * ```\n */\nexport function useRawCSS(\n cssOrFactory: string | (() => string),\n depsOrOptions?: readonly unknown[] | UseRawCSSOptions,\n options?: UseRawCSSOptions,\n): void {\n const isFactory = typeof cssOrFactory === 'function';\n\n const deps =\n isFactory && Array.isArray(depsOrOptions) ? depsOrOptions : undefined;\n const opts = isFactory\n ? options\n : (depsOrOptions as UseRawCSSOptions | undefined);\n\n const target = getStyleTarget();\n\n // Client deps cache: skip factory re-evaluation when deps haven't changed\n if (isFactory && deps && opts?.id && target.mode === 'client') {\n const cachedDeps = factoryDepsCache.get(opts.id);\n if (cachedDeps && depsEqual(cachedDeps, deps)) {\n return;\n }\n }\n\n const css = isFactory\n ? (cssOrFactory as () => string)()\n : (cssOrFactory as string);\n\n if (!css.trim()) return;\n\n if (target.mode === 'ssr') {\n const key = opts?.id ? `raw:${opts.id}` : `raw:${hashString(css)}`;\n target.collector.collectRawCSS(key, css);\n return;\n }\n\n if (target.mode === 'rsc') {\n const key = opts?.id ? `__raw:${opts.id}` : `__raw:${hashString(css)}`;\n pushRSCCSS(target.cache, key, css);\n return;\n }\n\n // Client path\n const id = opts?.id;\n\n if (id) {\n const existing = clientEntries.get(id);\n if (existing) {\n if (existing.contentKey === css) return;\n existing.dispose();\n }\n\n const { dispose } = injectRawCSS(css, opts);\n clientEntries.set(id, { contentKey: css, dispose });\n if (deps) factoryDepsCache.set(id, deps);\n } else {\n const contentKey = hashString(css);\n if (clientContentDedup.has(contentKey)) return;\n clientContentDedup.add(contentKey);\n injectRawCSS(css, opts);\n }\n}\n"],"mappings":";;;;;AA2BA,MAAM,gCAAgB,IAAI,KAA0B;AACpD,MAAM,qCAAqB,IAAI,KAAa;AAC5C,MAAM,mCAAmB,IAAI,KAAiC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqE9D,SAAgB,UACd,cACA,eACA,SACM;CACN,MAAM,YAAY,OAAO,iBAAiB;CAE1C,MAAM,OACJ,aAAa,MAAM,QAAQ,cAAc,GAAG,gBAAgB,KAAA;CAC9D,MAAM,OAAO,YACT,UACC;CAEL,MAAM,SAAS,gBAAgB;AAG/B,KAAI,aAAa,QAAQ,MAAM,MAAM,OAAO,SAAS,UAAU;EAC7D,MAAM,aAAa,iBAAiB,IAAI,KAAK,GAAG;AAChD,MAAI,cAAc,UAAU,YAAY,KAAK,CAC3C;;CAIJ,MAAM,MAAM,YACP,cAA+B,GAC/B;AAEL,KAAI,CAAC,IAAI,MAAM,CAAE;AAEjB,KAAI,OAAO,SAAS,OAAO;EACzB,MAAM,MAAM,MAAM,KAAK,OAAO,KAAK,OAAO,OAAO,WAAW,IAAI;AAChE,SAAO,UAAU,cAAc,KAAK,IAAI;AACxC;;AAGF,KAAI,OAAO,SAAS,OAAO;EACzB,MAAM,MAAM,MAAM,KAAK,SAAS,KAAK,OAAO,SAAS,WAAW,IAAI;AACpE,aAAW,OAAO,OAAO,KAAK,IAAI;AAClC;;CAIF,MAAM,KAAK,MAAM;AAEjB,KAAI,IAAI;EACN,MAAM,WAAW,cAAc,IAAI,GAAG;AACtC,MAAI,UAAU;AACZ,OAAI,SAAS,eAAe,IAAK;AACjC,YAAS,SAAS;;EAGpB,MAAM,EAAE,YAAY,aAAa,KAAK,KAAK;AAC3C,gBAAc,IAAI,IAAI;GAAE,YAAY;GAAK;GAAS,CAAC;AACnD,MAAI,KAAM,kBAAiB,IAAI,IAAI,KAAK;QACnC;EACL,MAAM,aAAa,WAAW,IAAI;AAClC,MAAI,mBAAmB,IAAI,WAAW,CAAE;AACxC,qBAAmB,IAAI,WAAW;AAClC,eAAa,KAAK,KAAK"}
@@ -15,11 +15,11 @@ interface UseStylesResult {
15
15
  className: string;
16
16
  }
17
17
  /**
18
- * Generate CSS classes from Tasty styles.
19
- * Thin re-export of `computeStyles()` kept for backward compatibility.
18
+ * Hook to generate CSS classes from Tasty styles.
19
+ * Thin wrapper around `computeStyles()` that adds React context-based
20
+ * SSR collector discovery for backward compatibility with TastyRegistry.
20
21
  *
21
- * Unlike a React hook, this is a plain function and can be called
22
- * from both client components and React Server Components.
22
+ * For hook-free usage (e.g. in server components), use `computeStyles()` directly.
23
23
  *
24
24
  * @example
25
25
  * ```tsx
@@ -1,11 +1,13 @@
1
1
  import { computeStyles } from "../compute-styles.js";
2
+ import { TastySSRContext } from "../ssr/context.js";
3
+ import { useContext } from "react";
2
4
  //#region src/hooks/useStyles.ts
3
5
  /**
4
- * Generate CSS classes from Tasty styles.
5
- * Thin re-export of `computeStyles()` kept for backward compatibility.
6
+ * Hook to generate CSS classes from Tasty styles.
7
+ * Thin wrapper around `computeStyles()` that adds React context-based
8
+ * SSR collector discovery for backward compatibility with TastyRegistry.
6
9
  *
7
- * Unlike a React hook, this is a plain function and can be called
8
- * from both client components and React Server Components.
10
+ * For hook-free usage (e.g. in server components), use `computeStyles()` directly.
9
11
  *
10
12
  * @example
11
13
  * ```tsx
@@ -21,7 +23,7 @@ import { computeStyles } from "../compute-styles.js";
21
23
  * ```
22
24
  */
23
25
  function useStyles(styles) {
24
- return computeStyles(styles);
26
+ return computeStyles(styles, { ssrCollector: useContext(TastySSRContext) });
25
27
  }
26
28
  //#endregion
27
29
  export { useStyles };
@@ -1 +1 @@
1
- {"version":3,"file":"useStyles.js","names":[],"sources":["../../src/hooks/useStyles.ts"],"sourcesContent":["import { computeStyles } from '../compute-styles';\nimport type { Styles } from '../styles/types';\n\n/**\n * Tasty styles object to generate CSS classes for.\n * Can be undefined or empty object for no styles.\n */\nexport type UseStylesOptions = Styles | undefined;\n\nexport interface UseStylesResult {\n /**\n * Generated className(s) to apply to the element.\n * Can be empty string if no styles are provided.\n * With chunking enabled, may contain multiple space-separated class names.\n */\n className: string;\n}\n\n/**\n * Generate CSS classes from Tasty styles.\n * Thin re-export of `computeStyles()` kept for backward compatibility.\n *\n * Unlike a React hook, this is a plain function and can be called\n * from both client components and React Server Components.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { className } = useStyles({\n * padding: '2x',\n * fill: '#purple',\n * radius: '1r',\n * });\n *\n * return <div className={className}>Styled content</div>;\n * }\n * ```\n */\nexport function useStyles(styles: UseStylesOptions): UseStylesResult {\n return computeStyles(styles);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAsCA,SAAgB,UAAU,QAA2C;AACnE,QAAO,cAAc,OAAO"}
1
+ {"version":3,"file":"useStyles.js","names":[],"sources":["../../src/hooks/useStyles.ts"],"sourcesContent":["import { useContext } from 'react';\n\nimport { computeStyles } from '../compute-styles';\nimport { TastySSRContext } from '../ssr/context';\nimport type { Styles } from '../styles/types';\n\n/**\n * Tasty styles object to generate CSS classes for.\n * Can be undefined or empty object for no styles.\n */\nexport type UseStylesOptions = Styles | undefined;\n\nexport interface UseStylesResult {\n /**\n * Generated className(s) to apply to the element.\n * Can be empty string if no styles are provided.\n * With chunking enabled, may contain multiple space-separated class names.\n */\n className: string;\n}\n\n/**\n * Hook to generate CSS classes from Tasty styles.\n * Thin wrapper around `computeStyles()` that adds React context-based\n * SSR collector discovery for backward compatibility with TastyRegistry.\n *\n * For hook-free usage (e.g. in server components), use `computeStyles()` directly.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { className } = useStyles({\n * padding: '2x',\n * fill: '#purple',\n * radius: '1r',\n * });\n *\n * return <div className={className}>Styled content</div>;\n * }\n * ```\n */\nexport function useStyles(styles: UseStylesOptions): UseStylesResult {\n return computeStyles(styles, { ssrCollector: useContext(TastySSRContext) });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAyCA,SAAgB,UAAU,QAA2C;AACnE,QAAO,cAAc,QAAQ,EAAE,cAAc,WAAW,gBAAgB,EAAE,CAAC"}
package/dist/index.d.ts CHANGED
@@ -12,6 +12,7 @@ import { RenderResult, StyleResult, isSelector, renderStyles } from "./pipeline/
12
12
  import { SheetManager } from "./injector/sheet-manager.js";
13
13
  import { StyleInjector } from "./injector/injector.js";
14
14
  import { ColorSpace } from "./utils/color-space.js";
15
+ import { TypographyPreset, TypographyTokenValue, generateTypographyTokens } from "./utils/typography.js";
15
16
  import { TastyPlugin, TastyPluginFactory } from "./plugins/types.js";
16
17
  import { TastyConfig, configure, getConfig, getGlobalCounterStyle, getGlobalFontFace, getGlobalKeyframes, getGlobalRecipes, hasGlobalKeyframes, hasGlobalRecipes, hasStylesGenerated, isConfigLocked, isTestEnvironment, resetConfig } from "./config.js";
17
18
  import { okhslFunc, okhslPlugin } from "./plugins/okhsl-plugin.js";
@@ -38,7 +39,6 @@ import { mergeStyles } from "./utils/merge-styles.js";
38
39
  import { resolveRecipes } from "./utils/resolve-recipes.js";
39
40
  import { deprecationWarning, warn } from "./utils/warnings.js";
40
41
  import { processTokens } from "./utils/process-tokens.js";
41
- import { TypographyPreset, generateTypographyTokens } from "./utils/typography.js";
42
42
  import { tastyDebug } from "./debug.js";
43
43
  import { CSSProperties as CSSProperties$1 } from "react";
44
44
 
@@ -47,5 +47,5 @@ declare module './utils/css-types' {
47
47
  interface CSSProperties extends CSSProperties$1 {}
48
48
  }
49
49
  //#endregion
50
- export { type AllBaseProps, type AllBasePropsWithMods, AtRuleContext, BASE_STYLES, BLOCK_INNER_STYLES, BLOCK_OUTER_STYLES, BLOCK_STYLES, type BaseProps, type BasePropsWithoutChildren, BaseStyleProps, BlockInnerStyleProps, BlockOuterStyleProps, BlockStyleProps, Bucket, CHUNK_NAMES, COLOR_STYLES, CONTAINER_STYLES, CSSMap, CSSProperties, CUSTOM_UNITS, CacheMetrics, ChunkInfo, ChunkName, ColorSpace, ColorStyleProps, ComputeStylesOptions, ComputeStylesResult, ConditionNode, ConfigTokenValue, ConfigTokens, ContainerStyleProps, CounterStyleDescriptors, DIMENSION_STYLES, DIRECTIONS, DimensionStyleProps, DisposeFunction, Element, type ElementsDefinition, FLOW_STYLES, FlowStyleProps, FontFaceDescriptors, FontFaceInput, GCConfig, GCOptions, GlobalStyledProps, INNER_STYLES, InjectResult, InnerStyleProps, KeyframesCacheEntry, KeyframesInfo, KeyframesResult, KeyframesSteps, type ModPropDef, type ModPropsInput, ModValue, Mods, NoType, NotSelector, OUTER_STYLES, OuterStyleProps, POSITION_STYLES, ParseStateKeyOptions, ParsedAdvancedState, ParsedColor, ParserOptions, PositionStyleProps, ProcessedStyle, PropertyDefinition, PropertyOptions, Props, RawCSSResult, RawStyleHandler, RecipeStyles, RenderResult, type ResolveModPropDef, type ResolveModProps, type ResolveTokenProps, RootRegistry, RuleInfo, STYLE_TO_CHUNK, Selector, SheetInfo, SheetManager, ShortGridStyles, StateParserContext, StyleDetails, StyleDetailsPart, StyleHandler, StyleHandlerDefinition, StyleHandlerResult, StyleInjector, StyleInjectorConfig, StyleMap, StyleParser, StylePropValue, StyleResult, StyleRule, StyleUsage, StyleValue, StyleValueStateMap, Styles, StylesInterface, StylesWithoutSelectors, type SubElementDefinition, type SubElementProps, SuffixForSelector, TEXT_STYLES, TagName, TastyConfig, type TastyElementOptions, type TastyElementProps, TastyExtensionConfig, TastyNamedColors, TastyPlugin, TastyPluginFactory, TastyPresetNames, type TastyProps, TastyThemeNames, TextStyleProps, type TokenPropsInput, TokenValue, Tokens, TypographyPreset, UnitHandler, type UsePropertyOptions, type UseStylesOptions, type UseStylesResult, type VariantMap, type WithVariant, categorizeStyleKeys, 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, getNamedColorHex, getRawCSSText, getRgbValuesFromRgbaString, hasGlobalKeyframes, hasGlobalRecipes, hasStylesGenerated, hexToRgb, hslToRgbValues, inject, injectGlobal, injectRawCSS, injector, isConfigLocked, isPropertyDefined, isSelector, isTestEnvironment, keyframes, mergeStyles, _modAttrs as modAttrs, normalizeColorTokenValue, okhslFunc, okhslPlugin, 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 };
50
+ export { type AllBaseProps, type AllBasePropsWithMods, AtRuleContext, BASE_STYLES, BLOCK_INNER_STYLES, BLOCK_OUTER_STYLES, BLOCK_STYLES, type BaseProps, type BasePropsWithoutChildren, BaseStyleProps, BlockInnerStyleProps, BlockOuterStyleProps, BlockStyleProps, Bucket, CHUNK_NAMES, COLOR_STYLES, CONTAINER_STYLES, CSSMap, CSSProperties, CUSTOM_UNITS, CacheMetrics, ChunkInfo, ChunkName, ColorSpace, ColorStyleProps, ComputeStylesOptions, ComputeStylesResult, ConditionNode, ConfigTokenValue, ConfigTokens, ContainerStyleProps, CounterStyleDescriptors, DIMENSION_STYLES, DIRECTIONS, DimensionStyleProps, DisposeFunction, Element, type ElementsDefinition, FLOW_STYLES, FlowStyleProps, FontFaceDescriptors, FontFaceInput, GCConfig, GCOptions, GlobalStyledProps, INNER_STYLES, InjectResult, InnerStyleProps, KeyframesCacheEntry, KeyframesInfo, KeyframesResult, KeyframesSteps, type ModPropDef, type ModPropsInput, ModValue, Mods, NoType, NotSelector, OUTER_STYLES, OuterStyleProps, POSITION_STYLES, ParseStateKeyOptions, ParsedAdvancedState, ParsedColor, ParserOptions, PositionStyleProps, ProcessedStyle, PropertyDefinition, PropertyOptions, Props, RawCSSResult, RawStyleHandler, RecipeStyles, RenderResult, type ResolveModPropDef, type ResolveModProps, type ResolveTokenProps, RootRegistry, RuleInfo, STYLE_TO_CHUNK, Selector, SheetInfo, SheetManager, ShortGridStyles, StateParserContext, StyleDetails, StyleDetailsPart, StyleHandler, StyleHandlerDefinition, StyleHandlerResult, StyleInjector, StyleInjectorConfig, StyleMap, StyleParser, StylePropValue, StyleResult, StyleRule, StyleUsage, StyleValue, StyleValueStateMap, Styles, StylesInterface, StylesWithoutSelectors, type SubElementDefinition, type SubElementProps, SuffixForSelector, TEXT_STYLES, TagName, TastyConfig, type TastyElementOptions, type TastyElementProps, TastyExtensionConfig, TastyNamedColors, TastyPlugin, TastyPluginFactory, TastyPresetNames, type TastyProps, TastyThemeNames, TextStyleProps, type TokenPropsInput, TokenValue, Tokens, TypographyPreset, TypographyTokenValue, UnitHandler, type UsePropertyOptions, type UseStylesOptions, type UseStylesResult, type VariantMap, type WithVariant, categorizeStyleKeys, 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, getNamedColorHex, getRawCSSText, getRgbValuesFromRgbaString, hasGlobalKeyframes, hasGlobalRecipes, hasStylesGenerated, hexToRgb, hslToRgbValues, inject, injectGlobal, injectRawCSS, injector, isConfigLocked, isPropertyDefined, isSelector, isTestEnvironment, keyframes, mergeStyles, _modAttrs as modAttrs, normalizeColorTokenValue, okhslFunc, okhslPlugin, 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 };
51
51
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@ import { StyleInjector } from "./injector/injector.js";
10
10
  import { createStateParserContext, getGlobalPredefinedStates, setGlobalPredefinedStates } from "./states/index.js";
11
11
  import { parseStateKey } from "./pipeline/parseStateKey.js";
12
12
  import { isSelector, renderStyles } from "./pipeline/index.js";
13
+ import { generateTypographyTokens } from "./utils/typography.js";
13
14
  import { configure, getConfig, getGlobalCounterStyle, getGlobalFontFace, getGlobalKeyframes, getGlobalRecipes, hasGlobalKeyframes, hasGlobalRecipes, hasStylesGenerated, isConfigLocked, isTestEnvironment, resetConfig } from "./config.js";
14
15
  import { CHUNK_NAMES, STYLE_TO_CHUNK, categorizeStyleKeys } from "./chunks/definitions.js";
15
16
  import { BASE_STYLES, BLOCK_INNER_STYLES, BLOCK_OUTER_STYLES, BLOCK_STYLES, COLOR_STYLES, CONTAINER_STYLES, DIMENSION_STYLES, FLOW_STYLES, INNER_STYLES, OUTER_STYLES, POSITION_STYLES, TEXT_STYLES } from "./styles/list.js";
@@ -22,7 +23,6 @@ import { color } from "./utils/colors.js";
22
23
  import { _modAttrs } from "./utils/mod-attrs.js";
23
24
  import { dotize } from "./utils/dotize.js";
24
25
  import { processTokens } from "./utils/process-tokens.js";
25
- import { generateTypographyTokens } from "./utils/typography.js";
26
26
  import { tastyDebug } from "./debug.js";
27
27
  import { getDisplayName } from "./utils/get-display-name.js";
28
28
  import { Element, tasty } from "./tasty.js";
@@ -119,7 +119,7 @@ function getCssTextForNode(node, options) {
119
119
  const readClasses = (el) => {
120
120
  const cls = el.getAttribute("class");
121
121
  if (!cls) return;
122
- for (const token of cls.split(/\s+/)) if (/^t\d+$/.test(token)) classSet.add(token);
122
+ for (const token of cls.split(/\s+/)) if (/^t[a-z0-9]+$/.test(token)) classSet.add(token);
123
123
  };
124
124
  if (node.getAttribute) readClasses(node);
125
125
  const elements = node.querySelectorAll ? node.querySelectorAll("[class]") : [];
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/injector/index.ts"],"sourcesContent":["import {\n getConfig,\n getGlobalInjector,\n isTestEnvironment,\n markStylesGenerated,\n} from '../config';\nimport type { StyleResult } from '../pipeline';\n\nimport { StyleInjector } from './injector';\nimport type {\n CounterStyleDescriptors,\n FontFaceDescriptors,\n GCOptions,\n GlobalInjectResult,\n InjectResult,\n KeyframesResult,\n KeyframesSteps,\n StyleInjectorConfig,\n} from './types';\n\n/**\n * Inject styles and return className with dispose function\n */\nexport function inject(\n rules: StyleResult[],\n options?: { root?: Document | ShadowRoot; cacheKey?: string },\n): InjectResult {\n const injector = getGlobalInjector();\n\n markStylesGenerated();\n\n return injector.inject(rules, options);\n}\n\n/**\n * Inject global rules that should not reserve tasty class names\n */\nexport function injectGlobal(\n rules: StyleResult[],\n options?: { root?: Document | ShadowRoot },\n): GlobalInjectResult {\n return getGlobalInjector().injectGlobal(rules, options);\n}\n\n/**\n * Inject raw CSS text directly without parsing\n * This is a low-overhead method for injecting raw CSS that doesn't need tasty processing.\n * The CSS is inserted into a separate style element to avoid conflicts with tasty's chunking.\n *\n * @example\n * ```tsx\n * // Inject raw CSS\n * const { dispose } = injectRawCSS(`\n * body { margin: 0; padding: 0; }\n * .my-class { color: red; }\n * `);\n *\n * // Later, remove the injected CSS\n * dispose();\n * ```\n */\nexport function injectRawCSS(\n css: string,\n options?: { root?: Document | ShadowRoot },\n): { dispose: () => void } {\n return getGlobalInjector().injectRawCSS(css, options);\n}\n\n/**\n * Get raw CSS text for SSR\n */\nexport function getRawCSSText(options?: {\n root?: Document | ShadowRoot;\n}): string {\n return getGlobalInjector().getRawCSSText(options);\n}\n\n/**\n * Inject keyframes and return object with toString() and dispose()\n */\nexport function keyframes(\n steps: KeyframesSteps,\n nameOrOptions?: string | { root?: Document | ShadowRoot; name?: string },\n): KeyframesResult {\n return getGlobalInjector().keyframes(steps, nameOrOptions);\n}\n\nexport interface PropertyOptions {\n /**\n * CSS syntax string for the property (e.g., '<color>', '<length>', '<angle>')\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/@property/syntax\n */\n syntax?: string;\n /**\n * Whether the property inherits from parent elements\n * @default true\n */\n inherits?: boolean;\n /**\n * Initial value for the property\n */\n initialValue?: string | number;\n /**\n * Shadow root or document to inject into\n */\n root?: Document | ShadowRoot;\n}\n\n/**\n * Define a CSS @property custom property.\n * This enables advanced features like animating custom properties.\n *\n * Note: @property rules are global and persistent once defined.\n * Re-registering the same property name is a no-op.\n *\n * @param name - The custom property name (must start with --)\n * @param options - Property configuration\n *\n * @example\n * ```ts\n * // Define a color property that can be animated\n * property('--my-color', {\n * syntax: '<color>',\n * initialValue: 'red',\n * });\n *\n * // Define an angle property\n * property('--rotation', {\n * syntax: '<angle>',\n * inherits: false,\n * initialValue: '0deg',\n * });\n * ```\n */\nexport function property(name: string, options?: PropertyOptions): void {\n return getGlobalInjector().property(name, options);\n}\n\n/**\n * Check if a CSS @property has already been defined\n *\n * @param name - The custom property name to check\n * @param options - Options including root\n */\nexport function isPropertyDefined(\n name: string,\n options?: { root?: Document | ShadowRoot },\n): boolean {\n return getGlobalInjector().isPropertyDefined(name, options);\n}\n\n/**\n * Inject a CSS @font-face rule.\n *\n * Permanent and global — no dispose or ref-counting.\n * Deduplicates by content hash (family + descriptors).\n */\nexport function fontFace(\n family: string,\n descriptors: FontFaceDescriptors,\n options?: { root?: Document | ShadowRoot },\n): void {\n return getGlobalInjector().fontFace(family, descriptors, options);\n}\n\n/**\n * Inject a CSS @counter-style rule.\n *\n * Permanent and global — no dispose or ref-counting.\n * Deduplicates by name (first definition wins).\n */\nexport function counterStyle(\n name: string,\n descriptors: CounterStyleDescriptors,\n options?: { root?: Document | ShadowRoot },\n): void {\n return getGlobalInjector().counterStyle(name, descriptors, options);\n}\n\n/**\n * Get CSS text from all sheets (for SSR)\n */\nexport function getCssText(options?: { root?: Document | ShadowRoot }): string {\n return getGlobalInjector().getCssText(options);\n}\n\n/**\n * Collect only CSS used by a rendered subtree (like jest-styled-components).\n * Pass the container returned by render(...).\n */\nexport function getCssTextForNode(\n node: ParentNode | Element | DocumentFragment,\n options?: { root?: Document | ShadowRoot },\n): string {\n // Collect tasty-generated class names (t<number>) from the subtree\n const classSet = new Set<string>();\n\n const readClasses = (el: Element) => {\n const cls = el.getAttribute('class');\n if (!cls) return;\n for (const token of cls.split(/\\s+/)) {\n if (/^t\\d+$/.test(token)) classSet.add(token);\n }\n };\n\n // Include node itself if it's an Element\n if ((node as Element).getAttribute) {\n readClasses(node as Element);\n }\n // Walk descendants\n const elements = (node as ParentNode).querySelectorAll\n ? (node as ParentNode).querySelectorAll('[class]')\n : ([] as unknown as NodeListOf<Element>);\n if (elements) elements.forEach(readClasses);\n\n return getGlobalInjector().getCssTextForClasses(classSet, options);\n}\n\n/**\n * Force cleanup of unused rules\n */\nexport function cleanup(root?: Document | ShadowRoot): void {\n return getGlobalInjector().cleanup(root);\n}\n\n/**\n * Record a render-time usage hit for one or more classNames.\n * Used internally by computeStyles and tasty() to track usage for GC.\n * When the global touch counter reaches `touchInterval`, schedules GC.\n */\nexport function touch(\n className: string,\n options?: { root?: Document | ShadowRoot },\n): void {\n if (!getConfig().gc) return;\n getGlobalInjector().touch(className, options);\n}\n\n/**\n * Synchronous garbage collection of unused styles.\n * Evicts the oldest unused styles when usageMap exceeds capacity.\n * With `{ force: true }`, removes ALL unused styles regardless of capacity.\n *\n * @returns Number of styles evicted.\n */\nexport function gc(options?: GCOptions): number {\n return getGlobalInjector().gc(options);\n}\n\n/**\n * Check if we're currently running in a test environment\n */\nexport function getIsTestEnvironment(): boolean {\n return isTestEnvironment();\n}\n\n/**\n * Get the global injector instance for debugging\n */\nexport const injector = {\n get instance() {\n return getGlobalInjector();\n },\n};\n\n/**\n * Destroy all resources and clean up\n */\nexport function destroy(root?: Document | ShadowRoot): void {\n return getGlobalInjector().destroy(root);\n}\n\n/**\n * Create a new isolated injector instance\n */\nexport function createInjector(\n config: Partial<StyleInjectorConfig> = {},\n): StyleInjector {\n const defaultConfig = getConfig();\n\n const fullConfig: StyleInjectorConfig = {\n ...defaultConfig,\n // Auto-enable forceTextInjection in test environments\n forceTextInjection: config.forceTextInjection ?? isTestEnvironment(),\n ...config,\n };\n\n return new StyleInjector(fullConfig);\n}\n\n// Re-export types\nexport type {\n StyleInjectorConfig,\n InjectResult,\n DisposeFunction,\n RuleInfo,\n SheetInfo,\n RootRegistry,\n StyleRule,\n KeyframesInfo,\n KeyframesResult,\n KeyframesSteps,\n KeyframesCacheEntry,\n CacheMetrics,\n RawCSSResult,\n PropertyDefinition,\n FontFaceDescriptors,\n FontFaceInput,\n CounterStyleDescriptors,\n StyleUsage,\n GCConfig,\n GCOptions,\n} from './types';\n\nexport { StyleInjector } from './injector';\nexport { SheetManager } from './sheet-manager';\n"],"mappings":";;;;;;;AAuBA,SAAgB,OACd,OACA,SACc;CACd,MAAM,WAAW,mBAAmB;AAEpC,sBAAqB;AAErB,QAAO,SAAS,OAAO,OAAO,QAAQ;;;;;AAMxC,SAAgB,aACd,OACA,SACoB;AACpB,QAAO,mBAAmB,CAAC,aAAa,OAAO,QAAQ;;;;;;;;;;;;;;;;;;;AAoBzD,SAAgB,aACd,KACA,SACyB;AACzB,QAAO,mBAAmB,CAAC,aAAa,KAAK,QAAQ;;;;;AAMvD,SAAgB,cAAc,SAEnB;AACT,QAAO,mBAAmB,CAAC,cAAc,QAAQ;;;;;AAMnD,SAAgB,UACd,OACA,eACiB;AACjB,QAAO,mBAAmB,CAAC,UAAU,OAAO,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkD5D,SAAgB,SAAS,MAAc,SAAiC;AACtE,QAAO,mBAAmB,CAAC,SAAS,MAAM,QAAQ;;;;;;;;AASpD,SAAgB,kBACd,MACA,SACS;AACT,QAAO,mBAAmB,CAAC,kBAAkB,MAAM,QAAQ;;;;;;;;AAS7D,SAAgB,SACd,QACA,aACA,SACM;AACN,QAAO,mBAAmB,CAAC,SAAS,QAAQ,aAAa,QAAQ;;;;;;;;AASnE,SAAgB,aACd,MACA,aACA,SACM;AACN,QAAO,mBAAmB,CAAC,aAAa,MAAM,aAAa,QAAQ;;;;;AAMrE,SAAgB,WAAW,SAAoD;AAC7E,QAAO,mBAAmB,CAAC,WAAW,QAAQ;;;;;;AAOhD,SAAgB,kBACd,MACA,SACQ;CAER,MAAM,2BAAW,IAAI,KAAa;CAElC,MAAM,eAAe,OAAgB;EACnC,MAAM,MAAM,GAAG,aAAa,QAAQ;AACpC,MAAI,CAAC,IAAK;AACV,OAAK,MAAM,SAAS,IAAI,MAAM,MAAM,CAClC,KAAI,SAAS,KAAK,MAAM,CAAE,UAAS,IAAI,MAAM;;AAKjD,KAAK,KAAiB,aACpB,aAAY,KAAgB;CAG9B,MAAM,WAAY,KAAoB,mBACjC,KAAoB,iBAAiB,UAAU,GAC/C,EAAE;AACP,KAAI,SAAU,UAAS,QAAQ,YAAY;AAE3C,QAAO,mBAAmB,CAAC,qBAAqB,UAAU,QAAQ;;;;;AAMpE,SAAgB,QAAQ,MAAoC;AAC1D,QAAO,mBAAmB,CAAC,QAAQ,KAAK;;;;;;;AAQ1C,SAAgB,MACd,WACA,SACM;AACN,KAAI,CAAC,WAAW,CAAC,GAAI;AACrB,oBAAmB,CAAC,MAAM,WAAW,QAAQ;;;;;;;;;AAU/C,SAAgB,GAAG,SAA6B;AAC9C,QAAO,mBAAmB,CAAC,GAAG,QAAQ;;;;;AAMxC,SAAgB,uBAAgC;AAC9C,QAAO,mBAAmB;;;;;AAM5B,MAAa,WAAW,EACtB,IAAI,WAAW;AACb,QAAO,mBAAmB;GAE7B;;;;AAKD,SAAgB,QAAQ,MAAoC;AAC1D,QAAO,mBAAmB,CAAC,QAAQ,KAAK;;;;;AAM1C,SAAgB,eACd,SAAuC,EAAE,EAC1B;AAUf,QAAO,IAAI,cAP6B;EACtC,GAHoB,WAAW;EAK/B,oBAAoB,OAAO,sBAAsB,mBAAmB;EACpE,GAAG;EACJ,CAEmC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/injector/index.ts"],"sourcesContent":["import {\n getConfig,\n getGlobalInjector,\n isTestEnvironment,\n markStylesGenerated,\n} from '../config';\nimport type { StyleResult } from '../pipeline';\n\nimport { StyleInjector } from './injector';\nimport type {\n CounterStyleDescriptors,\n FontFaceDescriptors,\n GCOptions,\n GlobalInjectResult,\n InjectResult,\n KeyframesResult,\n KeyframesSteps,\n StyleInjectorConfig,\n} from './types';\n\n/**\n * Inject styles and return className with dispose function\n */\nexport function inject(\n rules: StyleResult[],\n options?: { root?: Document | ShadowRoot; cacheKey?: string },\n): InjectResult {\n const injector = getGlobalInjector();\n\n markStylesGenerated();\n\n return injector.inject(rules, options);\n}\n\n/**\n * Inject global rules that should not reserve tasty class names\n */\nexport function injectGlobal(\n rules: StyleResult[],\n options?: { root?: Document | ShadowRoot },\n): GlobalInjectResult {\n return getGlobalInjector().injectGlobal(rules, options);\n}\n\n/**\n * Inject raw CSS text directly without parsing\n * This is a low-overhead method for injecting raw CSS that doesn't need tasty processing.\n * The CSS is inserted into a separate style element to avoid conflicts with tasty's chunking.\n *\n * @example\n * ```tsx\n * // Inject raw CSS\n * const { dispose } = injectRawCSS(`\n * body { margin: 0; padding: 0; }\n * .my-class { color: red; }\n * `);\n *\n * // Later, remove the injected CSS\n * dispose();\n * ```\n */\nexport function injectRawCSS(\n css: string,\n options?: { root?: Document | ShadowRoot },\n): { dispose: () => void } {\n return getGlobalInjector().injectRawCSS(css, options);\n}\n\n/**\n * Get raw CSS text for SSR\n */\nexport function getRawCSSText(options?: {\n root?: Document | ShadowRoot;\n}): string {\n return getGlobalInjector().getRawCSSText(options);\n}\n\n/**\n * Inject keyframes and return object with toString() and dispose()\n */\nexport function keyframes(\n steps: KeyframesSteps,\n nameOrOptions?: string | { root?: Document | ShadowRoot; name?: string },\n): KeyframesResult {\n return getGlobalInjector().keyframes(steps, nameOrOptions);\n}\n\nexport interface PropertyOptions {\n /**\n * CSS syntax string for the property (e.g., '<color>', '<length>', '<angle>')\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/@property/syntax\n */\n syntax?: string;\n /**\n * Whether the property inherits from parent elements\n * @default true\n */\n inherits?: boolean;\n /**\n * Initial value for the property\n */\n initialValue?: string | number;\n /**\n * Shadow root or document to inject into\n */\n root?: Document | ShadowRoot;\n}\n\n/**\n * Define a CSS @property custom property.\n * This enables advanced features like animating custom properties.\n *\n * Note: @property rules are global and persistent once defined.\n * Re-registering the same property name is a no-op.\n *\n * @param name - The custom property name (must start with --)\n * @param options - Property configuration\n *\n * @example\n * ```ts\n * // Define a color property that can be animated\n * property('--my-color', {\n * syntax: '<color>',\n * initialValue: 'red',\n * });\n *\n * // Define an angle property\n * property('--rotation', {\n * syntax: '<angle>',\n * inherits: false,\n * initialValue: '0deg',\n * });\n * ```\n */\nexport function property(name: string, options?: PropertyOptions): void {\n return getGlobalInjector().property(name, options);\n}\n\n/**\n * Check if a CSS @property has already been defined\n *\n * @param name - The custom property name to check\n * @param options - Options including root\n */\nexport function isPropertyDefined(\n name: string,\n options?: { root?: Document | ShadowRoot },\n): boolean {\n return getGlobalInjector().isPropertyDefined(name, options);\n}\n\n/**\n * Inject a CSS @font-face rule.\n *\n * Permanent and global — no dispose or ref-counting.\n * Deduplicates by content hash (family + descriptors).\n */\nexport function fontFace(\n family: string,\n descriptors: FontFaceDescriptors,\n options?: { root?: Document | ShadowRoot },\n): void {\n return getGlobalInjector().fontFace(family, descriptors, options);\n}\n\n/**\n * Inject a CSS @counter-style rule.\n *\n * Permanent and global — no dispose or ref-counting.\n * Deduplicates by name (first definition wins).\n */\nexport function counterStyle(\n name: string,\n descriptors: CounterStyleDescriptors,\n options?: { root?: Document | ShadowRoot },\n): void {\n return getGlobalInjector().counterStyle(name, descriptors, options);\n}\n\n/**\n * Get CSS text from all sheets (for SSR)\n */\nexport function getCssText(options?: { root?: Document | ShadowRoot }): string {\n return getGlobalInjector().getCssText(options);\n}\n\n/**\n * Collect only CSS used by a rendered subtree (like jest-styled-components).\n * Pass the container returned by render(...).\n */\nexport function getCssTextForNode(\n node: ParentNode | Element | DocumentFragment,\n options?: { root?: Document | ShadowRoot },\n): string {\n // Collect tasty-generated class names (t<number>) from the subtree\n const classSet = new Set<string>();\n\n const readClasses = (el: Element) => {\n const cls = el.getAttribute('class');\n if (!cls) return;\n for (const token of cls.split(/\\s+/)) {\n if (/^t[a-z0-9]+$/.test(token)) classSet.add(token);\n }\n };\n\n // Include node itself if it's an Element\n if ((node as Element).getAttribute) {\n readClasses(node as Element);\n }\n // Walk descendants\n const elements = (node as ParentNode).querySelectorAll\n ? (node as ParentNode).querySelectorAll('[class]')\n : ([] as unknown as NodeListOf<Element>);\n if (elements) elements.forEach(readClasses);\n\n return getGlobalInjector().getCssTextForClasses(classSet, options);\n}\n\n/**\n * Force cleanup of unused rules\n */\nexport function cleanup(root?: Document | ShadowRoot): void {\n return getGlobalInjector().cleanup(root);\n}\n\n/**\n * Record a render-time usage hit for one or more classNames.\n * Used internally by computeStyles and tasty() to track usage for GC.\n * When the global touch counter reaches `touchInterval`, schedules GC.\n */\nexport function touch(\n className: string,\n options?: { root?: Document | ShadowRoot },\n): void {\n if (!getConfig().gc) return;\n getGlobalInjector().touch(className, options);\n}\n\n/**\n * Synchronous garbage collection of unused styles.\n * Evicts the oldest unused styles when usageMap exceeds capacity.\n * With `{ force: true }`, removes ALL unused styles regardless of capacity.\n *\n * @returns Number of styles evicted.\n */\nexport function gc(options?: GCOptions): number {\n return getGlobalInjector().gc(options);\n}\n\n/**\n * Check if we're currently running in a test environment\n */\nexport function getIsTestEnvironment(): boolean {\n return isTestEnvironment();\n}\n\n/**\n * Get the global injector instance for debugging\n */\nexport const injector = {\n get instance() {\n return getGlobalInjector();\n },\n};\n\n/**\n * Destroy all resources and clean up\n */\nexport function destroy(root?: Document | ShadowRoot): void {\n return getGlobalInjector().destroy(root);\n}\n\n/**\n * Create a new isolated injector instance\n */\nexport function createInjector(\n config: Partial<StyleInjectorConfig> = {},\n): StyleInjector {\n const defaultConfig = getConfig();\n\n const fullConfig: StyleInjectorConfig = {\n ...defaultConfig,\n // Auto-enable forceTextInjection in test environments\n forceTextInjection: config.forceTextInjection ?? isTestEnvironment(),\n ...config,\n };\n\n return new StyleInjector(fullConfig);\n}\n\n// Re-export types\nexport type {\n StyleInjectorConfig,\n InjectResult,\n DisposeFunction,\n RuleInfo,\n SheetInfo,\n RootRegistry,\n StyleRule,\n KeyframesInfo,\n KeyframesResult,\n KeyframesSteps,\n KeyframesCacheEntry,\n CacheMetrics,\n RawCSSResult,\n PropertyDefinition,\n FontFaceDescriptors,\n FontFaceInput,\n CounterStyleDescriptors,\n StyleUsage,\n GCConfig,\n GCOptions,\n} from './types';\n\nexport { StyleInjector } from './injector';\nexport { SheetManager } from './sheet-manager';\n"],"mappings":";;;;;;;AAuBA,SAAgB,OACd,OACA,SACc;CACd,MAAM,WAAW,mBAAmB;AAEpC,sBAAqB;AAErB,QAAO,SAAS,OAAO,OAAO,QAAQ;;;;;AAMxC,SAAgB,aACd,OACA,SACoB;AACpB,QAAO,mBAAmB,CAAC,aAAa,OAAO,QAAQ;;;;;;;;;;;;;;;;;;;AAoBzD,SAAgB,aACd,KACA,SACyB;AACzB,QAAO,mBAAmB,CAAC,aAAa,KAAK,QAAQ;;;;;AAMvD,SAAgB,cAAc,SAEnB;AACT,QAAO,mBAAmB,CAAC,cAAc,QAAQ;;;;;AAMnD,SAAgB,UACd,OACA,eACiB;AACjB,QAAO,mBAAmB,CAAC,UAAU,OAAO,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkD5D,SAAgB,SAAS,MAAc,SAAiC;AACtE,QAAO,mBAAmB,CAAC,SAAS,MAAM,QAAQ;;;;;;;;AASpD,SAAgB,kBACd,MACA,SACS;AACT,QAAO,mBAAmB,CAAC,kBAAkB,MAAM,QAAQ;;;;;;;;AAS7D,SAAgB,SACd,QACA,aACA,SACM;AACN,QAAO,mBAAmB,CAAC,SAAS,QAAQ,aAAa,QAAQ;;;;;;;;AASnE,SAAgB,aACd,MACA,aACA,SACM;AACN,QAAO,mBAAmB,CAAC,aAAa,MAAM,aAAa,QAAQ;;;;;AAMrE,SAAgB,WAAW,SAAoD;AAC7E,QAAO,mBAAmB,CAAC,WAAW,QAAQ;;;;;;AAOhD,SAAgB,kBACd,MACA,SACQ;CAER,MAAM,2BAAW,IAAI,KAAa;CAElC,MAAM,eAAe,OAAgB;EACnC,MAAM,MAAM,GAAG,aAAa,QAAQ;AACpC,MAAI,CAAC,IAAK;AACV,OAAK,MAAM,SAAS,IAAI,MAAM,MAAM,CAClC,KAAI,eAAe,KAAK,MAAM,CAAE,UAAS,IAAI,MAAM;;AAKvD,KAAK,KAAiB,aACpB,aAAY,KAAgB;CAG9B,MAAM,WAAY,KAAoB,mBACjC,KAAoB,iBAAiB,UAAU,GAC/C,EAAE;AACP,KAAI,SAAU,UAAS,QAAQ,YAAY;AAE3C,QAAO,mBAAmB,CAAC,qBAAqB,UAAU,QAAQ;;;;;AAMpE,SAAgB,QAAQ,MAAoC;AAC1D,QAAO,mBAAmB,CAAC,QAAQ,KAAK;;;;;;;AAQ1C,SAAgB,MACd,WACA,SACM;AACN,KAAI,CAAC,WAAW,CAAC,GAAI;AACrB,oBAAmB,CAAC,MAAM,WAAW,QAAQ;;;;;;;;;AAU/C,SAAgB,GAAG,SAA6B;AAC9C,QAAO,mBAAmB,CAAC,GAAG,QAAQ;;;;;AAMxC,SAAgB,uBAAgC;AAC9C,QAAO,mBAAmB;;;;;AAM5B,MAAa,WAAW,EACtB,IAAI,WAAW;AACb,QAAO,mBAAmB;GAE7B;;;;AAKD,SAAgB,QAAQ,MAAoC;AAC1D,QAAO,mBAAmB,CAAC,QAAQ,KAAK;;;;;AAM1C,SAAgB,eACd,SAAuC,EAAE,EAC1B;AAUf,QAAO,IAAI,cAP6B;EACtC,GAHoB,WAAW;EAK/B,oBAAoB,OAAO,sBAAsB,mBAAmB;EACpE,GAAG;EACJ,CAEmC"}
@@ -7,11 +7,15 @@ declare class StyleInjector {
7
7
  private sheetManager;
8
8
  private config;
9
9
  private globalRuleCounter;
10
- private touchCount;
11
10
  private pendingGCHandle;
12
11
  /** @internal — exposed for debug utilities only */
13
12
  get _sheetManager(): SheetManager;
14
13
  constructor(config?: StyleInjectorConfig);
14
+ /**
15
+ * Check if `className` was hydrated from server-rendered styles and,
16
+ * if so, wire the cacheKey mapping. Returns true on hit.
17
+ */
18
+ private tryHydratedHit;
15
19
  /**
16
20
  * Allocate a className for a cacheKey without injecting styles yet.
17
21
  * This allows separating className allocation (render phase) from style injection (insertion phase).
@@ -170,18 +174,16 @@ declare class StyleInjector {
170
174
  }): void;
171
175
  /**
172
176
  * Schedule a GC via `requestIdleCallback` (or synchronously as fallback).
173
- * Avoids double-scheduling via `pendingGCHandle`.
177
+ * Runs GC on all active roots. Avoids double-scheduling via `pendingGCHandle`.
174
178
  */
175
179
  private scheduleGC;
176
180
  /**
177
181
  * Synchronous garbage collection.
178
182
  *
179
- * 1. Quick check: if non-active entries can't exceed capacity, skip entirely.
183
+ * 1. Quick upper-bound check: skip if unused count can't exceed capacity.
180
184
  * 2. Scans the DOM for live tasty classNames (safety guard).
181
- * 3. Collects unused entries (refCount === 0, not in DOM).
182
- * 4. Checks if unused count exceeds capacity (skip if not, unless `force`).
183
- * 5. Sorts unused by lastTouchedAt ascending, evicts oldest until at capacity.
184
- * With `force: true`, evicts ALL unused regardless of capacity.
185
+ * 3. With `force: true`: deletes all unused entries inline.
186
+ * Without `force`: collects unused, sorts oldest-first, evicts over capacity.
185
187
  *
186
188
  * @returns Number of styles evicted.
187
189
  */