@tenphi/tasty 0.0.0-snapshot.2c5752e → 0.0.0-snapshot.2dc73be

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 (162) hide show
  1. package/README.md +149 -26
  2. package/dist/chunks/definitions.js +1 -2
  3. package/dist/chunks/definitions.js.map +1 -1
  4. package/dist/config.d.ts +8 -0
  5. package/dist/config.js +15 -1
  6. package/dist/config.js.map +1 -1
  7. package/dist/core/index.d.ts +3 -3
  8. package/dist/core/index.js +4 -4
  9. package/dist/debug.js +1 -1
  10. package/dist/debug.js.map +1 -1
  11. package/dist/hooks/useGlobalStyles.d.ts +3 -0
  12. package/dist/hooks/useGlobalStyles.js +28 -1
  13. package/dist/hooks/useGlobalStyles.js.map +1 -1
  14. package/dist/hooks/useKeyframes.js +18 -3
  15. package/dist/hooks/useKeyframes.js.map +1 -1
  16. package/dist/hooks/useProperty.js +36 -13
  17. package/dist/hooks/useProperty.js.map +1 -1
  18. package/dist/hooks/useRawCSS.js +13 -1
  19. package/dist/hooks/useRawCSS.js.map +1 -1
  20. package/dist/hooks/useStyles.d.ts +5 -0
  21. package/dist/hooks/useStyles.js +87 -8
  22. package/dist/hooks/useStyles.js.map +1 -1
  23. package/dist/index.d.ts +11 -3
  24. package/dist/index.js +4 -4
  25. package/dist/injector/index.d.ts +9 -1
  26. package/dist/injector/index.js +9 -1
  27. package/dist/injector/index.js.map +1 -1
  28. package/dist/injector/injector.d.ts +9 -0
  29. package/dist/injector/injector.js +58 -26
  30. package/dist/injector/injector.js.map +1 -1
  31. package/dist/injector/sheet-manager.d.ts +16 -7
  32. package/dist/injector/sheet-manager.js +24 -9
  33. package/dist/injector/sheet-manager.js.map +1 -1
  34. package/dist/injector/types.d.ts +9 -0
  35. package/dist/parser/classify.js.map +1 -1
  36. package/dist/pipeline/conditions.js +14 -8
  37. package/dist/pipeline/conditions.js.map +1 -1
  38. package/dist/pipeline/index.js +59 -40
  39. package/dist/pipeline/index.js.map +1 -1
  40. package/dist/pipeline/materialize.js +93 -145
  41. package/dist/pipeline/materialize.js.map +1 -1
  42. package/dist/pipeline/parseStateKey.js +35 -14
  43. package/dist/pipeline/parseStateKey.js.map +1 -1
  44. package/dist/pipeline/simplify.js +111 -152
  45. package/dist/pipeline/simplify.js.map +1 -1
  46. package/dist/pipeline/warnings.js +18 -0
  47. package/dist/pipeline/warnings.js.map +1 -0
  48. package/dist/properties/index.js +94 -16
  49. package/dist/properties/index.js.map +1 -1
  50. package/dist/properties/property-type-resolver.d.ts +24 -0
  51. package/dist/properties/property-type-resolver.js +87 -0
  52. package/dist/properties/property-type-resolver.js.map +1 -0
  53. package/dist/ssr/astro.d.ts +29 -0
  54. package/dist/ssr/astro.js +65 -0
  55. package/dist/ssr/astro.js.map +1 -0
  56. package/dist/ssr/async-storage.d.ts +17 -0
  57. package/dist/ssr/async-storage.js +35 -0
  58. package/dist/ssr/async-storage.js.map +1 -0
  59. package/dist/ssr/collect-auto-properties.js +40 -0
  60. package/dist/ssr/collect-auto-properties.js.map +1 -0
  61. package/dist/ssr/collector.d.ts +85 -0
  62. package/dist/ssr/collector.js +173 -0
  63. package/dist/ssr/collector.js.map +1 -0
  64. package/dist/ssr/context.d.ts +8 -0
  65. package/dist/ssr/context.js +14 -0
  66. package/dist/ssr/context.js.map +1 -0
  67. package/dist/ssr/format-global-rules.js +22 -0
  68. package/dist/ssr/format-global-rules.js.map +1 -0
  69. package/dist/ssr/format-keyframes.js +70 -0
  70. package/dist/ssr/format-keyframes.js.map +1 -0
  71. package/dist/ssr/format-property.js +48 -0
  72. package/dist/ssr/format-property.js.map +1 -0
  73. package/dist/ssr/format-rules.js +70 -0
  74. package/dist/ssr/format-rules.js.map +1 -0
  75. package/dist/ssr/hydrate.d.ts +22 -0
  76. package/dist/ssr/hydrate.js +50 -0
  77. package/dist/ssr/hydrate.js.map +1 -0
  78. package/dist/ssr/index.d.ts +5 -0
  79. package/dist/ssr/index.js +12 -0
  80. package/dist/ssr/index.js.map +1 -0
  81. package/dist/ssr/next.d.ts +45 -0
  82. package/dist/ssr/next.js +71 -0
  83. package/dist/ssr/next.js.map +1 -0
  84. package/dist/ssr/ssr-collector-ref.js +12 -0
  85. package/dist/ssr/ssr-collector-ref.js.map +1 -0
  86. package/dist/styles/align.d.ts +1 -1
  87. package/dist/styles/align.js.map +1 -1
  88. package/dist/styles/border.d.ts +1 -1
  89. package/dist/styles/border.js.map +1 -1
  90. package/dist/styles/color.d.ts +2 -2
  91. package/dist/styles/color.js.map +1 -1
  92. package/dist/styles/createStyle.js +1 -1
  93. package/dist/styles/createStyle.js.map +1 -1
  94. package/dist/styles/fade.d.ts +1 -1
  95. package/dist/styles/fade.js.map +1 -1
  96. package/dist/styles/fill.d.ts +14 -16
  97. package/dist/styles/fill.js +6 -5
  98. package/dist/styles/fill.js.map +1 -1
  99. package/dist/styles/flow.d.ts +3 -3
  100. package/dist/styles/flow.js.map +1 -1
  101. package/dist/styles/inset.d.ts +3 -1
  102. package/dist/styles/inset.js +48 -40
  103. package/dist/styles/inset.js.map +1 -1
  104. package/dist/styles/justify.d.ts +1 -1
  105. package/dist/styles/justify.js.map +1 -1
  106. package/dist/styles/margin.d.ts +1 -5
  107. package/dist/styles/margin.js +48 -40
  108. package/dist/styles/margin.js.map +1 -1
  109. package/dist/styles/padding.d.ts +1 -5
  110. package/dist/styles/padding.js +48 -40
  111. package/dist/styles/padding.js.map +1 -1
  112. package/dist/styles/predefined.d.ts +0 -2
  113. package/dist/styles/predefined.js +0 -3
  114. package/dist/styles/predefined.js.map +1 -1
  115. package/dist/styles/preset.js +1 -1
  116. package/dist/styles/preset.js.map +1 -1
  117. package/dist/styles/radius.d.ts +1 -1
  118. package/dist/styles/radius.js.map +1 -1
  119. package/dist/styles/scrollbar.d.ts +9 -5
  120. package/dist/styles/scrollbar.js +25 -82
  121. package/dist/styles/scrollbar.js.map +1 -1
  122. package/dist/styles/shadow.d.ts +2 -2
  123. package/dist/styles/shadow.js.map +1 -1
  124. package/dist/styles/transition.d.ts +1 -1
  125. package/dist/styles/transition.js +27 -7
  126. package/dist/styles/transition.js.map +1 -1
  127. package/dist/styles/types.d.ts +10 -13
  128. package/dist/tasty.d.ts +11 -12
  129. package/dist/tasty.js +16 -1
  130. package/dist/tasty.js.map +1 -1
  131. package/dist/types.d.ts +2 -2
  132. package/dist/utils/colors.d.ts +1 -1
  133. package/dist/utils/colors.js.map +1 -1
  134. package/dist/utils/css-types.d.ts +1 -1
  135. package/dist/utils/dotize.d.ts +1 -1
  136. package/dist/utils/mod-attrs.js.map +1 -1
  137. package/dist/utils/selector-transform.js +32 -0
  138. package/dist/utils/selector-transform.js.map +1 -0
  139. package/dist/utils/string.js.map +1 -1
  140. package/dist/utils/styles.d.ts +8 -14
  141. package/dist/utils/styles.js +175 -30
  142. package/dist/utils/styles.js.map +1 -1
  143. package/dist/zero/babel.d.ts +21 -2
  144. package/dist/zero/babel.js +45 -8
  145. package/dist/zero/babel.js.map +1 -1
  146. package/dist/zero/extractor.js +66 -1
  147. package/dist/zero/extractor.js.map +1 -1
  148. package/dist/zero/next.d.ts +29 -30
  149. package/dist/zero/next.js +49 -39
  150. package/dist/zero/next.js.map +1 -1
  151. package/docs/configuration.md +211 -0
  152. package/docs/debug.md +505 -0
  153. package/docs/injector.md +528 -0
  154. package/docs/ssr.md +372 -0
  155. package/docs/styles.md +574 -0
  156. package/docs/tasty-static.md +376 -0
  157. package/docs/usage.md +695 -0
  158. package/package.json +99 -31
  159. package/tasty.config.ts +14 -0
  160. package/dist/styles/styledScrollbar.d.ts +0 -47
  161. package/dist/styles/styledScrollbar.js +0 -38
  162. package/dist/styles/styledScrollbar.js.map +0 -1
package/README.md CHANGED
@@ -17,21 +17,23 @@
17
17
 
18
18
  ---
19
19
 
20
- Most CSS-in-JS libraries generate CSS. Tasty generates **mutually exclusive CSS** — for any combination of states, exactly one rule matches per property. No cascade conflicts, no specificity wars, no `!important` hacks. Components compose and extend without breaking each other. That's the foundation everything else is built on.
20
+ Most CSS-in-JS libraries emit rules that compete through cascade and specificity. Tasty emits **mutually exclusive CSS selectors** — for any component state combination, exactly one selector matches each property at a time. No cascade conflicts, no specificity wars, no `!important` escapes. Components compose and extend without breaking each other.
21
21
 
22
- On top of that foundation, Tasty gives you a concise, CSS-like DSL with design tokens, custom units, responsive states, container queries, dark mode, sub-element styling, and zero-runtime extraction all in one coherent system that scales from a single component to an enterprise design system.
22
+ That guarantee unlocks a concise, CSS-like DSL where design tokens, custom units, responsive states, container queries, sub-element styling, and theming all compose without surprises one coherent system that scales from a single component to an enterprise design system.
23
23
 
24
24
  ## Why Tasty
25
25
 
26
- - **Deterministic at any scale** — Exclusive selector generation eliminates the entire class of cascade/specificity bugs. Every state combination resolves to exactly one CSS rule per property. Refactor freely.
27
- - **DSL that feels like CSS** — Property names you already know (`padding`, `color`, `display`) with syntax sugar that removes boilerplate. Learn the DSL in minutes, not days.
28
- - **Design-system native** — Color tokens (`#primary`), spacing units (`2x`), typography presets (`h1`, `t2`), border radius (`1r`), and recipes are first-class primitives, not afterthoughts.
29
- - **Full modern CSS coverage** — Media queries, container queries, `@supports`, `:has()`, `@starting-style`, `@property`, keyframes, boolean state logic with `&`, `|`, `!` operators. If CSS can do it, Tasty can express it concisely.
30
- - **Runtime or zero-runtime — your call** — Use `tasty()` for dynamic React components with runtime injection, or `tastyStatic()` with the Babel plugin for zero-runtime CSS extraction. Same DSL, same tokens, same output.
26
+ - **Deterministic at any scale** — Exclusive selector generation eliminates the entire class of cascade/specificity bugs. Every state combination resolves to exactly one CSS rule per property. Refactor freely. See [How It Actually Works](#how-it-actually-works).
27
+ - **AI-friendly by design** — Style definitions are declarative, self-contained, and structurally consistent. AI tools can read, understand, and refactor even advanced state bindings as confidently as a human — because there's no hidden cascade logic or implicit ordering to second-guess.
28
+ - **DSL that feels like CSS** — Property names you already know (`padding`, `color`, `display`) with syntax sugar that removes boilerplate. Learn the DSL in minutes, not days. See [Style Properties](docs/styles.md).
29
+ - **CSS properties as normal component props** — `styleProps` lets you expose selected styles as typed React props. Use `<Button placeSelf="end">` or `<Space flow="row" gap="2x">` without extra wrappers, utility classes, or `styles` overrides. The same props also accept state maps, so responsive values work with the same API. See [CSS properties as props](#css-properties-as-props).
30
+ - **Design-system native** — Color tokens (`#primary`), spacing units (`2x`), typography presets (`h1`, `t2`), border radius (`1r`), and recipes are first-class primitives, not afterthoughts. See [Configuration](docs/configuration.md).
31
+ - **Near-complete modern CSS coverage** — Media queries, container queries, `@supports`, `:has()`, `@starting-style`, `@property`, `@keyframes`, etc. Some features that don't fit Tasty's component model (such as `@layer` and `!important`) are intentionally omitted, but real-world use cases are covered almost completely.
32
+ - **Runtime, zero-runtime, or SSR — your call** — Use `tasty()` for dynamic React components with runtime injection, `tastyStatic()` with the Babel plugin for zero-runtime CSS extraction, or enable SSR with zero-cost client hydration for Next.js, Astro, or any React framework (experimental). Same DSL, same tokens, same output.
31
33
  - **Only generate what is used** — In runtime mode, Tasty injects CSS on demand for mounted components/variants, so your app avoids shipping style rules for UI states that are never rendered.
32
34
  - **Runtime performance that holds at scale** — The runtime path is tested against enterprise-scale applications and tuned with multi-level caching, chunk-level style reuse, style garbage collection, and a dedicated injector.
33
35
  - **Composable and extensible by design** — Extend any component's styles with proper merge semantics, and evolve built-in behavior through configuration and plugins.
34
- - **TypeScript-first** — Full type definitions, module augmentation for custom properties, and autocomplete for tokens, presets, and themes.
36
+ - **TypeScript-first** — Full type definitions, module augmentation for custom properties, and autocomplete for tokens, presets, and themes. See [Configuration](docs/configuration.md).
35
37
 
36
38
  ## Installation
37
39
 
@@ -129,13 +131,70 @@ configure({
129
131
 
130
132
  Predefined states turn complex selector logic into single tokens. Use `@mobile` instead of writing media query expressions in every component.
131
133
 
134
+ ### CSS properties as props
135
+
136
+ With `styleProps`, a component can expose the styles you choose as normal typed props. That means you can adjust layout, spacing, alignment, or positioning right where the component is used, instead of introducing wrapper elements or reaching for a separate styling API.
137
+
138
+ This is especially good for prototyping and fast UI iteration: you can shape interfaces quickly, while still staying inside a typed, design-system-aware component API that scales to production.
139
+
140
+ ```tsx
141
+ import { tasty, FLOW_STYLES, POSITION_STYLES } from '@tenphi/tasty';
142
+
143
+ const Space = tasty({
144
+ styles: {
145
+ display: 'flex',
146
+ flow: 'column',
147
+ gap: '1x',
148
+ },
149
+ styleProps: FLOW_STYLES,
150
+ });
151
+
152
+ const Button = tasty({
153
+ as: 'button',
154
+ styles: {
155
+ padding: '1.5x 3x',
156
+ fill: '#primary',
157
+ color: '#primary-text',
158
+ radius: true,
159
+ },
160
+ styleProps: POSITION_STYLES,
161
+ });
162
+ ```
163
+
164
+ Now you can compose layout and tweak component positioning directly in JSX:
165
+
166
+ ```tsx
167
+ <Space flow="row" gap="2x" placeItems="center">
168
+ <Title>Dashboard</Title>
169
+ <Button placeSelf="end">Add Item</Button>
170
+ </Space>
171
+ ```
172
+
173
+ The same props also support state maps, so responsive values use the exact same API:
174
+
175
+ ```tsx
176
+ <Space
177
+ flow={{ '': 'column', '@tablet': 'row' }}
178
+ gap={{ '': '2x', '@tablet': '4x' }}
179
+ >
180
+ <Sidebar />
181
+ <Content />
182
+ </Space>
183
+ ```
184
+
185
+ Layout components can expose flow props. Buttons can expose positioning props. Each component can offer only the style props that make sense for its role, while still keeping tokens, custom units, and state maps fully typed. This works in runtime `tasty()` components, not in `tastyStatic()`.
186
+
132
187
  ## How It Actually Works
133
188
 
134
189
  This is the core idea that makes everything else possible.
135
190
 
136
- Traditional CSS uses the cascade to resolve conflicts: when multiple selectors match, the one with the highest specificity wins, or — if specificity is equal — the last one in source order wins. This makes styles inherently fragile. Reordering imports, adding a new media query, or composing components from different libraries can silently break styling.
191
+ Traditional CSS has two structural problems.
192
+
193
+ First, the **cascade** resolves conflicts by specificity and source order: when multiple selectors match, the one with the highest specificity wins, or — if specificity is equal — the last one in source order wins. That makes styles inherently fragile. Reordering imports, adding a media query, or composing components from different libraries can silently break styling.
137
194
 
138
- Tasty takes a fundamentally different approach: **every state mapping compiles into selectors that are guaranteed to never overlap.**
195
+ Second, **authoring selectors that capture real-world state logic is fundamentally hard.** A single state like "dark mode" may depend on a root attribute, an OS preference, or both — each branch needing its own selector, proper negation of competing branches, and correct `@media` nesting. The example below shows the CSS you'd write by hand for just *one* property with *one* state. Scale that across dozens of properties, then add breakpoints and container queries, and the selector logic quickly becomes unmanageable.
196
+
197
+ Tasty solves both problems at once: **every state mapping compiles into mutually exclusive selectors.**
139
198
 
140
199
  ```tsx
141
200
  const Text = tasty({
@@ -180,7 +239,9 @@ If `@dark` expands to `@root(schema=dark) | (!@root(schema) & @media(prefers-col
180
239
  }
181
240
  ```
182
241
 
183
- Every rule is guarded by the negation of all higher-priority rules. No two rules can ever match simultaneously. No specificity arithmetic. No source-order dependence. Components compose and extend without ever colliding.
242
+ Every rule is guarded by the negation of higher-priority rules. No two rules can match at the same time. No specificity arithmetic. No source-order dependence. Components compose and extend without collisions.
243
+
244
+ By absorbing selector complexity, Tasty makes advanced CSS patterns practical again — nested container queries, multi-condition `@supports` gates, and combined root-state/media branches. You stay in pure CSS instead of relying on JavaScript workarounds, so the browser can optimize layout, painting, and transitions natively. Tasty doesn't limit CSS; it unlocks its full potential by removing the complexity that held teams back.
184
245
 
185
246
  ## Capabilities
186
247
 
@@ -303,22 +364,15 @@ const ProfileCard = tasty({
303
364
 
304
365
  Use `/` to post-apply recipes after local styles when you need recipe states/styles to win the final merge order. Use `none` to skip base recipes: `recipe: 'none / disabled'`.
305
366
 
306
- ### Keyframes and `@property`
367
+ ### Auto-Inferred `@property`
307
368
 
308
- Modern CSS features are natively supported:
369
+ CSS custom properties do not animate smoothly by default because the browser does not know how to interpolate their values. The [`@property`](https://developer.mozilla.org/en-US/docs/Web/CSS/@property) at-rule fixes that by declaring a property's syntax, such as `<number>` or `<color>`.
309
370
 
310
- Color tokens are automatically registered as typed properties (`<color>`), so token-based transitions work without extra setup.
371
+ In Tasty, you usually do not need to declare `@property` manually. When a custom property is assigned a concrete value, Tasty infers the syntax and registers the matching `@property` for you:
311
372
 
312
373
  ```tsx
313
374
  const Pulse = tasty({
314
375
  styles: {
315
- '@properties': {
316
- '$pulse-scale': {
317
- syntax: '<number>',
318
- inherits: false,
319
- initialValue: 1,
320
- },
321
- },
322
376
  animation: 'pulse 2s infinite',
323
377
  transform: 'scale($pulse-scale)',
324
378
  '@keyframes': {
@@ -331,6 +385,20 @@ const Pulse = tasty({
331
385
  });
332
386
  ```
333
387
 
388
+ Here, `$pulse-scale: 1` is inferred as `<number>`, so Tasty injects `@property --pulse-scale` automatically before using it in the animation. Numeric types (`<number>`, `<length>`, `<percentage>`, `<angle>`, `<time>`) are inferred from values; `<color>` is inferred from the `#name` token convention.
389
+
390
+ If you prefer full manual control, disable auto-inference globally with `configure({ autoPropertyTypes: false })`.
391
+
392
+ ### Explicit `@properties`
393
+
394
+ Declare `@properties` yourself only when you need to override the defaults, for example to set `inherits: false` or provide a custom `initialValue`:
395
+
396
+ ```tsx
397
+ '@properties': {
398
+ '$pulse-scale': { syntax: '<number>', inherits: false, initialValue: 1 },
399
+ },
400
+ ```
401
+
334
402
  ### React Hooks
335
403
 
336
404
  For cases where you don't need a full component:
@@ -408,15 +476,61 @@ If you choose the runtime approach, performance is usually a non-issue in practi
408
476
  - A dedicated style injector minimizes DOM/style-tag overhead.
409
477
  - This approach is validated in enterprise-scale apps where runtime styling overhead is not noticeable in normal UI flows.
410
478
 
479
+ ### Server-Side Rendering (Experimental)
480
+
481
+ SSR with zero-cost client hydration. Existing `tasty()` components work unchanged — SSR is opt-in and requires no per-component modifications. Supports Next.js (App Router with streaming), Astro (middleware + islands), and any React-based framework via the core API. Requires React 19+.
482
+
483
+ **Next.js setup:**
484
+
485
+ ```tsx
486
+ // app/tasty-registry.tsx
487
+ 'use client';
488
+
489
+ import { TastyRegistry } from '@tenphi/tasty/ssr/next';
490
+
491
+ export default function TastyStyleRegistry({
492
+ children,
493
+ }: {
494
+ children: React.ReactNode;
495
+ }) {
496
+ return <TastyRegistry>{children}</TastyRegistry>;
497
+ }
498
+ ```
499
+
500
+ ```tsx
501
+ // app/layout.tsx
502
+ import TastyStyleRegistry from './tasty-registry';
503
+
504
+ export default function RootLayout({
505
+ children,
506
+ }: {
507
+ children: React.ReactNode;
508
+ }) {
509
+ return (
510
+ <html>
511
+ <body>
512
+ <TastyStyleRegistry>{children}</TastyStyleRegistry>
513
+ </body>
514
+ </html>
515
+ );
516
+ }
517
+ ```
518
+
519
+ See the [full SSR guide](docs/ssr.md) for Astro integration, streaming SSR, generic framework usage, and the complete API reference.
520
+
411
521
  ## Entry Points
412
522
 
413
523
  | Import | Description | Platform |
414
524
  |--------|-------------|----------|
415
- | `@tenphi/tasty` | Runtime style engine | Browser |
416
- | `@tenphi/tasty/static` | Zero-runtime static styles | Browser |
417
- | `@tenphi/tasty/babel-plugin` | Babel plugin for CSS extraction | Node |
525
+ | `@tenphi/tasty` | Runtime style engine (`tasty`, hooks, `configure`) | Browser |
526
+ | `@tenphi/tasty/static` | Zero-runtime static styles (`tastyStatic`) | Browser |
527
+ | `@tenphi/tasty/core` | Lower-level internals (config, parser, pipeline, injector, style handlers) for tooling and advanced use | Browser / Node |
528
+ | `@tenphi/tasty/babel-plugin` | Babel plugin for zero-runtime CSS extraction | Node |
418
529
  | `@tenphi/tasty/zero` | Programmatic extraction API | Node |
419
- | `@tenphi/tasty/next` | Next.js integration | Node |
530
+ | `@tenphi/tasty/next` | Next.js integration wrapper | Node |
531
+ | `@tenphi/tasty/ssr` | Core SSR API (collector, context, hydration) | Node |
532
+ | `@tenphi/tasty/ssr/next` | Next.js App Router SSR integration | Node |
533
+ | `@tenphi/tasty/ssr/astro` | Astro middleware + auto-hydration | Node / Browser |
420
534
 
421
535
  ## Ecosystem
422
536
 
@@ -455,14 +569,23 @@ const tokens = theme.tasty(); // Ready-to-use Tasty tokens
455
569
 
456
570
  Syntax highlighting for Tasty styles in TypeScript, TSX, JavaScript, and JSX. Highlights color tokens, custom units, state keys, presets, and style properties inside `tasty()`, `tastyStatic()`, and related APIs.
457
571
 
572
+ <p align="center">
573
+ <img src="assets/tasty-vscode-highlight.png" width="512" alt="Tasty VS Code syntax highlighting example">
574
+ </p>
575
+
458
576
  ### [Cube UI Kit](https://github.com/cube-js/cube-ui-kit)
459
577
 
460
578
  Open-source React UI kit built on Tasty + React Aria. 100+ production components proving Tasty works at design-system scale. A reference implementation and a ready-to-use component library.
461
579
 
462
580
  ## Documentation
463
581
 
464
- - **[Runtime API (tasty)](docs/tasty.md)** — Full runtime styling documentation: component creation, state mappings, sub-elements, variants, hooks, configuration, and style property reference
582
+ - **[Usage Guide](docs/usage.md)** — Runtime styling: component creation, state mappings, sub-elements, variants, and hooks
583
+ - **[Configuration](docs/configuration.md)** — Global configuration: tokens, recipes, custom units, style handlers, and TypeScript extensions
584
+ - **[Style Properties](docs/styles.md)** — Complete reference for all enhanced style properties: syntax, values, modifiers, and recommendations
465
585
  - **[Zero Runtime (tastyStatic)](docs/tasty-static.md)** — Build-time static styling: Babel plugin setup, Next.js integration, and static style patterns
586
+ - **[Server-Side Rendering](docs/ssr.md)** — SSR setup for Next.js, Astro, and generic frameworks: streaming support, cache hydration, and troubleshooting
587
+ - **[Style Injector](docs/injector.md)** — Internal CSS injection engine: `inject()`, `injectGlobal()`, `injectRawCSS()`, `keyframes()`, deduplication, reference counting, cleanup, SSR support, and Shadow DOM
588
+ - **[Debug Utilities](docs/debug.md)** — Runtime CSS inspection via `tastyDebug`: CSS extraction, element inspection, cache metrics, chunk breakdown, and performance monitoring
466
589
 
467
590
  ## License
468
591
 
@@ -130,8 +130,7 @@ const DISPLAY_CHUNK_STYLES = [
130
130
  "whiteSpace",
131
131
  "flow",
132
132
  "gap",
133
- "scrollbar",
134
- "styledScrollbar"
133
+ "scrollbar"
135
134
  ];
136
135
  /**
137
136
  * Layout chunk - flex/grid alignment and grid templates
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.js","names":[],"sources":["../../src/chunks/definitions.ts"],"sourcesContent":["/**\n * Style chunk definitions for CSS chunking optimization.\n *\n * Styles are grouped into chunks based on:\n * 1. Handler dependencies - styles that share a handler MUST be in the same chunk\n * 2. Logical grouping - related styles grouped for better cache reuse\n *\n * See STYLE_CHUNKING_SPEC.md for detailed rationale.\n *\n * ============================================================================\n * ⚠️ CRITICAL ARCHITECTURAL CONSTRAINT: NO CROSS-CHUNK HANDLER DEPENDENCIES\n * ============================================================================\n *\n * Style handlers declare their dependencies via `__lookupStyles` array.\n * This creates a dependency graph where handlers read multiple style props.\n *\n * **ALL styles in a handler's `__lookupStyles` MUST be in the SAME chunk.**\n *\n * Why this matters:\n * 1. Each chunk computes a cache key from ONLY its own style values\n * 2. If a handler reads a style from another chunk, that value isn't in the cache key\n * 3. Changing the cross-chunk style won't invalidate this chunk's cache\n * 4. Result: stale CSS output or incorrect cache hits\n *\n * Example of a violation:\n * ```\n * // flowStyle.__lookupStyles = ['display', 'flow']\n * // If 'display' is in DISPLAY chunk and 'flow' is in LAYOUT chunk:\n * // - User sets { display: 'grid', flow: 'column' }\n * // - LAYOUT chunk caches CSS with flow=column, display=grid\n * // - User changes to { display: 'flex', flow: 'column' }\n * // - LAYOUT chunk cache key unchanged (only has 'flow')\n * // - Returns stale CSS computed with display=grid!\n * ```\n *\n * Before adding/moving styles, verify:\n * 1. Find all handlers that use this style (grep for the style name in __lookupStyles)\n * 2. Ensure ALL styles from each handler's __lookupStyles are in the same chunk\n * ============================================================================\n */\n\nimport { isSelector } from '../pipeline';\n\n// ============================================================================\n// Chunk Style Lists\n// ============================================================================\n\n/**\n * Appearance chunk - visual styling with independent handlers\n */\nexport const APPEARANCE_CHUNK_STYLES = [\n 'fill', // fillStyle (independent)\n 'color', // colorStyle (independent)\n 'opacity', // independent\n 'border', // borderStyle (independent)\n 'radius', // radiusStyle (independent)\n 'outline', // outlineStyle: outline ↔ outlineOffset\n 'outlineOffset', // outlineStyle: outline ↔ outlineOffset\n 'shadow', // shadowStyle (independent)\n 'fade', // fadeStyle (independent)\n] as const;\n\n/**\n * Font chunk - typography styles\n *\n * Handler dependencies (all styles in each handler MUST stay in this chunk):\n * ⚠️ presetStyle: preset, fontSize, lineHeight, letterSpacing, textTransform,\n * fontWeight, fontStyle, font\n */\nexport const FONT_CHUNK_STYLES = [\n // All from presetStyle handler - MUST stay together\n 'preset',\n 'font',\n 'fontWeight',\n 'fontStyle',\n 'fontSize',\n 'lineHeight',\n 'letterSpacing',\n 'textTransform',\n // Independent text styles grouped for cohesion\n 'fontFamily', // independent alias (logical grouping with font styles)\n 'textAlign',\n 'textDecoration',\n 'wordBreak',\n 'wordWrap',\n 'boldFontWeight',\n] as const;\n\n/**\n * Dimension chunk - sizing and spacing\n *\n * Handler dependencies (all styles in each handler MUST stay in this chunk):\n * ⚠️ paddingStyle: padding, paddingTop/Right/Bottom/Left, paddingBlock/Inline\n * ⚠️ marginStyle: margin, marginTop/Right/Bottom/Left, marginBlock/Inline\n * ⚠️ widthStyle: width, minWidth, maxWidth\n * ⚠️ heightStyle: height, minHeight, maxHeight\n */\nexport const DIMENSION_CHUNK_STYLES = [\n // All from paddingStyle handler - MUST stay together\n 'padding',\n 'paddingTop',\n 'paddingRight',\n 'paddingBottom',\n 'paddingLeft',\n 'paddingBlock',\n 'paddingInline',\n // All from marginStyle handler - MUST stay together\n 'margin',\n 'marginTop',\n 'marginRight',\n 'marginBottom',\n 'marginLeft',\n 'marginBlock',\n 'marginInline',\n // widthStyle handler - MUST stay together\n 'width',\n 'minWidth',\n 'maxWidth',\n // heightStyle handler - MUST stay together\n 'height',\n 'minHeight',\n 'maxHeight',\n 'flexBasis',\n 'flexGrow',\n 'flexShrink',\n 'flex',\n] as const;\n\n/**\n * Display chunk - display mode, layout flow, text overflow, and scrollbar\n *\n * Handler dependencies (all styles in each handler MUST stay in this chunk):\n * ⚠️ displayStyle: display, hide, textOverflow, overflow, whiteSpace\n * ⚠️ flowStyle: display, flow\n * ⚠️ gapStyle: display, flow, gap\n * ⚠️ scrollbarStyle: scrollbar, overflow\n */\nexport const DISPLAY_CHUNK_STYLES = [\n // displayStyle handler\n 'display',\n 'hide',\n 'textOverflow',\n 'overflow', // also used by scrollbarStyle\n 'whiteSpace',\n // flowStyle handler (requires display)\n 'flow',\n // gapStyle handler (requires display, flow)\n 'gap',\n // scrollbarStyle handler (requires overflow)\n 'scrollbar',\n 'styledScrollbar', // styledScrollbarStyle (deprecated)\n] as const;\n\n/**\n * Layout chunk - flex/grid alignment and grid templates\n *\n * Note: flow and gap are in DISPLAY chunk due to handler dependencies\n * (flowStyle and gapStyle both require 'display' prop).\n */\nexport const LAYOUT_CHUNK_STYLES = [\n // Alignment styles (all independent handlers)\n 'placeItems',\n 'placeContent',\n 'alignItems',\n 'alignContent',\n 'justifyItems',\n 'justifyContent',\n 'align', // alignStyle (independent)\n 'justify', // justifyStyle (independent)\n 'place', // placeStyle (independent)\n 'columnGap',\n 'rowGap',\n // Grid template styles\n 'gridColumns',\n 'gridRows',\n 'gridTemplate',\n 'gridAreas',\n 'gridAutoFlow',\n 'gridAutoColumns',\n 'gridAutoRows',\n] as const;\n\n/**\n * Position chunk - element positioning\n *\n * Handler dependencies (all styles in each handler MUST stay in this chunk):\n * ⚠️ insetStyle: inset, insetBlock, insetInline, top, right, bottom, left\n */\nexport const POSITION_CHUNK_STYLES = [\n 'position',\n // All from insetStyle handler - MUST stay together\n 'inset',\n 'insetBlock',\n 'insetInline',\n 'top',\n 'right',\n 'bottom',\n 'left',\n 'zIndex',\n 'gridArea',\n 'gridColumn',\n 'gridRow',\n 'order',\n 'placeSelf',\n 'alignSelf',\n 'justifySelf',\n 'transform',\n 'transition',\n 'animation',\n] as const;\n\n// ============================================================================\n// Chunk Names\n// ============================================================================\n\nexport const CHUNK_NAMES = {\n /** Special chunk for styles that cannot be split (e.g., @starting-style) */\n COMBINED: 'combined',\n SUBCOMPONENTS: 'subcomponents',\n APPEARANCE: 'appearance',\n FONT: 'font',\n DIMENSION: 'dimension',\n DISPLAY: 'display',\n LAYOUT: 'layout',\n POSITION: 'position',\n MISC: 'misc',\n} as const;\n\nexport type ChunkName = (typeof CHUNK_NAMES)[keyof typeof CHUNK_NAMES];\n\n// ============================================================================\n// Style-to-Chunk Lookup Map (O(1) categorization)\n// ============================================================================\n\n/**\n * Pre-computed map for O(1) style-to-chunk lookup.\n * Built once at module load time.\n */\nexport const STYLE_TO_CHUNK = new Map<string, ChunkName>();\n\n// Populate the lookup map\nfunction populateStyleToChunkMap() {\n for (const style of APPEARANCE_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.APPEARANCE);\n }\n for (const style of FONT_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.FONT);\n }\n for (const style of DIMENSION_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.DIMENSION);\n }\n for (const style of DISPLAY_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.DISPLAY);\n }\n for (const style of LAYOUT_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.LAYOUT);\n }\n for (const style of POSITION_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.POSITION);\n }\n}\n\n// Initialize at module load\npopulateStyleToChunkMap();\n\n// ============================================================================\n// Chunk Priority Order\n// ============================================================================\n\n/**\n * Chunk processing order. This ensures deterministic className allocation\n * regardless of style key order in the input.\n */\nconst CHUNK_ORDER: readonly string[] = [\n CHUNK_NAMES.APPEARANCE,\n CHUNK_NAMES.FONT,\n CHUNK_NAMES.DIMENSION,\n CHUNK_NAMES.DISPLAY,\n CHUNK_NAMES.LAYOUT,\n CHUNK_NAMES.POSITION,\n CHUNK_NAMES.MISC,\n CHUNK_NAMES.SUBCOMPONENTS,\n] as const;\n\n/**\n * Map from chunk name to its priority index for sorting.\n */\nconst _CHUNK_PRIORITY = new Map<string, number>(\n CHUNK_ORDER.map((name, index) => [name, index]),\n);\n\n// ============================================================================\n// Chunk Info Interface\n// ============================================================================\n\nexport interface ChunkInfo {\n /** Name of the chunk */\n name: ChunkName | string;\n /** Style keys belonging to this chunk */\n styleKeys: string[];\n}\n\n// ============================================================================\n// Style Categorization\n// ============================================================================\n\n/**\n * Categorize style keys into chunks.\n *\n * Returns chunks in a deterministic order (by CHUNK_ORDER) regardless\n * of the order of keys in the input styles object.\n *\n * @param styles - The styles object to categorize\n * @returns Map of chunk name to array of style keys in that chunk (in priority order)\n */\nexport function categorizeStyleKeys(\n styles: Record<string, unknown>,\n): Map<string, string[]> {\n // First pass: collect keys into chunks (unordered)\n const chunkData: Record<string, string[]> = {};\n const keys = Object.keys(styles);\n\n for (const key of keys) {\n // Skip the $ helper key (used for selector combinators)\n // Skip @keyframes and @properties (processed separately in useStyles)\n // Skip recipe (resolved before pipeline by resolveRecipes)\n if (\n key === '$' ||\n key === '@keyframes' ||\n key === '@properties' ||\n key === 'recipe'\n ) {\n continue;\n }\n\n if (isSelector(key)) {\n // All selectors go into the subcomponents chunk\n if (!chunkData[CHUNK_NAMES.SUBCOMPONENTS]) {\n chunkData[CHUNK_NAMES.SUBCOMPONENTS] = [];\n }\n chunkData[CHUNK_NAMES.SUBCOMPONENTS].push(key);\n } else {\n // Look up the chunk for this style, default to misc\n const chunkName = STYLE_TO_CHUNK.get(key) ?? CHUNK_NAMES.MISC;\n if (!chunkData[chunkName]) {\n chunkData[chunkName] = [];\n }\n chunkData[chunkName].push(key);\n }\n }\n\n // Second pass: build ordered Map based on CHUNK_ORDER\n const orderedChunks = new Map<string, string[]>();\n\n // Add chunks in priority order\n for (const chunkName of CHUNK_ORDER) {\n if (chunkData[chunkName] && chunkData[chunkName].length > 0) {\n // Sort keys within chunk for consistent cache key generation\n orderedChunks.set(chunkName, chunkData[chunkName].sort());\n }\n }\n\n // Handle any unknown chunks (shouldn't happen, but be defensive)\n for (const chunkName of Object.keys(chunkData)) {\n if (!orderedChunks.has(chunkName)) {\n orderedChunks.set(chunkName, chunkData[chunkName].sort());\n }\n }\n\n return orderedChunks;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,MAAa,0BAA0B;CACrC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;AASD,MAAa,oBAAoB;CAE/B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;;;AAWD,MAAa,yBAAyB;CAEpC;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;;;AAWD,MAAa,uBAAuB;CAElC;CACA;CACA;CACA;CACA;CAEA;CAEA;CAEA;CACA;CACD;;;;;;;AAQD,MAAa,sBAAsB;CAEjC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;AAQD,MAAa,wBAAwB;CACnC;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAMD,MAAa,cAAc;CAEzB,UAAU;CACV,eAAe;CACf,YAAY;CACZ,MAAM;CACN,WAAW;CACX,SAAS;CACT,QAAQ;CACR,UAAU;CACV,MAAM;CACP;;;;;AAYD,MAAa,iCAAiB,IAAI,KAAwB;AAG1D,SAAS,0BAA0B;AACjC,MAAK,MAAM,SAAS,wBAClB,gBAAe,IAAI,OAAO,YAAY,WAAW;AAEnD,MAAK,MAAM,SAAS,kBAClB,gBAAe,IAAI,OAAO,YAAY,KAAK;AAE7C,MAAK,MAAM,SAAS,uBAClB,gBAAe,IAAI,OAAO,YAAY,UAAU;AAElD,MAAK,MAAM,SAAS,qBAClB,gBAAe,IAAI,OAAO,YAAY,QAAQ;AAEhD,MAAK,MAAM,SAAS,oBAClB,gBAAe,IAAI,OAAO,YAAY,OAAO;AAE/C,MAAK,MAAM,SAAS,sBAClB,gBAAe,IAAI,OAAO,YAAY,SAAS;;AAKnD,yBAAyB;;;;;AAUzB,MAAM,cAAiC;CACrC,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,YAAY;CACb;AAKuB,IAAI,IAC1B,YAAY,KAAK,MAAM,UAAU,CAAC,MAAM,MAAM,CAAC,CAChD;;;;;;;;;;AA0BD,SAAgB,oBACd,QACuB;CAEvB,MAAM,YAAsC,EAAE;CAC9C,MAAM,OAAO,OAAO,KAAK,OAAO;AAEhC,MAAK,MAAM,OAAO,MAAM;AAItB,MACE,QAAQ,OACR,QAAQ,gBACR,QAAQ,iBACR,QAAQ,SAER;AAGF,MAAI,WAAW,IAAI,EAAE;AAEnB,OAAI,CAAC,UAAU,YAAY,eACzB,WAAU,YAAY,iBAAiB,EAAE;AAE3C,aAAU,YAAY,eAAe,KAAK,IAAI;SACzC;GAEL,MAAM,YAAY,eAAe,IAAI,IAAI,IAAI,YAAY;AACzD,OAAI,CAAC,UAAU,WACb,WAAU,aAAa,EAAE;AAE3B,aAAU,WAAW,KAAK,IAAI;;;CAKlC,MAAM,gCAAgB,IAAI,KAAuB;AAGjD,MAAK,MAAM,aAAa,YACtB,KAAI,UAAU,cAAc,UAAU,WAAW,SAAS,EAExD,eAAc,IAAI,WAAW,UAAU,WAAW,MAAM,CAAC;AAK7D,MAAK,MAAM,aAAa,OAAO,KAAK,UAAU,CAC5C,KAAI,CAAC,cAAc,IAAI,UAAU,CAC/B,eAAc,IAAI,WAAW,UAAU,WAAW,MAAM,CAAC;AAI7D,QAAO"}
1
+ {"version":3,"file":"definitions.js","names":[],"sources":["../../src/chunks/definitions.ts"],"sourcesContent":["/**\n * Style chunk definitions for CSS chunking optimization.\n *\n * Styles are grouped into chunks based on:\n * 1. Handler dependencies - styles that share a handler MUST be in the same chunk\n * 2. Logical grouping - related styles grouped for better cache reuse\n *\n * See STYLE_CHUNKING_SPEC.md for detailed rationale.\n *\n * ============================================================================\n * ⚠️ CRITICAL ARCHITECTURAL CONSTRAINT: NO CROSS-CHUNK HANDLER DEPENDENCIES\n * ============================================================================\n *\n * Style handlers declare their dependencies via `__lookupStyles` array.\n * This creates a dependency graph where handlers read multiple style props.\n *\n * **ALL styles in a handler's `__lookupStyles` MUST be in the SAME chunk.**\n *\n * Why this matters:\n * 1. Each chunk computes a cache key from ONLY its own style values\n * 2. If a handler reads a style from another chunk, that value isn't in the cache key\n * 3. Changing the cross-chunk style won't invalidate this chunk's cache\n * 4. Result: stale CSS output or incorrect cache hits\n *\n * Example of a violation:\n * ```\n * // flowStyle.__lookupStyles = ['display', 'flow']\n * // If 'display' is in DISPLAY chunk and 'flow' is in LAYOUT chunk:\n * // - User sets { display: 'grid', flow: 'column' }\n * // - LAYOUT chunk caches CSS with flow=column, display=grid\n * // - User changes to { display: 'flex', flow: 'column' }\n * // - LAYOUT chunk cache key unchanged (only has 'flow')\n * // - Returns stale CSS computed with display=grid!\n * ```\n *\n * Before adding/moving styles, verify:\n * 1. Find all handlers that use this style (grep for the style name in __lookupStyles)\n * 2. Ensure ALL styles from each handler's __lookupStyles are in the same chunk\n * ============================================================================\n */\n\nimport { isSelector } from '../pipeline';\n\n// ============================================================================\n// Chunk Style Lists\n// ============================================================================\n\n/**\n * Appearance chunk - visual styling with independent handlers\n */\nexport const APPEARANCE_CHUNK_STYLES = [\n 'fill', // fillStyle (independent)\n 'color', // colorStyle (independent)\n 'opacity', // independent\n 'border', // borderStyle (independent)\n 'radius', // radiusStyle (independent)\n 'outline', // outlineStyle: outline ↔ outlineOffset\n 'outlineOffset', // outlineStyle: outline ↔ outlineOffset\n 'shadow', // shadowStyle (independent)\n 'fade', // fadeStyle (independent)\n] as const;\n\n/**\n * Font chunk - typography styles\n *\n * Handler dependencies (all styles in each handler MUST stay in this chunk):\n * ⚠️ presetStyle: preset, fontSize, lineHeight, letterSpacing, textTransform,\n * fontWeight, fontStyle, font\n */\nexport const FONT_CHUNK_STYLES = [\n // All from presetStyle handler - MUST stay together\n 'preset',\n 'font',\n 'fontWeight',\n 'fontStyle',\n 'fontSize',\n 'lineHeight',\n 'letterSpacing',\n 'textTransform',\n // Independent text styles grouped for cohesion\n 'fontFamily', // independent alias (logical grouping with font styles)\n 'textAlign',\n 'textDecoration',\n 'wordBreak',\n 'wordWrap',\n 'boldFontWeight',\n] as const;\n\n/**\n * Dimension chunk - sizing and spacing\n *\n * Handler dependencies (all styles in each handler MUST stay in this chunk):\n * ⚠️ paddingStyle: padding, paddingTop/Right/Bottom/Left, paddingBlock/Inline\n * ⚠️ marginStyle: margin, marginTop/Right/Bottom/Left, marginBlock/Inline\n * ⚠️ widthStyle: width, minWidth, maxWidth\n * ⚠️ heightStyle: height, minHeight, maxHeight\n */\nexport const DIMENSION_CHUNK_STYLES = [\n // All from paddingStyle handler - MUST stay together\n 'padding',\n 'paddingTop',\n 'paddingRight',\n 'paddingBottom',\n 'paddingLeft',\n 'paddingBlock',\n 'paddingInline',\n // All from marginStyle handler - MUST stay together\n 'margin',\n 'marginTop',\n 'marginRight',\n 'marginBottom',\n 'marginLeft',\n 'marginBlock',\n 'marginInline',\n // widthStyle handler - MUST stay together\n 'width',\n 'minWidth',\n 'maxWidth',\n // heightStyle handler - MUST stay together\n 'height',\n 'minHeight',\n 'maxHeight',\n 'flexBasis',\n 'flexGrow',\n 'flexShrink',\n 'flex',\n] as const;\n\n/**\n * Display chunk - display mode, layout flow, text overflow, and scrollbar\n *\n * Handler dependencies (all styles in each handler MUST stay in this chunk):\n * ⚠️ displayStyle: display, hide, textOverflow, overflow, whiteSpace\n * ⚠️ flowStyle: display, flow\n * ⚠️ gapStyle: display, flow, gap\n * ⚠️ scrollbarStyle: scrollbar, overflow\n */\nexport const DISPLAY_CHUNK_STYLES = [\n // displayStyle handler\n 'display',\n 'hide',\n 'textOverflow',\n 'overflow', // also used by scrollbarStyle\n 'whiteSpace',\n // flowStyle handler (requires display)\n 'flow',\n // gapStyle handler (requires display, flow)\n 'gap',\n // scrollbarStyle handler (requires overflow)\n 'scrollbar',\n] as const;\n\n/**\n * Layout chunk - flex/grid alignment and grid templates\n *\n * Note: flow and gap are in DISPLAY chunk due to handler dependencies\n * (flowStyle and gapStyle both require 'display' prop).\n */\nexport const LAYOUT_CHUNK_STYLES = [\n // Alignment styles (all independent handlers)\n 'placeItems',\n 'placeContent',\n 'alignItems',\n 'alignContent',\n 'justifyItems',\n 'justifyContent',\n 'align', // alignStyle (independent)\n 'justify', // justifyStyle (independent)\n 'place', // placeStyle (independent)\n 'columnGap',\n 'rowGap',\n // Grid template styles\n 'gridColumns',\n 'gridRows',\n 'gridTemplate',\n 'gridAreas',\n 'gridAutoFlow',\n 'gridAutoColumns',\n 'gridAutoRows',\n] as const;\n\n/**\n * Position chunk - element positioning\n *\n * Handler dependencies (all styles in each handler MUST stay in this chunk):\n * ⚠️ insetStyle: inset, insetBlock, insetInline, top, right, bottom, left\n */\nexport const POSITION_CHUNK_STYLES = [\n 'position',\n // All from insetStyle handler - MUST stay together\n 'inset',\n 'insetBlock',\n 'insetInline',\n 'top',\n 'right',\n 'bottom',\n 'left',\n 'zIndex',\n 'gridArea',\n 'gridColumn',\n 'gridRow',\n 'order',\n 'placeSelf',\n 'alignSelf',\n 'justifySelf',\n 'transform',\n 'transition',\n 'animation',\n] as const;\n\n// ============================================================================\n// Chunk Names\n// ============================================================================\n\nexport const CHUNK_NAMES = {\n /** Special chunk for styles that cannot be split (e.g., @starting-style) */\n COMBINED: 'combined',\n SUBCOMPONENTS: 'subcomponents',\n APPEARANCE: 'appearance',\n FONT: 'font',\n DIMENSION: 'dimension',\n DISPLAY: 'display',\n LAYOUT: 'layout',\n POSITION: 'position',\n MISC: 'misc',\n} as const;\n\nexport type ChunkName = (typeof CHUNK_NAMES)[keyof typeof CHUNK_NAMES];\n\n// ============================================================================\n// Style-to-Chunk Lookup Map (O(1) categorization)\n// ============================================================================\n\n/**\n * Pre-computed map for O(1) style-to-chunk lookup.\n * Built once at module load time.\n */\nexport const STYLE_TO_CHUNK = new Map<string, ChunkName>();\n\n// Populate the lookup map\nfunction populateStyleToChunkMap() {\n for (const style of APPEARANCE_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.APPEARANCE);\n }\n for (const style of FONT_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.FONT);\n }\n for (const style of DIMENSION_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.DIMENSION);\n }\n for (const style of DISPLAY_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.DISPLAY);\n }\n for (const style of LAYOUT_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.LAYOUT);\n }\n for (const style of POSITION_CHUNK_STYLES) {\n STYLE_TO_CHUNK.set(style, CHUNK_NAMES.POSITION);\n }\n}\n\n// Initialize at module load\npopulateStyleToChunkMap();\n\n// ============================================================================\n// Chunk Priority Order\n// ============================================================================\n\n/**\n * Chunk processing order. This ensures deterministic className allocation\n * regardless of style key order in the input.\n */\nconst CHUNK_ORDER: readonly string[] = [\n CHUNK_NAMES.APPEARANCE,\n CHUNK_NAMES.FONT,\n CHUNK_NAMES.DIMENSION,\n CHUNK_NAMES.DISPLAY,\n CHUNK_NAMES.LAYOUT,\n CHUNK_NAMES.POSITION,\n CHUNK_NAMES.MISC,\n CHUNK_NAMES.SUBCOMPONENTS,\n] as const;\n\n/**\n * Map from chunk name to its priority index for sorting.\n */\nconst _CHUNK_PRIORITY = new Map<string, number>(\n CHUNK_ORDER.map((name, index) => [name, index]),\n);\n\n// ============================================================================\n// Chunk Info Interface\n// ============================================================================\n\nexport interface ChunkInfo {\n /** Name of the chunk */\n name: ChunkName | string;\n /** Style keys belonging to this chunk */\n styleKeys: string[];\n}\n\n// ============================================================================\n// Style Categorization\n// ============================================================================\n\n/**\n * Categorize style keys into chunks.\n *\n * Returns chunks in a deterministic order (by CHUNK_ORDER) regardless\n * of the order of keys in the input styles object.\n *\n * @param styles - The styles object to categorize\n * @returns Map of chunk name to array of style keys in that chunk (in priority order)\n */\nexport function categorizeStyleKeys(\n styles: Record<string, unknown>,\n): Map<string, string[]> {\n // First pass: collect keys into chunks (unordered)\n const chunkData: Record<string, string[]> = {};\n const keys = Object.keys(styles);\n\n for (const key of keys) {\n // Skip the $ helper key (used for selector combinators)\n // Skip @keyframes and @properties (processed separately in useStyles)\n // Skip recipe (resolved before pipeline by resolveRecipes)\n if (\n key === '$' ||\n key === '@keyframes' ||\n key === '@properties' ||\n key === 'recipe'\n ) {\n continue;\n }\n\n if (isSelector(key)) {\n // All selectors go into the subcomponents chunk\n if (!chunkData[CHUNK_NAMES.SUBCOMPONENTS]) {\n chunkData[CHUNK_NAMES.SUBCOMPONENTS] = [];\n }\n chunkData[CHUNK_NAMES.SUBCOMPONENTS].push(key);\n } else {\n // Look up the chunk for this style, default to misc\n const chunkName = STYLE_TO_CHUNK.get(key) ?? CHUNK_NAMES.MISC;\n if (!chunkData[chunkName]) {\n chunkData[chunkName] = [];\n }\n chunkData[chunkName].push(key);\n }\n }\n\n // Second pass: build ordered Map based on CHUNK_ORDER\n const orderedChunks = new Map<string, string[]>();\n\n // Add chunks in priority order\n for (const chunkName of CHUNK_ORDER) {\n if (chunkData[chunkName] && chunkData[chunkName].length > 0) {\n // Sort keys within chunk for consistent cache key generation\n orderedChunks.set(chunkName, chunkData[chunkName].sort());\n }\n }\n\n // Handle any unknown chunks (shouldn't happen, but be defensive)\n for (const chunkName of Object.keys(chunkData)) {\n if (!orderedChunks.has(chunkName)) {\n orderedChunks.set(chunkName, chunkData[chunkName].sort());\n }\n }\n\n return orderedChunks;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,MAAa,0BAA0B;CACrC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;AASD,MAAa,oBAAoB;CAE/B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;;;AAWD,MAAa,yBAAyB;CAEpC;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;;;;AAWD,MAAa,uBAAuB;CAElC;CACA;CACA;CACA;CACA;CAEA;CAEA;CAEA;CACD;;;;;;;AAQD,MAAa,sBAAsB;CAEjC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;AAQD,MAAa,wBAAwB;CACnC;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAMD,MAAa,cAAc;CAEzB,UAAU;CACV,eAAe;CACf,YAAY;CACZ,MAAM;CACN,WAAW;CACX,SAAS;CACT,QAAQ;CACR,UAAU;CACV,MAAM;CACP;;;;;AAYD,MAAa,iCAAiB,IAAI,KAAwB;AAG1D,SAAS,0BAA0B;AACjC,MAAK,MAAM,SAAS,wBAClB,gBAAe,IAAI,OAAO,YAAY,WAAW;AAEnD,MAAK,MAAM,SAAS,kBAClB,gBAAe,IAAI,OAAO,YAAY,KAAK;AAE7C,MAAK,MAAM,SAAS,uBAClB,gBAAe,IAAI,OAAO,YAAY,UAAU;AAElD,MAAK,MAAM,SAAS,qBAClB,gBAAe,IAAI,OAAO,YAAY,QAAQ;AAEhD,MAAK,MAAM,SAAS,oBAClB,gBAAe,IAAI,OAAO,YAAY,OAAO;AAE/C,MAAK,MAAM,SAAS,sBAClB,gBAAe,IAAI,OAAO,YAAY,SAAS;;AAKnD,yBAAyB;;;;;AAUzB,MAAM,cAAiC;CACrC,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,YAAY;CACZ,YAAY;CACb;AAKuB,IAAI,IAC1B,YAAY,KAAK,MAAM,UAAU,CAAC,MAAM,MAAM,CAAC,CAChD;;;;;;;;;;AA0BD,SAAgB,oBACd,QACuB;CAEvB,MAAM,YAAsC,EAAE;CAC9C,MAAM,OAAO,OAAO,KAAK,OAAO;AAEhC,MAAK,MAAM,OAAO,MAAM;AAItB,MACE,QAAQ,OACR,QAAQ,gBACR,QAAQ,iBACR,QAAQ,SAER;AAGF,MAAI,WAAW,IAAI,EAAE;AAEnB,OAAI,CAAC,UAAU,YAAY,eACzB,WAAU,YAAY,iBAAiB,EAAE;AAE3C,aAAU,YAAY,eAAe,KAAK,IAAI;SACzC;GAEL,MAAM,YAAY,eAAe,IAAI,IAAI,IAAI,YAAY;AACzD,OAAI,CAAC,UAAU,WACb,WAAU,aAAa,EAAE;AAE3B,aAAU,WAAW,KAAK,IAAI;;;CAKlC,MAAM,gCAAgB,IAAI,KAAuB;AAGjD,MAAK,MAAM,aAAa,YACtB,KAAI,UAAU,cAAc,UAAU,WAAW,SAAS,EAExD,eAAc,IAAI,WAAW,UAAU,WAAW,MAAM,CAAC;AAK7D,MAAK,MAAM,aAAa,OAAO,KAAK,UAAU,CAC5C,KAAI,CAAC,cAAc,IAAI,UAAU,CAC/B,eAAc,IAAI,WAAW,UAAU,WAAW,MAAM,CAAC;AAI7D,QAAO"}
package/dist/config.d.ts CHANGED
@@ -58,6 +58,14 @@ interface TastyConfig {
58
58
  * @example { myFunc: (groups) => groups.map(g => g.output).join(' ') }
59
59
  */
60
60
  funcs?: Record<string, (groups: StyleDetails[]) => string>;
61
+ /**
62
+ * Automatically infer and register CSS @property declarations
63
+ * from custom property values found in styles, keyframes, and global config.
64
+ * Covers all types: \<color\>, \<number\>, \<length\>, \<angle\>, \<percentage\>, \<time\>.
65
+ * When false, only explicitly declared @properties are registered.
66
+ * @default true
67
+ */
68
+ autoPropertyTypes?: boolean;
61
69
  /**
62
70
  * Plugins that extend tasty with custom functions, units, or states.
63
71
  * Plugins are processed in order, with later plugins overriding earlier ones.
package/dist/config.js CHANGED
@@ -190,6 +190,20 @@ function setGlobalKeyframes(keyframes) {
190
190
  globalKeyframes = keyframes;
191
191
  }
192
192
  /**
193
+ * Check if any global properties are configured.
194
+ * Fast path: returns false if no properties were ever set.
195
+ */
196
+ function hasGlobalProperties() {
197
+ return globalProperties !== null && Object.keys(globalProperties).length > 0;
198
+ }
199
+ /**
200
+ * Get global properties configuration.
201
+ * Returns null if no properties configured (fast path for zero-overhead).
202
+ */
203
+ function getGlobalProperties() {
204
+ return globalProperties;
205
+ }
206
+ /**
193
207
  * Set global properties (called from configure).
194
208
  * Internal use only.
195
209
  */
@@ -399,5 +413,5 @@ function resetConfig() {
399
413
  }
400
414
 
401
415
  //#endregion
402
- export { configure, getConfig, getGlobalInjector, getGlobalKeyframes, getGlobalRecipes, hasGlobalKeyframes, hasGlobalRecipes, hasStylesGenerated, isConfigLocked, isTestEnvironment, markStylesGenerated, resetConfig };
416
+ export { INTERNAL_PROPERTIES, INTERNAL_TOKENS, configure, getConfig, getGlobalInjector, getGlobalKeyframes, getGlobalProperties, getGlobalRecipes, hasGlobalKeyframes, hasGlobalProperties, hasGlobalRecipes, hasStylesGenerated, isConfigLocked, isTestEnvironment, markStylesGenerated, resetConfig };
403
417
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","names":[],"sources":["../src/config.ts"],"sourcesContent":["/**\n * Tasty Configuration Module\n *\n * Centralizes all tasty configuration, including:\n * - Style injector settings (nonce, cleanup thresholds, etc.)\n * - Global predefined states for advanced state mapping\n * - stylesGenerated flag that locks configuration after first style generation\n *\n * Configuration must be done BEFORE any styles are generated.\n * After the first `inject()` call, configuration is locked and attempts to\n * reconfigure will emit a warning and be ignored.\n */\n\nimport { StyleInjector } from './injector/injector';\nimport { clearPipelineCache, isSelector } from './pipeline';\nimport { setGlobalPredefinedStates } from './states';\nimport {\n normalizeHandlerDefinition,\n registerHandler,\n resetHandlers,\n} from './styles/predefined';\nimport { isDevEnv } from './utils/is-dev-env';\nimport {\n CUSTOM_UNITS,\n getGlobalFuncs,\n getGlobalParser,\n normalizeColorTokenValue,\n resetGlobalPredefinedTokens,\n setGlobalPredefinedTokens,\n} from './utils/styles';\n\nimport type { KeyframesSteps, PropertyDefinition } from './injector/types';\nimport type { StyleDetails, UnitHandler } from './parser/types';\nimport type { TastyPlugin } from './plugins/types';\nimport type { RecipeStyles } from './styles/types';\nimport type { StyleHandlerDefinition } from './utils/styles';\n\n/**\n * Configuration options for the Tasty style system\n */\nexport interface TastyConfig {\n /** CSP nonce for style elements */\n nonce?: string;\n /** Maximum rules per stylesheet (default: 8192) */\n maxRulesPerSheet?: number;\n /** Threshold for bulk cleanup of unused styles (default: 500) */\n unusedStylesThreshold?: number;\n /** Delay before bulk cleanup in ms, ignored if idleCleanup is true (default: 5000) */\n bulkCleanupDelay?: number;\n /** Use requestIdleCallback for cleanup when available (default: true) */\n idleCleanup?: boolean;\n /** Force text injection mode, auto-detected in test environments (default: auto) */\n forceTextInjection?: boolean;\n /** Enable development mode features: performance metrics and debug info (default: auto) */\n devMode?: boolean;\n /**\n * Ratio of unused styles to delete per bulk cleanup run (0..1).\n * Defaults to 0.5 (oldest half) to reduce risk of removing styles\n * that may be restored shortly after being marked unused.\n */\n bulkCleanupBatchRatio?: number;\n /**\n * Minimum age (in ms) a style must remain unused before eligible for deletion.\n * Helps avoid races during rapid mount/unmount cycles. Default: 10000ms.\n */\n unusedStylesMinAgeMs?: number;\n /**\n * Global predefined states for advanced state mapping.\n * These are state aliases that can be used in any component.\n * Example: { '@mobile': '@media(w < 920px)', '@dark': '@root(theme=dark)' }\n */\n states?: Record<string, string>;\n /**\n * Parser LRU cache size (default: 1000).\n * Larger values improve performance for apps with many unique style values.\n */\n parserCacheSize?: number;\n /**\n * Custom units for the style parser (merged with built-in units).\n * Units transform numeric values like `2x` → `calc(2 * var(--gap))`.\n * @example { em: 'em', vw: 'vw', custom: (n) => `${n * 10}px` }\n */\n units?: Record<string, string | UnitHandler>;\n /**\n * Custom functions for the style parser (merged with existing).\n * Functions process parsed style groups and return CSS values.\n * @example { myFunc: (groups) => groups.map(g => g.output).join(' ') }\n */\n funcs?: Record<string, (groups: StyleDetails[]) => string>;\n /**\n * Plugins that extend tasty with custom functions, units, or states.\n * Plugins are processed in order, with later plugins overriding earlier ones.\n * @example\n * ```ts\n * import { okhslPlugin } from '@tenphi/tasty';\n *\n * configure({\n * plugins: [okhslPlugin()],\n * });\n * ```\n */\n plugins?: TastyPlugin[];\n /**\n * Global keyframes definitions that can be referenced by animation names in styles.\n * Keys are animation names, values are keyframes step definitions.\n * Keyframes are only injected when actually used in styles.\n * @example\n * ```ts\n * configure({\n * keyframes: {\n * fadeIn: { from: { opacity: 0 }, to: { opacity: 1 } },\n * pulse: { '0%, 100%': { transform: 'scale(1)' }, '50%': { transform: 'scale(1.05)' } },\n * },\n * });\n * ```\n */\n keyframes?: Record<string, KeyframesSteps>;\n /**\n * Global CSS @property definitions for custom properties.\n * Keys use tasty token syntax ($name for properties, #name for colors).\n * Properties are only injected when the component using them is rendered.\n *\n * For color tokens (#name), `syntax: '<color>'` is auto-set and\n * `initialValue` defaults to `'transparent'` if not specified.\n *\n * @example\n * ```ts\n * configure({\n * properties: {\n * '$rotation': { syntax: '<angle>', initialValue: '0deg' },\n * '$scale': { syntax: '<number>', inherits: false, initialValue: 1 },\n * '#accent': { initialValue: 'purple' }, // syntax: '<color>' auto-set\n * },\n * });\n *\n * // Now use in styles - properties are registered when component renders:\n * const Spinner = tasty({\n * styles: {\n * transform: 'rotate($rotation)',\n * transition: '$$rotation 0.3s', // outputs: --rotation 0.3s\n * },\n * });\n * ```\n */\n properties?: Record<string, PropertyDefinition>;\n /**\n * Custom style handlers that transform style properties into CSS declarations.\n * Handlers replace built-in handlers for the same style name.\n * @example\n * ```ts\n * import { styleHandlers } from '@tenphi/tasty';\n *\n * configure({\n * handlers: {\n * // Override fill with custom behavior\n * fill: ({ fill }) => {\n * if (fill?.startsWith('gradient:')) {\n * return { background: fill.slice(9) };\n * }\n * return styleHandlers.fill({ fill });\n * },\n * // Add new custom style\n * elevation: ({ elevation }) => {\n * const level = parseInt(elevation) || 1;\n * return {\n * 'box-shadow': `0 ${level * 2}px ${level * 4}px rgba(0,0,0,0.1)`,\n * 'z-index': String(level * 100),\n * };\n * },\n * },\n * });\n * ```\n */\n handlers?: Record<string, StyleHandlerDefinition>;\n /**\n * Predefined tokens that are replaced during style parsing.\n * Token values are processed through the parser (like component tokens).\n * Use `$name` for custom properties and `#name` for color tokens.\n *\n * For color tokens (#name), boolean `true` is converted to `transparent`.\n *\n * @example\n * ```ts\n * configure({\n * tokens: {\n * $spacing: '2x',\n * '$card-padding': '4x',\n * '#accent': '#purple',\n * '#surface': '#white',\n * '#overlay': true, // → transparent\n * },\n * });\n *\n * // Now use in styles - tokens are replaced at parse time:\n * const Card = tasty({\n * styles: {\n * padding: '$card-padding', // → calc(4 * var(--gap))\n * fill: '#surface', // → var(--white-color)\n * },\n * });\n * ```\n */\n tokens?: Record<`$${string}`, string | number | boolean> &\n Record<`#${string}`, string | number | boolean>;\n /**\n * Predefined style recipes -- named style bundles that can be applied via `recipe` style property.\n * Recipe values are flat tasty styles (no sub-element keys). They may contain base styles,\n * tokens (`$name`/`#name` definitions), local states, `@keyframes`, and `@properties`.\n *\n * Components reference recipes via: `recipe: 'name1 name2'` in their styles.\n * Use `/` to separate base recipes from post recipes: `recipe: 'base1 base2 / post1'`.\n * Use `none` to skip base recipes: `recipe: 'none / post1'`.\n * Resolution order: `base_recipes → component styles → post_recipes`.\n *\n * Recipes cannot reference other recipes.\n *\n * @example\n * ```ts\n * configure({\n * recipes: {\n * card: { padding: '4x', fill: '#surface', radius: '1r', border: true },\n * elevated: { shadow: '2x 2x 4x #shadow' },\n * },\n * });\n *\n * // Usage in styles:\n * const Card = tasty({\n * styles: {\n * recipe: 'card elevated',\n * color: '#text', // Overrides recipe values\n * },\n * });\n * ```\n */\n recipes?: Record<string, RecipeStyles>;\n}\n\n// Warnings tracking to avoid duplicates\nconst emittedWarnings = new Set<string>();\n\nconst devMode = isDevEnv();\n\n/**\n * Emit a warning only once\n */\nfunction warnOnce(key: string, message: string): void {\n if (devMode && !emittedWarnings.has(key)) {\n emittedWarnings.add(key);\n console.warn(message);\n }\n}\n\n// ============================================================================\n// Configuration State\n// ============================================================================\n\n// Track whether styles have been generated (locks configuration)\nlet stylesGenerated = false;\n\n// Current configuration (null until first configure() or auto-configured on first use)\nlet currentConfig: TastyConfig | null = null;\n\n// Global keyframes storage (null = no keyframes configured, empty object checked via hasGlobalKeyframes)\nlet globalKeyframes: Record<string, KeyframesSteps> | null = null;\n\n// Global properties storage (null = no properties configured)\nlet globalProperties: Record<string, PropertyDefinition> | null = null;\n\n// Global recipes storage (null = no recipes configured)\nlet globalRecipes: Record<string, RecipeStyles> | null = null;\n\n/**\n * Internal properties required by tasty core features.\n * These are always injected when styles are first generated.\n * Keys use tasty token syntax (#name for colors, $name for other properties).\n *\n * For properties with CSS @property-compatible types (length, time, number, color),\n * an `initialValue` is provided so the property works even without a project-level token.\n */\nexport const INTERNAL_PROPERTIES: Record<string, PropertyDefinition> = {\n // Used by dual-fill feature to enable CSS transitions on the second fill color\n '#tasty-second-fill': {\n inherits: false,\n initialValue: 'transparent',\n },\n // Current color context variable (set by the color style handler).\n // Companion --current-color-rgb is auto-created.\n '#current': {\n inherits: true,\n initialValue: 'transparent',\n },\n // White and black are fundamental colors that need explicit initial values.\n // Companion -rgb properties are auto-created from the color initial values.\n '#white': {\n inherits: true,\n initialValue: 'rgb(255 255 255)',\n },\n '#black': {\n inherits: true,\n initialValue: 'rgb(0 0 0)',\n },\n\n // ---- Core design tokens used by style handlers ----\n // These provide sensible defaults so tasty works standalone without a design system.\n // Consuming projects (e.g. uikit) override these by defining tokens on :root.\n\n $gap: {\n syntax: '<length>',\n inherits: true,\n initialValue: '4px',\n },\n $radius: {\n syntax: '<length>',\n inherits: true,\n initialValue: '6px',\n },\n '$border-width': {\n syntax: '<length>',\n inherits: true,\n initialValue: '1px',\n },\n '$outline-width': {\n syntax: '<length>',\n inherits: true,\n initialValue: '3px',\n },\n $transition: {\n syntax: '<time>',\n inherits: true,\n initialValue: '80ms',\n },\n // Used by radius.ts for `radius=\"leaf\"` modifier\n '$sharp-radius': {\n syntax: '<length>',\n inherits: true,\n initialValue: '0px',\n },\n // Used by preset.ts for `preset=\"... strong\"`\n '$bold-font-weight': {\n syntax: '<number>',\n inherits: true,\n initialValue: '700',\n },\n};\n\n/**\n * Internal token defaults that cannot be expressed as CSS @property initial values\n * (e.g. font stacks, keyword colors). These are injected as :root CSS variables.\n * Consuming projects override them by setting their own tokens on :root.\n *\n * Keys are raw CSS custom property names (--name).\n */\nexport const INTERNAL_TOKENS: Record<string, string> = {\n '--font':\n 'system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\", sans-serif',\n '--monospace-font':\n 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace',\n // Default border color to the element's current text color\n '--border-color': 'currentColor',\n};\n\n// Global injector instance key\nconst GLOBAL_INJECTOR_KEY = '__TASTY_GLOBAL_INJECTOR__';\n\ninterface TastyGlobalStorage {\n [GLOBAL_INJECTOR_KEY]?: StyleInjector;\n}\n\ndeclare global {\n interface Window {\n [GLOBAL_INJECTOR_KEY]?: StyleInjector;\n }\n\n var __TASTY_GLOBAL_INJECTOR__: StyleInjector | undefined;\n}\n\n/**\n * Detect if we're running in a test environment\n */\nexport function isTestEnvironment(): boolean {\n // Check Node.js environment\n if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'test') {\n return true;\n }\n\n // Check for test runner globals (safely)\n if (typeof global !== 'undefined') {\n const g = global as unknown as Record<string, unknown>;\n if (g.vi || g.jest || g.expect || g.describe || g.it) {\n return true;\n }\n }\n\n // Check for jsdom environment (common in tests)\n if (\n typeof window !== 'undefined' &&\n window.navigator?.userAgent?.includes('jsdom')\n ) {\n return true;\n }\n\n // Check for other test runners\n if (typeof globalThis !== 'undefined') {\n const gt = globalThis as unknown as Record<string, unknown>;\n if (gt.vitest || gt.mocha) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Create default configuration with optional test environment detection\n */\nfunction createDefaultConfig(isTest?: boolean): TastyConfig {\n return {\n maxRulesPerSheet: 8192,\n unusedStylesThreshold: 500,\n bulkCleanupDelay: 5000,\n idleCleanup: true,\n forceTextInjection: isTest ?? false,\n devMode: isDevEnv(),\n bulkCleanupBatchRatio: 0.5,\n unusedStylesMinAgeMs: 10000,\n };\n}\n\n// ============================================================================\n// stylesGenerated Flag Management\n// ============================================================================\n\n/**\n * Mark that styles have been generated (called by injector on first inject)\n * This locks the configuration - no further changes allowed.\n * Also injects internal and global properties.\n */\nexport function markStylesGenerated(): void {\n if (stylesGenerated) return; // Already marked, skip\n\n stylesGenerated = true;\n\n const injector = getGlobalInjector();\n\n // Inject internal properties required by tasty core features\n for (const [token, definition] of Object.entries(INTERNAL_PROPERTIES)) {\n injector.property(token, definition);\n }\n\n // Inject internal token defaults as :root CSS variables.\n // Use injectGlobal (not injectRawCSS) so the rule goes through the same\n // injection path as all other tasty styles (consistent in both DOM and text/test mode).\n const internalTokenEntries = Object.entries(INTERNAL_TOKENS);\n if (internalTokenEntries.length > 0) {\n const declarations = internalTokenEntries\n .map(([name, value]) => `${name}: ${value}`)\n .join('; ');\n injector.injectGlobal([{ selector: ':root', declarations }]);\n }\n\n // Inject global properties if any were configured\n // Properties are permanent and only need to be injected once\n if (globalProperties && Object.keys(globalProperties).length > 0) {\n for (const [token, definition] of Object.entries(globalProperties)) {\n injector.property(token, definition);\n }\n }\n}\n\n/**\n * Check if styles have been generated (configuration is locked)\n */\nexport function hasStylesGenerated(): boolean {\n return stylesGenerated;\n}\n\n/**\n * Reset styles generated flag (for testing only)\n */\nexport function resetStylesGenerated(): void {\n stylesGenerated = false;\n emittedWarnings.clear();\n}\n\n// ============================================================================\n// Global Keyframes Management\n// ============================================================================\n\n/**\n * Check if any global keyframes are configured.\n * Fast path: returns false if no keyframes were ever set.\n */\nexport function hasGlobalKeyframes(): boolean {\n return globalKeyframes !== null && Object.keys(globalKeyframes).length > 0;\n}\n\n/**\n * Get global keyframes configuration.\n * Returns null if no keyframes configured (fast path for zero-overhead).\n */\nexport function getGlobalKeyframes(): Record<string, KeyframesSteps> | null {\n return globalKeyframes;\n}\n\n/**\n * Set global keyframes (called from configure).\n * Internal use only.\n */\nfunction setGlobalKeyframes(keyframes: Record<string, KeyframesSteps>): void {\n if (stylesGenerated) {\n warnOnce(\n 'keyframes-after-styles',\n `[Tasty] Cannot update keyframes after styles have been generated.\\n` +\n `The new keyframes will be ignored.`,\n );\n return;\n }\n globalKeyframes = keyframes;\n}\n\n// ============================================================================\n// Global Properties Management\n// ============================================================================\n\n/**\n * Check if any global properties are configured.\n * Fast path: returns false if no properties were ever set.\n */\nexport function hasGlobalProperties(): boolean {\n return globalProperties !== null && Object.keys(globalProperties).length > 0;\n}\n\n/**\n * Get global properties configuration.\n * Returns null if no properties configured (fast path for zero-overhead).\n */\nexport function getGlobalProperties(): Record<\n string,\n PropertyDefinition\n> | null {\n return globalProperties;\n}\n\n/**\n * Set global properties (called from configure).\n * Internal use only.\n */\nfunction setGlobalProperties(\n properties: Record<string, PropertyDefinition>,\n): void {\n if (stylesGenerated) {\n warnOnce(\n 'properties-after-styles',\n `[Tasty] Cannot update properties after styles have been generated.\\n` +\n `The new properties will be ignored.`,\n );\n return;\n }\n globalProperties = properties;\n}\n\n// ============================================================================\n// Global Recipes Management\n// ============================================================================\n\n/**\n * Check if any global recipes are configured.\n * Fast path: returns false if no recipes were ever set.\n */\nexport function hasGlobalRecipes(): boolean {\n return globalRecipes !== null && Object.keys(globalRecipes).length > 0;\n}\n\n/**\n * Get global recipes configuration.\n * Returns null if no recipes configured (fast path for zero-overhead).\n */\nexport function getGlobalRecipes(): Record<string, RecipeStyles> | null {\n return globalRecipes;\n}\n\n/**\n * Set global recipes (called from configure).\n * Internal use only.\n */\nfunction setGlobalRecipes(recipes: Record<string, RecipeStyles>): void {\n if (stylesGenerated) {\n warnOnce(\n 'recipes-after-styles',\n `[Tasty] Cannot update recipes after styles have been generated.\\n` +\n `The new recipes will be ignored.`,\n );\n return;\n }\n\n // Dev-mode validation\n if (devMode) {\n for (const [name, recipeStyles] of Object.entries(recipes)) {\n if (name === 'none') {\n warnOnce(\n 'recipe-reserved-none',\n `[Tasty] Recipe name \"none\" is reserved. ` +\n `It is used as a keyword meaning \"no base recipes\" ` +\n `(e.g. recipe: 'none / post-recipe'). ` +\n `Choose a different name for your recipe.`,\n );\n }\n\n for (const key of Object.keys(recipeStyles)) {\n if (isSelector(key)) {\n warnOnce(\n `recipe-selector-${name}-${key}`,\n `[Tasty] Recipe \"${name}\" contains sub-element key \"${key}\". ` +\n `Recipes must be flat styles without sub-element keys. ` +\n `Remove the sub-element key from the recipe definition.`,\n );\n }\n if (key === 'recipe') {\n warnOnce(\n `recipe-recursive-${name}`,\n `[Tasty] Recipe \"${name}\" contains a \"recipe\" key. ` +\n `Recipes cannot reference other recipes. ` +\n `Use space-separated names for composition: recipe: 'base elevated'.`,\n );\n }\n }\n }\n }\n\n globalRecipes = recipes;\n}\n\n/**\n * Check if configuration is locked (styles have been generated)\n */\nexport function isConfigLocked(): boolean {\n return stylesGenerated;\n}\n\n// ============================================================================\n// Configuration API\n// ============================================================================\n\n/**\n * Configure the Tasty style system.\n *\n * Must be called BEFORE any styles are generated (before first render that uses tasty).\n * After styles are generated, configuration is locked and calls to configure() will\n * emit a warning and be ignored.\n *\n * @example\n * ```ts\n * import { configure } from '@tenphi/tasty';\n *\n * // Configure before app renders\n * configure({\n * nonce: 'abc123',\n * states: {\n * '@mobile': '@media(w < 768px)',\n * '@dark': '@root(theme=dark)',\n * },\n * });\n * ```\n */\nexport function configure(config: Partial<TastyConfig> = {}): void {\n if (stylesGenerated) {\n warnOnce(\n 'configure-after-styles',\n `[Tasty] Cannot call configure() after styles have been generated.\\n` +\n `Configuration must be done before the first render. The configuration will be ignored.`,\n );\n return;\n }\n\n // Collect merged values from plugins first, then override with direct config\n let mergedStates: Record<string, string> = {};\n let mergedUnits: Record<string, string | UnitHandler> = {};\n let mergedFuncs: Record<string, (groups: StyleDetails[]) => string> = {};\n let mergedHandlers: Record<string, StyleHandlerDefinition> = {};\n let mergedTokens: Record<string, string | number | boolean> = {};\n let mergedRecipes: Record<string, RecipeStyles> = {};\n\n // Process plugins in order\n if (config.plugins) {\n for (const plugin of config.plugins) {\n if (plugin.states) {\n mergedStates = { ...mergedStates, ...plugin.states };\n }\n if (plugin.units) {\n mergedUnits = { ...mergedUnits, ...plugin.units };\n }\n if (plugin.funcs) {\n mergedFuncs = { ...mergedFuncs, ...plugin.funcs };\n }\n if (plugin.handlers) {\n mergedHandlers = { ...mergedHandlers, ...plugin.handlers };\n }\n if (plugin.tokens) {\n mergedTokens = { ...mergedTokens, ...plugin.tokens };\n }\n if (plugin.recipes) {\n mergedRecipes = { ...mergedRecipes, ...plugin.recipes };\n }\n }\n }\n\n // Direct config overrides plugins\n if (config.states) {\n mergedStates = { ...mergedStates, ...config.states };\n }\n if (config.units) {\n mergedUnits = { ...mergedUnits, ...config.units };\n }\n if (config.funcs) {\n mergedFuncs = { ...mergedFuncs, ...config.funcs };\n }\n if (config.handlers) {\n mergedHandlers = { ...mergedHandlers, ...config.handlers };\n }\n if (config.tokens) {\n mergedTokens = { ...mergedTokens, ...config.tokens };\n }\n if (config.recipes) {\n mergedRecipes = { ...mergedRecipes, ...config.recipes };\n }\n\n // Handle predefined states\n if (Object.keys(mergedStates).length > 0) {\n setGlobalPredefinedStates(mergedStates);\n }\n\n // Handle parser configuration (merge semantics - extend, not replace)\n const parser = getGlobalParser();\n\n if (config.parserCacheSize !== undefined) {\n parser.updateOptions({ cacheSize: config.parserCacheSize });\n }\n\n if (Object.keys(mergedUnits).length > 0) {\n // Merge with existing units\n const currentUnits = parser.getUnits() ?? CUSTOM_UNITS;\n parser.setUnits({ ...currentUnits, ...mergedUnits });\n }\n\n if (Object.keys(mergedFuncs).length > 0) {\n // Merge with existing funcs\n const currentFuncs = getGlobalFuncs();\n const finalFuncs = { ...currentFuncs, ...mergedFuncs };\n parser.setFuncs(finalFuncs);\n // Also update the global registry so customFunc() continues to work\n Object.assign(currentFuncs, mergedFuncs);\n }\n\n // Handle keyframes\n if (config.keyframes) {\n setGlobalKeyframes(config.keyframes);\n }\n\n // Handle properties\n if (config.properties) {\n setGlobalProperties(config.properties);\n }\n\n // Handle custom handlers\n if (Object.keys(mergedHandlers).length > 0) {\n for (const [name, definition] of Object.entries(mergedHandlers)) {\n const handler = normalizeHandlerDefinition(name, definition);\n registerHandler(handler);\n }\n }\n\n // Handle predefined tokens\n // Note: Tokens are processed by the classifier, not here.\n // We just store the raw values; the classifier will process them when encountered.\n if (Object.keys(mergedTokens).length > 0) {\n // Store tokens (keys are normalized to lowercase by setGlobalPredefinedTokens)\n const processedTokens: Record<string, string> = {};\n for (const [key, value] of Object.entries(mergedTokens)) {\n if (key.startsWith('#')) {\n // Color token - use shared helper for boolean handling\n const normalized = normalizeColorTokenValue(value);\n if (normalized === null) continue; // Skip false values\n processedTokens[key] = String(normalized);\n } else if (value === false) {\n // Skip false values for non-color tokens\n continue;\n } else {\n processedTokens[key] = String(value);\n }\n }\n setGlobalPredefinedTokens(processedTokens);\n }\n\n // Handle recipes\n if (Object.keys(mergedRecipes).length > 0) {\n setGlobalRecipes(mergedRecipes);\n }\n\n const {\n states: _states,\n parserCacheSize: _parserCacheSize,\n units: _units,\n funcs: _funcs,\n plugins: _plugins,\n keyframes: _keyframes,\n properties: _properties,\n handlers: _handlers,\n tokens: _tokens,\n recipes: _recipes,\n ...injectorConfig\n } = config;\n\n const fullConfig: TastyConfig = {\n ...createDefaultConfig(),\n ...currentConfig,\n ...injectorConfig,\n };\n\n // Store the config\n currentConfig = fullConfig;\n\n // Create/replace the global injector\n const storage: TastyGlobalStorage =\n typeof window !== 'undefined' ? window : globalThis;\n storage[GLOBAL_INJECTOR_KEY] = new StyleInjector(fullConfig);\n}\n\n/**\n * Get the current configuration.\n * If not configured, returns default configuration.\n */\nexport function getConfig(): TastyConfig {\n if (!currentConfig) {\n currentConfig = createDefaultConfig(isTestEnvironment());\n }\n return currentConfig;\n}\n\n/**\n * Get the global injector instance.\n * Auto-configures with defaults if not already configured.\n */\nexport function getGlobalInjector(): StyleInjector {\n const storage: TastyGlobalStorage =\n typeof window !== 'undefined' ? window : globalThis;\n\n if (!storage[GLOBAL_INJECTOR_KEY]) {\n configure();\n }\n\n return storage[GLOBAL_INJECTOR_KEY]!;\n}\n\n/**\n * Reset configuration (for testing only).\n * Clears the global injector and allows reconfiguration.\n */\nexport function resetConfig(): void {\n stylesGenerated = false;\n currentConfig = null;\n globalKeyframes = null;\n globalProperties = null;\n globalRecipes = null;\n resetGlobalPredefinedTokens();\n resetHandlers();\n clearPipelineCache();\n emittedWarnings.clear();\n\n const storage: TastyGlobalStorage =\n typeof window !== 'undefined' ? window : globalThis;\n delete storage[GLOBAL_INJECTOR_KEY];\n}\n\n// Re-export TastyConfig as StyleInjectorConfig for backward compatibility\nexport type { TastyConfig as StyleInjectorConfig };\n"],"mappings":"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"}
1
+ {"version":3,"file":"config.js","names":[],"sources":["../src/config.ts"],"sourcesContent":["/**\n * Tasty Configuration Module\n *\n * Centralizes all tasty configuration, including:\n * - Style injector settings (nonce, cleanup thresholds, etc.)\n * - Global predefined states for advanced state mapping\n * - stylesGenerated flag that locks configuration after first style generation\n *\n * Configuration must be done BEFORE any styles are generated.\n * After the first `inject()` call, configuration is locked and attempts to\n * reconfigure will emit a warning and be ignored.\n */\n\nimport { StyleInjector } from './injector/injector';\nimport { clearPipelineCache, isSelector } from './pipeline';\nimport { setGlobalPredefinedStates } from './states';\nimport {\n normalizeHandlerDefinition,\n registerHandler,\n resetHandlers,\n} from './styles/predefined';\nimport { isDevEnv } from './utils/is-dev-env';\nimport {\n CUSTOM_UNITS,\n getGlobalFuncs,\n getGlobalParser,\n normalizeColorTokenValue,\n resetGlobalPredefinedTokens,\n setGlobalPredefinedTokens,\n} from './utils/styles';\n\nimport type { KeyframesSteps, PropertyDefinition } from './injector/types';\nimport type { StyleDetails, UnitHandler } from './parser/types';\nimport type { TastyPlugin } from './plugins/types';\nimport type { RecipeStyles } from './styles/types';\nimport type { StyleHandlerDefinition } from './utils/styles';\n\n/**\n * Configuration options for the Tasty style system\n */\nexport interface TastyConfig {\n /** CSP nonce for style elements */\n nonce?: string;\n /** Maximum rules per stylesheet (default: 8192) */\n maxRulesPerSheet?: number;\n /** Threshold for bulk cleanup of unused styles (default: 500) */\n unusedStylesThreshold?: number;\n /** Delay before bulk cleanup in ms, ignored if idleCleanup is true (default: 5000) */\n bulkCleanupDelay?: number;\n /** Use requestIdleCallback for cleanup when available (default: true) */\n idleCleanup?: boolean;\n /** Force text injection mode, auto-detected in test environments (default: auto) */\n forceTextInjection?: boolean;\n /** Enable development mode features: performance metrics and debug info (default: auto) */\n devMode?: boolean;\n /**\n * Ratio of unused styles to delete per bulk cleanup run (0..1).\n * Defaults to 0.5 (oldest half) to reduce risk of removing styles\n * that may be restored shortly after being marked unused.\n */\n bulkCleanupBatchRatio?: number;\n /**\n * Minimum age (in ms) a style must remain unused before eligible for deletion.\n * Helps avoid races during rapid mount/unmount cycles. Default: 10000ms.\n */\n unusedStylesMinAgeMs?: number;\n /**\n * Global predefined states for advanced state mapping.\n * These are state aliases that can be used in any component.\n * Example: { '@mobile': '@media(w < 920px)', '@dark': '@root(theme=dark)' }\n */\n states?: Record<string, string>;\n /**\n * Parser LRU cache size (default: 1000).\n * Larger values improve performance for apps with many unique style values.\n */\n parserCacheSize?: number;\n /**\n * Custom units for the style parser (merged with built-in units).\n * Units transform numeric values like `2x` → `calc(2 * var(--gap))`.\n * @example { em: 'em', vw: 'vw', custom: (n) => `${n * 10}px` }\n */\n units?: Record<string, string | UnitHandler>;\n /**\n * Custom functions for the style parser (merged with existing).\n * Functions process parsed style groups and return CSS values.\n * @example { myFunc: (groups) => groups.map(g => g.output).join(' ') }\n */\n funcs?: Record<string, (groups: StyleDetails[]) => string>;\n /**\n * Automatically infer and register CSS @property declarations\n * from custom property values found in styles, keyframes, and global config.\n * Covers all types: \\<color\\>, \\<number\\>, \\<length\\>, \\<angle\\>, \\<percentage\\>, \\<time\\>.\n * When false, only explicitly declared @properties are registered.\n * @default true\n */\n autoPropertyTypes?: boolean;\n /**\n * Plugins that extend tasty with custom functions, units, or states.\n * Plugins are processed in order, with later plugins overriding earlier ones.\n * @example\n * ```ts\n * import { okhslPlugin } from '@tenphi/tasty';\n *\n * configure({\n * plugins: [okhslPlugin()],\n * });\n * ```\n */\n plugins?: TastyPlugin[];\n /**\n * Global keyframes definitions that can be referenced by animation names in styles.\n * Keys are animation names, values are keyframes step definitions.\n * Keyframes are only injected when actually used in styles.\n * @example\n * ```ts\n * configure({\n * keyframes: {\n * fadeIn: { from: { opacity: 0 }, to: { opacity: 1 } },\n * pulse: { '0%, 100%': { transform: 'scale(1)' }, '50%': { transform: 'scale(1.05)' } },\n * },\n * });\n * ```\n */\n keyframes?: Record<string, KeyframesSteps>;\n /**\n * Global CSS @property definitions for custom properties.\n * Keys use tasty token syntax ($name for properties, #name for colors).\n * Properties are only injected when the component using them is rendered.\n *\n * For color tokens (#name), `syntax: '<color>'` is auto-set and\n * `initialValue` defaults to `'transparent'` if not specified.\n *\n * @example\n * ```ts\n * configure({\n * properties: {\n * '$rotation': { syntax: '<angle>', initialValue: '0deg' },\n * '$scale': { syntax: '<number>', inherits: false, initialValue: 1 },\n * '#accent': { initialValue: 'purple' }, // syntax: '<color>' auto-set\n * },\n * });\n *\n * // Now use in styles - properties are registered when component renders:\n * const Spinner = tasty({\n * styles: {\n * transform: 'rotate($rotation)',\n * transition: '$$rotation 0.3s', // outputs: --rotation 0.3s\n * },\n * });\n * ```\n */\n properties?: Record<string, PropertyDefinition>;\n /**\n * Custom style handlers that transform style properties into CSS declarations.\n * Handlers replace built-in handlers for the same style name.\n * @example\n * ```ts\n * import { styleHandlers } from '@tenphi/tasty';\n *\n * configure({\n * handlers: {\n * // Override fill with custom behavior\n * fill: ({ fill }) => {\n * if (fill?.startsWith('gradient:')) {\n * return { background: fill.slice(9) };\n * }\n * return styleHandlers.fill({ fill });\n * },\n * // Add new custom style\n * elevation: ({ elevation }) => {\n * const level = parseInt(elevation) || 1;\n * return {\n * 'box-shadow': `0 ${level * 2}px ${level * 4}px rgba(0,0,0,0.1)`,\n * 'z-index': String(level * 100),\n * };\n * },\n * },\n * });\n * ```\n */\n handlers?: Record<string, StyleHandlerDefinition>;\n /**\n * Predefined tokens that are replaced during style parsing.\n * Token values are processed through the parser (like component tokens).\n * Use `$name` for custom properties and `#name` for color tokens.\n *\n * For color tokens (#name), boolean `true` is converted to `transparent`.\n *\n * @example\n * ```ts\n * configure({\n * tokens: {\n * $spacing: '2x',\n * '$card-padding': '4x',\n * '#accent': '#purple',\n * '#surface': '#white',\n * '#overlay': true, // → transparent\n * },\n * });\n *\n * // Now use in styles - tokens are replaced at parse time:\n * const Card = tasty({\n * styles: {\n * padding: '$card-padding', // → calc(4 * var(--gap))\n * fill: '#surface', // → var(--white-color)\n * },\n * });\n * ```\n */\n tokens?: Record<`$${string}`, string | number | boolean> &\n Record<`#${string}`, string | number | boolean>;\n /**\n * Predefined style recipes -- named style bundles that can be applied via `recipe` style property.\n * Recipe values are flat tasty styles (no sub-element keys). They may contain base styles,\n * tokens (`$name`/`#name` definitions), local states, `@keyframes`, and `@properties`.\n *\n * Components reference recipes via: `recipe: 'name1 name2'` in their styles.\n * Use `/` to separate base recipes from post recipes: `recipe: 'base1 base2 / post1'`.\n * Use `none` to skip base recipes: `recipe: 'none / post1'`.\n * Resolution order: `base_recipes → component styles → post_recipes`.\n *\n * Recipes cannot reference other recipes.\n *\n * @example\n * ```ts\n * configure({\n * recipes: {\n * card: { padding: '4x', fill: '#surface', radius: '1r', border: true },\n * elevated: { shadow: '2x 2x 4x #shadow' },\n * },\n * });\n *\n * // Usage in styles:\n * const Card = tasty({\n * styles: {\n * recipe: 'card elevated',\n * color: '#text', // Overrides recipe values\n * },\n * });\n * ```\n */\n recipes?: Record<string, RecipeStyles>;\n}\n\n// Warnings tracking to avoid duplicates\nconst emittedWarnings = new Set<string>();\n\nconst devMode = isDevEnv();\n\n/**\n * Emit a warning only once\n */\nfunction warnOnce(key: string, message: string): void {\n if (devMode && !emittedWarnings.has(key)) {\n emittedWarnings.add(key);\n console.warn(message);\n }\n}\n\n// ============================================================================\n// Configuration State\n// ============================================================================\n\n// Track whether styles have been generated (locks configuration)\nlet stylesGenerated = false;\n\n// Current configuration (null until first configure() or auto-configured on first use)\nlet currentConfig: TastyConfig | null = null;\n\n// Global keyframes storage (null = no keyframes configured, empty object checked via hasGlobalKeyframes)\nlet globalKeyframes: Record<string, KeyframesSteps> | null = null;\n\n// Global properties storage (null = no properties configured)\nlet globalProperties: Record<string, PropertyDefinition> | null = null;\n\n// Global recipes storage (null = no recipes configured)\nlet globalRecipes: Record<string, RecipeStyles> | null = null;\n\n/**\n * Internal properties required by tasty core features.\n * These are always injected when styles are first generated.\n * Keys use tasty token syntax (#name for colors, $name for other properties).\n *\n * For properties with CSS @property-compatible types (length, time, number, color),\n * an `initialValue` is provided so the property works even without a project-level token.\n */\nexport const INTERNAL_PROPERTIES: Record<string, PropertyDefinition> = {\n // Used by dual-fill feature to enable CSS transitions on the second fill color\n '#tasty-second-fill': {\n inherits: false,\n initialValue: 'transparent',\n },\n // Current color context variable (set by the color style handler).\n '#current': {\n inherits: true,\n initialValue: 'transparent',\n },\n // White and black are fundamental colors that need explicit initial values.\n '#white': {\n inherits: true,\n initialValue: 'rgb(255 255 255)',\n },\n '#black': {\n inherits: true,\n initialValue: 'rgb(0 0 0)',\n },\n\n // ---- Core design tokens used by style handlers ----\n // These provide sensible defaults so tasty works standalone without a design system.\n // Consuming projects (e.g. uikit) override these by defining tokens on :root.\n\n $gap: {\n syntax: '<length>',\n inherits: true,\n initialValue: '4px',\n },\n $radius: {\n syntax: '<length>',\n inherits: true,\n initialValue: '6px',\n },\n '$border-width': {\n syntax: '<length>',\n inherits: true,\n initialValue: '1px',\n },\n '$outline-width': {\n syntax: '<length>',\n inherits: true,\n initialValue: '3px',\n },\n $transition: {\n syntax: '<time>',\n inherits: true,\n initialValue: '80ms',\n },\n // Used by radius.ts for `radius=\"leaf\"` modifier\n '$sharp-radius': {\n syntax: '<length>',\n inherits: true,\n initialValue: '0px',\n },\n // Used by preset.ts for `preset=\"... strong\"`\n '$bold-font-weight': {\n syntax: '<number>',\n inherits: true,\n initialValue: '700',\n },\n};\n\n/**\n * Internal token defaults that cannot be expressed as CSS @property initial values\n * (e.g. font stacks, keyword colors). These are injected as :root CSS variables.\n * Consuming projects override them by setting their own tokens on :root.\n *\n * Keys are raw CSS custom property names (--name).\n */\nexport const INTERNAL_TOKENS: Record<string, string> = {\n '--font':\n 'system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\", sans-serif',\n '--monospace-font':\n 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace',\n // Default border color to the element's current text color\n '--border-color': 'currentColor',\n};\n\n// Global injector instance key\nconst GLOBAL_INJECTOR_KEY = '__TASTY_GLOBAL_INJECTOR__';\n\ninterface TastyGlobalStorage {\n [GLOBAL_INJECTOR_KEY]?: StyleInjector;\n}\n\ndeclare global {\n interface Window {\n [GLOBAL_INJECTOR_KEY]?: StyleInjector;\n }\n\n var __TASTY_GLOBAL_INJECTOR__: StyleInjector | undefined;\n}\n\n/**\n * Detect if we're running in a test environment\n */\nexport function isTestEnvironment(): boolean {\n // Check Node.js environment\n if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'test') {\n return true;\n }\n\n // Check for test runner globals (safely)\n if (typeof global !== 'undefined') {\n const g = global as unknown as Record<string, unknown>;\n if (g.vi || g.jest || g.expect || g.describe || g.it) {\n return true;\n }\n }\n\n // Check for jsdom environment (common in tests)\n if (\n typeof window !== 'undefined' &&\n window.navigator?.userAgent?.includes('jsdom')\n ) {\n return true;\n }\n\n // Check for other test runners\n if (typeof globalThis !== 'undefined') {\n const gt = globalThis as unknown as Record<string, unknown>;\n if (gt.vitest || gt.mocha) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Create default configuration with optional test environment detection\n */\nfunction createDefaultConfig(isTest?: boolean): TastyConfig {\n return {\n maxRulesPerSheet: 8192,\n unusedStylesThreshold: 500,\n bulkCleanupDelay: 5000,\n idleCleanup: true,\n forceTextInjection: isTest ?? false,\n devMode: isDevEnv(),\n bulkCleanupBatchRatio: 0.5,\n unusedStylesMinAgeMs: 10000,\n };\n}\n\n// ============================================================================\n// stylesGenerated Flag Management\n// ============================================================================\n\n/**\n * Mark that styles have been generated (called by injector on first inject)\n * This locks the configuration - no further changes allowed.\n * Also injects internal and global properties.\n */\nexport function markStylesGenerated(): void {\n if (stylesGenerated) return; // Already marked, skip\n\n stylesGenerated = true;\n\n const injector = getGlobalInjector();\n\n // Inject internal properties required by tasty core features\n for (const [token, definition] of Object.entries(INTERNAL_PROPERTIES)) {\n injector.property(token, definition);\n }\n\n // Inject internal token defaults as :root CSS variables.\n // Use injectGlobal (not injectRawCSS) so the rule goes through the same\n // injection path as all other tasty styles (consistent in both DOM and text/test mode).\n const internalTokenEntries = Object.entries(INTERNAL_TOKENS);\n if (internalTokenEntries.length > 0) {\n const declarations = internalTokenEntries\n .map(([name, value]) => `${name}: ${value}`)\n .join('; ');\n injector.injectGlobal([{ selector: ':root', declarations }]);\n }\n\n // Inject global properties if any were configured\n // Properties are permanent and only need to be injected once\n if (globalProperties && Object.keys(globalProperties).length > 0) {\n for (const [token, definition] of Object.entries(globalProperties)) {\n injector.property(token, definition);\n }\n }\n}\n\n/**\n * Check if styles have been generated (configuration is locked)\n */\nexport function hasStylesGenerated(): boolean {\n return stylesGenerated;\n}\n\n/**\n * Reset styles generated flag (for testing only)\n */\nexport function resetStylesGenerated(): void {\n stylesGenerated = false;\n emittedWarnings.clear();\n}\n\n// ============================================================================\n// Global Keyframes Management\n// ============================================================================\n\n/**\n * Check if any global keyframes are configured.\n * Fast path: returns false if no keyframes were ever set.\n */\nexport function hasGlobalKeyframes(): boolean {\n return globalKeyframes !== null && Object.keys(globalKeyframes).length > 0;\n}\n\n/**\n * Get global keyframes configuration.\n * Returns null if no keyframes configured (fast path for zero-overhead).\n */\nexport function getGlobalKeyframes(): Record<string, KeyframesSteps> | null {\n return globalKeyframes;\n}\n\n/**\n * Set global keyframes (called from configure).\n * Internal use only.\n */\nfunction setGlobalKeyframes(keyframes: Record<string, KeyframesSteps>): void {\n if (stylesGenerated) {\n warnOnce(\n 'keyframes-after-styles',\n `[Tasty] Cannot update keyframes after styles have been generated.\\n` +\n `The new keyframes will be ignored.`,\n );\n return;\n }\n globalKeyframes = keyframes;\n}\n\n// ============================================================================\n// Global Properties Management\n// ============================================================================\n\n/**\n * Check if any global properties are configured.\n * Fast path: returns false if no properties were ever set.\n */\nexport function hasGlobalProperties(): boolean {\n return globalProperties !== null && Object.keys(globalProperties).length > 0;\n}\n\n/**\n * Get global properties configuration.\n * Returns null if no properties configured (fast path for zero-overhead).\n */\nexport function getGlobalProperties(): Record<\n string,\n PropertyDefinition\n> | null {\n return globalProperties;\n}\n\n/**\n * Set global properties (called from configure).\n * Internal use only.\n */\nfunction setGlobalProperties(\n properties: Record<string, PropertyDefinition>,\n): void {\n if (stylesGenerated) {\n warnOnce(\n 'properties-after-styles',\n `[Tasty] Cannot update properties after styles have been generated.\\n` +\n `The new properties will be ignored.`,\n );\n return;\n }\n globalProperties = properties;\n}\n\n// ============================================================================\n// Global Recipes Management\n// ============================================================================\n\n/**\n * Check if any global recipes are configured.\n * Fast path: returns false if no recipes were ever set.\n */\nexport function hasGlobalRecipes(): boolean {\n return globalRecipes !== null && Object.keys(globalRecipes).length > 0;\n}\n\n/**\n * Get global recipes configuration.\n * Returns null if no recipes configured (fast path for zero-overhead).\n */\nexport function getGlobalRecipes(): Record<string, RecipeStyles> | null {\n return globalRecipes;\n}\n\n/**\n * Set global recipes (called from configure).\n * Internal use only.\n */\nfunction setGlobalRecipes(recipes: Record<string, RecipeStyles>): void {\n if (stylesGenerated) {\n warnOnce(\n 'recipes-after-styles',\n `[Tasty] Cannot update recipes after styles have been generated.\\n` +\n `The new recipes will be ignored.`,\n );\n return;\n }\n\n // Dev-mode validation\n if (devMode) {\n for (const [name, recipeStyles] of Object.entries(recipes)) {\n if (name === 'none') {\n warnOnce(\n 'recipe-reserved-none',\n `[Tasty] Recipe name \"none\" is reserved. ` +\n `It is used as a keyword meaning \"no base recipes\" ` +\n `(e.g. recipe: 'none / post-recipe'). ` +\n `Choose a different name for your recipe.`,\n );\n }\n\n for (const key of Object.keys(recipeStyles)) {\n if (isSelector(key)) {\n warnOnce(\n `recipe-selector-${name}-${key}`,\n `[Tasty] Recipe \"${name}\" contains sub-element key \"${key}\". ` +\n `Recipes must be flat styles without sub-element keys. ` +\n `Remove the sub-element key from the recipe definition.`,\n );\n }\n if (key === 'recipe') {\n warnOnce(\n `recipe-recursive-${name}`,\n `[Tasty] Recipe \"${name}\" contains a \"recipe\" key. ` +\n `Recipes cannot reference other recipes. ` +\n `Use space-separated names for composition: recipe: 'base elevated'.`,\n );\n }\n }\n }\n }\n\n globalRecipes = recipes;\n}\n\n/**\n * Check if configuration is locked (styles have been generated)\n */\nexport function isConfigLocked(): boolean {\n return stylesGenerated;\n}\n\n// ============================================================================\n// Configuration API\n// ============================================================================\n\n/**\n * Configure the Tasty style system.\n *\n * Must be called BEFORE any styles are generated (before first render that uses tasty).\n * After styles are generated, configuration is locked and calls to configure() will\n * emit a warning and be ignored.\n *\n * @example\n * ```ts\n * import { configure } from '@tenphi/tasty';\n *\n * // Configure before app renders\n * configure({\n * nonce: 'abc123',\n * states: {\n * '@mobile': '@media(w < 768px)',\n * '@dark': '@root(theme=dark)',\n * },\n * });\n * ```\n */\nexport function configure(config: Partial<TastyConfig> = {}): void {\n if (stylesGenerated) {\n warnOnce(\n 'configure-after-styles',\n `[Tasty] Cannot call configure() after styles have been generated.\\n` +\n `Configuration must be done before the first render. The configuration will be ignored.`,\n );\n return;\n }\n\n // Collect merged values from plugins first, then override with direct config\n let mergedStates: Record<string, string> = {};\n let mergedUnits: Record<string, string | UnitHandler> = {};\n let mergedFuncs: Record<string, (groups: StyleDetails[]) => string> = {};\n let mergedHandlers: Record<string, StyleHandlerDefinition> = {};\n let mergedTokens: Record<string, string | number | boolean> = {};\n let mergedRecipes: Record<string, RecipeStyles> = {};\n\n // Process plugins in order\n if (config.plugins) {\n for (const plugin of config.plugins) {\n if (plugin.states) {\n mergedStates = { ...mergedStates, ...plugin.states };\n }\n if (plugin.units) {\n mergedUnits = { ...mergedUnits, ...plugin.units };\n }\n if (plugin.funcs) {\n mergedFuncs = { ...mergedFuncs, ...plugin.funcs };\n }\n if (plugin.handlers) {\n mergedHandlers = { ...mergedHandlers, ...plugin.handlers };\n }\n if (plugin.tokens) {\n mergedTokens = { ...mergedTokens, ...plugin.tokens };\n }\n if (plugin.recipes) {\n mergedRecipes = { ...mergedRecipes, ...plugin.recipes };\n }\n }\n }\n\n // Direct config overrides plugins\n if (config.states) {\n mergedStates = { ...mergedStates, ...config.states };\n }\n if (config.units) {\n mergedUnits = { ...mergedUnits, ...config.units };\n }\n if (config.funcs) {\n mergedFuncs = { ...mergedFuncs, ...config.funcs };\n }\n if (config.handlers) {\n mergedHandlers = { ...mergedHandlers, ...config.handlers };\n }\n if (config.tokens) {\n mergedTokens = { ...mergedTokens, ...config.tokens };\n }\n if (config.recipes) {\n mergedRecipes = { ...mergedRecipes, ...config.recipes };\n }\n\n // Handle predefined states\n if (Object.keys(mergedStates).length > 0) {\n setGlobalPredefinedStates(mergedStates);\n }\n\n // Handle parser configuration (merge semantics - extend, not replace)\n const parser = getGlobalParser();\n\n if (config.parserCacheSize !== undefined) {\n parser.updateOptions({ cacheSize: config.parserCacheSize });\n }\n\n if (Object.keys(mergedUnits).length > 0) {\n // Merge with existing units\n const currentUnits = parser.getUnits() ?? CUSTOM_UNITS;\n parser.setUnits({ ...currentUnits, ...mergedUnits });\n }\n\n if (Object.keys(mergedFuncs).length > 0) {\n // Merge with existing funcs\n const currentFuncs = getGlobalFuncs();\n const finalFuncs = { ...currentFuncs, ...mergedFuncs };\n parser.setFuncs(finalFuncs);\n // Also update the global registry so customFunc() continues to work\n Object.assign(currentFuncs, mergedFuncs);\n }\n\n // Handle keyframes\n if (config.keyframes) {\n setGlobalKeyframes(config.keyframes);\n }\n\n // Handle properties\n if (config.properties) {\n setGlobalProperties(config.properties);\n }\n\n // Handle custom handlers\n if (Object.keys(mergedHandlers).length > 0) {\n for (const [name, definition] of Object.entries(mergedHandlers)) {\n const handler = normalizeHandlerDefinition(name, definition);\n registerHandler(handler);\n }\n }\n\n // Handle predefined tokens\n // Note: Tokens are processed by the classifier, not here.\n // We just store the raw values; the classifier will process them when encountered.\n if (Object.keys(mergedTokens).length > 0) {\n // Store tokens (keys are normalized to lowercase by setGlobalPredefinedTokens)\n const processedTokens: Record<string, string> = {};\n for (const [key, value] of Object.entries(mergedTokens)) {\n if (key.startsWith('#')) {\n // Color token - use shared helper for boolean handling\n const normalized = normalizeColorTokenValue(value);\n if (normalized === null) continue; // Skip false values\n processedTokens[key] = String(normalized);\n } else if (value === false) {\n // Skip false values for non-color tokens\n continue;\n } else {\n processedTokens[key] = String(value);\n }\n }\n setGlobalPredefinedTokens(processedTokens);\n }\n\n // Handle recipes\n if (Object.keys(mergedRecipes).length > 0) {\n setGlobalRecipes(mergedRecipes);\n }\n\n const {\n states: _states,\n parserCacheSize: _parserCacheSize,\n units: _units,\n funcs: _funcs,\n plugins: _plugins,\n keyframes: _keyframes,\n properties: _properties,\n handlers: _handlers,\n tokens: _tokens,\n recipes: _recipes,\n ...injectorConfig\n } = config;\n\n const fullConfig: TastyConfig = {\n ...createDefaultConfig(),\n ...currentConfig,\n ...injectorConfig,\n };\n\n // Store the config\n currentConfig = fullConfig;\n\n // Create/replace the global injector\n const storage: TastyGlobalStorage =\n typeof window !== 'undefined' ? window : globalThis;\n storage[GLOBAL_INJECTOR_KEY] = new StyleInjector(fullConfig);\n}\n\n/**\n * Get the current configuration.\n * If not configured, returns default configuration.\n */\nexport function getConfig(): TastyConfig {\n if (!currentConfig) {\n currentConfig = createDefaultConfig(isTestEnvironment());\n }\n return currentConfig;\n}\n\n/**\n * Get the global injector instance.\n * Auto-configures with defaults if not already configured.\n */\nexport function getGlobalInjector(): StyleInjector {\n const storage: TastyGlobalStorage =\n typeof window !== 'undefined' ? window : globalThis;\n\n if (!storage[GLOBAL_INJECTOR_KEY]) {\n configure();\n }\n\n return storage[GLOBAL_INJECTOR_KEY]!;\n}\n\n/**\n * Reset configuration (for testing only).\n * Clears the global injector and allows reconfiguration.\n */\nexport function resetConfig(): void {\n stylesGenerated = false;\n currentConfig = null;\n globalKeyframes = null;\n globalProperties = null;\n globalRecipes = null;\n resetGlobalPredefinedTokens();\n resetHandlers();\n clearPipelineCache();\n emittedWarnings.clear();\n\n const storage: TastyGlobalStorage =\n typeof window !== 'undefined' ? window : globalThis;\n delete storage[GLOBAL_INJECTOR_KEY];\n}\n\n// Re-export TastyConfig as StyleInjectorConfig for backward compatibility\nexport type { TastyConfig as StyleInjectorConfig };\n"],"mappings":"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"}
@@ -3,7 +3,7 @@ import { CSSProperties } from "../utils/css-types.js";
3
3
  import { Bucket, ParserOptions, ProcessedStyle, StyleDetails, StyleDetailsPart, UnitHandler } from "../parser/types.js";
4
4
  import { StyleParser } from "../parser/parser.js";
5
5
  import { AtRuleContext, ParsedAdvancedState, StateParserContext, createStateParserContext, getGlobalPredefinedStates, setGlobalPredefinedStates } from "../states/index.js";
6
- import { COMPUTE_FUNC_MAP, CSSMap, CUSTOM_UNITS, ComputeModel, DIRECTIONS, ParsedColor, RawStyleHandler, STATE_OPERATORS, STATE_OPERATOR_LIST, StyleHandler, StyleHandlerDefinition, StyleHandlerResult, StyleMap, StylePropValue, StyleStateData, StyleStateDataList, StyleStateDataListMap, StyleStateMap, StyleValue, StyleValueStateMap, buildAtRuleContext, computeState, customFunc, extendStyles, extractStyles, filterMods, getGlobalFuncs, getGlobalParser, getGlobalPredefinedTokens, getModSelector, getRgbValuesFromRgbaString, hexToRgb, isAdvancedStateToken, normalizeColorTokenValue, parseColor, parseStateNotation, parseStyle, resetGlobalPredefinedTokens, setGlobalPredefinedTokens, strToRgb, stringifyStyles, styleStateMapToStyleStateDataList } from "../utils/styles.js";
6
+ import { COMPUTE_FUNC_MAP, CSSMap, CUSTOM_UNITS, ComputeModel, DIRECTIONS, ParsedColor, RawStyleHandler, STATE_OPERATORS, STATE_OPERATOR_LIST, StyleHandler, StyleHandlerDefinition, StyleHandlerResult, StyleMap, StylePropValue, StyleStateData, StyleStateDataList, StyleStateDataListMap, StyleStateMap, StyleValue, StyleValueStateMap, buildAtRuleContext, computeState, customFunc, extendStyles, extractStyles, filterMods, getGlobalFuncs, getGlobalParser, getGlobalPredefinedTokens, getRgbValuesFromRgbaString, hexToRgb, isAdvancedStateToken, normalizeColorTokenValue, parseColor, parseStateNotation, parseStyle, resetGlobalPredefinedTokens, setGlobalPredefinedTokens, strToRgb, stringifyStyles, styleStateMapToStyleStateDataList } from "../utils/styles.js";
7
7
  import { NoType, NotSelector, RecipeStyles, Selector, Styles, StylesInterface, StylesWithoutSelectors, SuffixForSelector, TastyNamedColors, TastyPresetNames } from "../styles/types.js";
8
8
  import { ConditionNode } from "../pipeline/conditions.js";
9
9
  import { ParseStateKeyOptions, parseStateKey } from "../pipeline/parseStateKey.js";
@@ -14,7 +14,7 @@ import { TastyPlugin, TastyPluginFactory } from "../plugins/types.js";
14
14
  import { TastyConfig, configure, getConfig, getGlobalKeyframes, getGlobalRecipes, hasGlobalKeyframes, hasGlobalRecipes, hasStylesGenerated, isConfigLocked, isTestEnvironment, resetConfig } from "../config.js";
15
15
  import { okhslFunc, okhslPlugin } from "../plugins/okhsl-plugin.js";
16
16
  import { CHUNK_NAMES, ChunkInfo, ChunkName, STYLE_TO_CHUNK, categorizeStyleKeys } from "../chunks/definitions.js";
17
- import { PropertyOptions, allocateClassName, cleanup, createInjector, destroy, getCssText, getCssTextForNode, getIsTestEnvironment, getRawCSSText, inject, injectGlobal, injectRawCSS, injector, isPropertyDefined, keyframes, property } from "../injector/index.js";
17
+ import { PropertyOptions, allocateClassName, cleanup, createInjector, destroy, getCssText, getCssTextForNode, getIsTestEnvironment, getRawCSSText, inject, injectGlobal, injectRawCSS, injector, isPropertyDefined, keyframes, property, trackRef } from "../injector/index.js";
18
18
  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";
19
19
  import { BaseStyleProps, BlockInnerStyleProps, BlockOuterStyleProps, BlockStyleProps, ColorStyleProps, ContainerStyleProps, DimensionStyleProps, FlowStyleProps, GlobalStyledProps, InnerStyleProps, ModValue, Mods, OuterStyleProps, PositionStyleProps, Props, ShortGridStyles, TagName, TastyExtensionConfig, TastyThemeNames, TextStyleProps, TokenValue, Tokens } from "../types.js";
20
20
  import { styleHandlers } from "../styles/predefined.js";
@@ -30,4 +30,4 @@ import { hslToRgbValues, processTokens, stringifyTokens } from "../utils/process
30
30
  import { TypographyPreset } from "../tokens/typography.js";
31
31
  import { generateTypographyTokens } from "../utils/typography.js";
32
32
  import { tastyDebug } from "../debug.js";
33
- export { type AtRuleContext, BASE_STYLES, BLOCK_INNER_STYLES, BLOCK_OUTER_STYLES, BLOCK_STYLES, type BaseStyleProps, type BlockInnerStyleProps, type BlockOuterStyleProps, type BlockStyleProps, Bucket, CHUNK_NAMES, COLOR_STYLES, COMPUTE_FUNC_MAP, CONTAINER_STYLES, CSSMap, type CSSProperties, CUSTOM_UNITS, CacheMetrics, type ChunkInfo, type ChunkName, type ColorStyleProps, ComputeModel, type ConditionNode, type ContainerStyleProps, DIMENSION_STYLES, DIRECTIONS, type DimensionStyleProps, DisposeFunction, FLOW_STYLES, type FlowStyleProps, type GlobalStyledProps, INNER_STYLES, InjectResult, type InnerStyleProps, KeyframesCacheEntry, KeyframesInfo, KeyframesResult, KeyframesSteps, type ModValue, type Mods, type NoType, type NotSelector, OUTER_STYLES, type OuterStyleProps, POSITION_STYLES, type ParseStateKeyOptions, type ParsedAdvancedState, ParsedColor, type ParserOptions, type PositionStyleProps, type ProcessedStyle, PropertyDefinition, PropertyOptions, type Props, RawCSSResult, RawStyleHandler, type RecipeStyles, type RenderResult, RootRegistry, RuleInfo, STATE_OPERATORS, STATE_OPERATOR_LIST, STYLE_TO_CHUNK, type Selector, SheetInfo, SheetManager, type ShortGridStyles, type StateParserContext, type StyleDetails, type StyleDetailsPart, StyleHandler, StyleHandlerDefinition, StyleHandlerResult, StyleInjector, StyleInjectorConfig, StyleMap, StyleParser, StylePropValue, type StyleResult, StyleRule, StyleStateData, StyleStateDataList, StyleStateDataListMap, StyleStateMap, StyleValue, StyleValueStateMap, type Styles, type StylesInterface, type StylesWithoutSelectors, type SuffixForSelector, TEXT_STYLES, type TagName, type TastyConfig, type TastyExtensionConfig, type TastyNamedColors, type TastyPlugin, type TastyPluginFactory, type TastyPresetNames, type TastyThemeNames, type TextStyleProps, type TokenValue, type Tokens, TypographyPreset, type UnitHandler, allocateClassName, buildAtRuleContext, categorizeStyleKeys, cleanup, color, computeState, configure, createInjector, createStateParserContext, customFunc, deprecationWarning, destroy, dotize, extendStyles, extractStyles, filterBaseProps, filterMods, generateTypographyTokens, getConfig, getCssText, getCssTextForNode, getGlobalFuncs, getGlobalKeyframes, getGlobalParser, getGlobalPredefinedStates, getGlobalPredefinedTokens, getGlobalRecipes, getIsTestEnvironment, getModSelector, getRawCSSText, getRgbValuesFromRgbaString, hasGlobalKeyframes, hasGlobalRecipes, hasStylesGenerated, hexToRgb, hslToRgbValues, inject, injectGlobal, injectRawCSS, injector, isAdvancedStateToken, isConfigLocked, isPropertyDefined, isSelector, isTestEnvironment, keyframes, mergeStyles, _modAttrs as modAttrs, normalizeColorTokenValue, okhslFunc, okhslPlugin, parseColor, parseStateKey, parseStateNotation, parseStyle, processTokens, property, renderStyles, resetConfig, resetGlobalPredefinedTokens, resolveRecipes, setGlobalPredefinedStates, setGlobalPredefinedTokens, strToRgb, stringifyStyles, stringifyTokens, styleHandlers, styleStateMapToStyleStateDataList, tastyDebug, warn };
33
+ export { type AtRuleContext, BASE_STYLES, BLOCK_INNER_STYLES, BLOCK_OUTER_STYLES, BLOCK_STYLES, type BaseStyleProps, type BlockInnerStyleProps, type BlockOuterStyleProps, type BlockStyleProps, Bucket, CHUNK_NAMES, COLOR_STYLES, COMPUTE_FUNC_MAP, CONTAINER_STYLES, CSSMap, type CSSProperties, CUSTOM_UNITS, CacheMetrics, type ChunkInfo, type ChunkName, type ColorStyleProps, ComputeModel, type ConditionNode, type ContainerStyleProps, DIMENSION_STYLES, DIRECTIONS, type DimensionStyleProps, DisposeFunction, FLOW_STYLES, type FlowStyleProps, type GlobalStyledProps, INNER_STYLES, InjectResult, type InnerStyleProps, KeyframesCacheEntry, KeyframesInfo, KeyframesResult, KeyframesSteps, type ModValue, type Mods, type NoType, type NotSelector, OUTER_STYLES, type OuterStyleProps, POSITION_STYLES, type ParseStateKeyOptions, type ParsedAdvancedState, ParsedColor, type ParserOptions, type PositionStyleProps, type ProcessedStyle, PropertyDefinition, PropertyOptions, type Props, RawCSSResult, RawStyleHandler, type RecipeStyles, type RenderResult, RootRegistry, RuleInfo, STATE_OPERATORS, STATE_OPERATOR_LIST, STYLE_TO_CHUNK, type Selector, SheetInfo, SheetManager, type ShortGridStyles, type StateParserContext, type StyleDetails, type StyleDetailsPart, StyleHandler, StyleHandlerDefinition, StyleHandlerResult, StyleInjector, StyleInjectorConfig, StyleMap, StyleParser, StylePropValue, type StyleResult, StyleRule, StyleStateData, StyleStateDataList, StyleStateDataListMap, StyleStateMap, StyleValue, StyleValueStateMap, type Styles, type StylesInterface, type StylesWithoutSelectors, type SuffixForSelector, TEXT_STYLES, type TagName, type TastyConfig, type TastyExtensionConfig, type TastyNamedColors, type TastyPlugin, type TastyPluginFactory, type TastyPresetNames, type TastyThemeNames, type TextStyleProps, type TokenValue, type Tokens, TypographyPreset, type UnitHandler, allocateClassName, buildAtRuleContext, categorizeStyleKeys, cleanup, color, computeState, configure, createInjector, createStateParserContext, customFunc, deprecationWarning, destroy, dotize, extendStyles, extractStyles, filterBaseProps, filterMods, generateTypographyTokens, getConfig, getCssText, getCssTextForNode, getGlobalFuncs, getGlobalKeyframes, getGlobalParser, getGlobalPredefinedStates, getGlobalPredefinedTokens, getGlobalRecipes, getIsTestEnvironment, getRawCSSText, getRgbValuesFromRgbaString, hasGlobalKeyframes, hasGlobalRecipes, hasStylesGenerated, hexToRgb, hslToRgbValues, inject, injectGlobal, injectRawCSS, injector, isAdvancedStateToken, isConfigLocked, isPropertyDefined, isSelector, isTestEnvironment, keyframes, mergeStyles, _modAttrs as modAttrs, normalizeColorTokenValue, okhslFunc, okhslPlugin, parseColor, parseStateKey, parseStateNotation, parseStyle, processTokens, property, renderStyles, resetConfig, resetGlobalPredefinedTokens, resolveRecipes, setGlobalPredefinedStates, setGlobalPredefinedTokens, strToRgb, stringifyStyles, stringifyTokens, styleHandlers, styleStateMapToStyleStateDataList, tastyDebug, trackRef, warn };