@tenphi/tasty 3.4.0 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +147 -129
  2. package/dist/astro-ib7E7V4Y.js +307 -0
  3. package/dist/astro-ib7E7V4Y.js.map +1 -0
  4. package/dist/{babel-BUQGeOXA.d.ts → babel-DWCsafYw.d.ts} +2 -2
  5. package/dist/{collector-DUaHCcTS.d.ts → collector-BTVXj2hS.d.ts} +21 -3
  6. package/dist/{collector-DTahQUiV.js → collector-C6TtL8HJ.js} +36 -13
  7. package/dist/collector-C6TtL8HJ.js.map +1 -0
  8. package/dist/{config-B5kHzuNz.js → config-B3gPdCqd.js} +305 -153
  9. package/dist/config-B3gPdCqd.js.map +1 -0
  10. package/dist/{config-YsxGv4tq.d.ts → config-LfIDmVHx.d.ts} +132 -32
  11. package/dist/core/index.d.ts +5 -5
  12. package/dist/core/index.js +6 -6
  13. package/dist/{core-Dr4u1NVD.js → core-Bq7w2kti.js} +215 -95
  14. package/dist/core-Bq7w2kti.js.map +1 -0
  15. package/dist/{css-writer-B-J87ncv.js → css-writer-Bgowobbx.js} +3 -3
  16. package/dist/{css-writer-B-J87ncv.js.map → css-writer-Bgowobbx.js.map} +1 -1
  17. package/dist/{format-rules-DKOA-6qu.js → format-rules-rCZ37rqY.js} +2 -2
  18. package/dist/format-rules-rCZ37rqY.js.map +1 -0
  19. package/dist/{hydrate-OeMX99We.js → hydrate-CNOmZprz.js} +3 -3
  20. package/dist/{hydrate-OeMX99We.js.map → hydrate-CNOmZprz.js.map} +1 -1
  21. package/dist/{index-PqN-DIpn.d.ts → index-Bm9ltvdH.d.ts} +38 -16
  22. package/dist/{index-Cd45t5NM.d.ts → index-BmogEzTo.d.ts} +69 -15
  23. package/dist/index.d.ts +5 -5
  24. package/dist/index.js +7 -7
  25. package/dist/index.js.map +1 -1
  26. package/dist/{keyframes-CV8azJf3.js → keyframes-DE-OE76F.js} +45 -3
  27. package/dist/keyframes-DE-OE76F.js.map +1 -0
  28. package/dist/{merge-styles-CU7JbEwg.d.ts → merge-styles-BcLl1u_d.d.ts} +2 -2
  29. package/dist/{merge-styles-oklji0KB.js → merge-styles-DuoZEsm9.js} +2 -2
  30. package/dist/{merge-styles-oklji0KB.js.map → merge-styles-DuoZEsm9.js.map} +1 -1
  31. package/dist/{resolve-recipes-DTG81rzl.js → resolve-recipes-H9NqOQuP.js} +3 -3
  32. package/dist/{resolve-recipes-DTG81rzl.js.map → resolve-recipes-H9NqOQuP.js.map} +1 -1
  33. package/dist/ssr/astro-client.js +1 -1
  34. package/dist/ssr/astro-middleware-extract-static.d.ts +11 -0
  35. package/dist/ssr/astro-middleware-extract-static.js +9 -0
  36. package/dist/ssr/astro-middleware-extract-static.js.map +1 -0
  37. package/dist/ssr/astro-middleware-extract.d.ts +11 -0
  38. package/dist/ssr/astro-middleware-extract.js +9 -0
  39. package/dist/ssr/astro-middleware-extract.js.map +1 -0
  40. package/dist/ssr/astro-middleware-static.d.ts +3 -1
  41. package/dist/ssr/astro-middleware-static.js +1 -1
  42. package/dist/ssr/astro-middleware.d.ts +3 -1
  43. package/dist/ssr/astro-middleware.js +1 -1
  44. package/dist/ssr/astro.d.ts +37 -3
  45. package/dist/ssr/astro.js +1 -156
  46. package/dist/ssr/index.d.ts +2 -2
  47. package/dist/ssr/index.js +3 -3
  48. package/dist/ssr/index.js.map +1 -1
  49. package/dist/ssr/next.d.ts +1 -1
  50. package/dist/ssr/next.js +4 -4
  51. package/dist/static/index.d.ts +2 -2
  52. package/dist/static/index.js +1 -1
  53. package/dist/zero/babel.d.ts +1 -1
  54. package/dist/zero/babel.js +4 -4
  55. package/dist/zero/babel.js.map +1 -1
  56. package/dist/zero/index.d.ts +1 -1
  57. package/dist/zero/index.js +1 -1
  58. package/dist/zero/next.d.ts +1 -1
  59. package/docs/README.md +13 -8
  60. package/docs/adoption.md +56 -31
  61. package/docs/ai-agents.md +78 -77
  62. package/docs/comparison.md +69 -40
  63. package/docs/configuration.md +97 -87
  64. package/docs/debug.md +4 -2
  65. package/docs/design-system.md +41 -22
  66. package/docs/dsl.md +225 -195
  67. package/docs/getting-started.md +60 -39
  68. package/docs/injector.md +63 -26
  69. package/docs/methodology.md +42 -36
  70. package/docs/migration-v3.md +49 -49
  71. package/docs/plugins.md +37 -33
  72. package/docs/react-api.md +65 -44
  73. package/docs/runtime-benchmarks.md +178 -0
  74. package/docs/ssr.md +99 -61
  75. package/docs/styles.md +1 -1
  76. package/docs/tasty-static.md +137 -104
  77. package/package.json +26 -8
  78. package/dist/collector-DTahQUiV.js.map +0 -1
  79. package/dist/config-B5kHzuNz.js.map +0 -1
  80. package/dist/core-Dr4u1NVD.js.map +0 -1
  81. package/dist/format-rules-DKOA-6qu.js.map +0 -1
  82. package/dist/keyframes-CV8azJf3.js.map +0 -1
  83. package/dist/ssr/astro.js.map +0 -1
@@ -1,6 +1,8 @@
1
- # Zero Runtime Mode (tastyStatic)
1
+ # Build-Time Extraction (`tastyStatic`)
2
2
 
3
- `tastyStatic` is a build-time utility for generating CSS with zero runtime overhead. It's designed for static sites, no-JS websites, and performance-critical applications where you want to eliminate all runtime styling code. For the broader docs map, see the [Docs Hub](README.md).
3
+ `tastyStatic` is Tasty's framework-agnostic build-time utility for extracting CSS before rendering. It produces no client styling runtime in file mode, but it is not the only way to achieve zero-runtime delivery: server-only `tasty()` also ships no Tasty runtime to the browser while retaining the full React component API. Astro's `tastyIntegration({ islands: false })` is the clearest example; the same architecture applies to server-only Next.js RSC.
4
+
5
+ Choose `tastyStatic` specifically when styles must be generated during the build, when the consumer is not React, or when you want static class-name objects instead of components. For the broader docs map, see the [Docs Hub](README.md).
4
6
 
5
7
  ---
6
8
 
@@ -8,19 +10,19 @@
8
10
 
9
11
  - **Static site generation (SSG)** — Pre-render all styles at build time
10
12
  - **No-JavaScript websites** — CSS works without any JS runtime
11
- - **Performance-critical pages** — Zero runtime overhead for styling
13
+ - **Performance-critical pages** — CSS extracted before the application renders
12
14
  - **Landing pages** — Minimal bundle size with pre-generated CSS
13
15
 
14
16
  ## Requirements
15
17
 
16
- The zero-runtime mode is part of the main `@tenphi/tasty` package but requires additional peer dependencies depending on your setup:
18
+ Build-time extraction is part of the main `@tenphi/tasty` package but requires additional peer dependencies depending on your setup:
17
19
 
18
- | Dependency | Version | Required for |
19
- |---|---|---|
20
- | `@babel/core` | >= 7.24 | Babel plugin (`@tenphi/tasty/babel-plugin`) |
21
- | `@babel/helper-plugin-utils` | >= 7.24 | Babel plugin |
22
- | `@babel/types` | >= 7.24 | Babel plugin |
23
- | `jiti` | >= 2.6 | Next.js wrapper (`@tenphi/tasty/zero/next`) when using `configFile` option |
20
+ | Dependency | Version | Required for |
21
+ | ---------------------------- | ------- | -------------------------------------------------------------------------- |
22
+ | `@babel/core` | >= 7.24 | Babel plugin (`@tenphi/tasty/babel-plugin`) |
23
+ | `@babel/helper-plugin-utils` | >= 7.24 | Babel plugin |
24
+ | `@babel/types` | >= 7.24 | Babel plugin |
25
+ | `jiti` | >= 2.6 | Next.js wrapper (`@tenphi/tasty/zero/next`) when using `configFile` option |
24
26
 
25
27
  All of these are declared as optional peer dependencies of `@tenphi/tasty`. Install only what your setup requires:
26
28
 
@@ -107,11 +109,11 @@ tastyStatic('body', {
107
109
 
108
110
  ## StaticStyle Object
109
111
 
110
- | Property | Type | Description |
111
- |----------|------|-------------|
112
- | `className` | `string` | Space-separated class names for use in JSX |
113
- | `styles` | `Styles` | The original (or merged) styles object |
114
- | `toString()` | `() => string` | Returns `className` for string coercion |
112
+ | Property | Type | Description |
113
+ | ------------ | -------------- | ------------------------------------------ |
114
+ | `className` | `string` | Space-separated class names for use in JSX |
115
+ | `styles` | `Styles` | The original (or merged) styles object |
116
+ | `toString()` | `() => string` | Returns `className` for string coercion |
115
117
 
116
118
  ---
117
119
 
@@ -123,67 +125,73 @@ tastyStatic('body', {
123
125
  // babel.config.js
124
126
  module.exports = {
125
127
  plugins: [
126
- ['@tenphi/tasty/babel-plugin', {
127
- output: 'public/tasty.css',
128
- }]
129
- ]
128
+ [
129
+ '@tenphi/tasty/babel-plugin',
130
+ {
131
+ output: 'public/tasty.css',
132
+ },
133
+ ],
134
+ ],
130
135
  };
131
136
  ```
132
137
 
133
- These examples use `data-schema="dark"` as the root-state convention. If your app already uses a different root attribute such as `data-theme`, keep the same alias pattern and swap the attribute name consistently in your zero-runtime config.
138
+ These examples use `data-schema="dark"` as the root-state convention. If your app already uses a different root attribute such as `data-theme`, keep the same alias pattern and swap the attribute name consistently in your build-time config.
134
139
 
135
140
  ### With Configuration
136
141
 
137
142
  ```javascript
138
143
  module.exports = {
139
144
  plugins: [
140
- ['@tenphi/tasty/babel-plugin', {
141
- output: 'public/tasty.css',
142
- config: {
143
- states: {
144
- '@mobile': '@media(w < 768px)',
145
- '@tablet': '@media(w < 1024px)',
146
- '@dark': '@root(schema=dark)',
145
+ [
146
+ '@tenphi/tasty/babel-plugin',
147
+ {
148
+ output: 'public/tasty.css',
149
+ config: {
150
+ states: {
151
+ '@mobile': '@media(w < 768px)',
152
+ '@tablet': '@media(w < 1024px)',
153
+ '@dark': '@root(schema=dark)',
154
+ },
155
+ devMode: true,
147
156
  },
148
- devMode: true,
149
157
  },
150
- }]
151
- ]
158
+ ],
159
+ ],
152
160
  };
153
161
  ```
154
162
 
155
163
  ### Plugin Options
156
164
 
157
- | Option | Type | Default | Description |
158
- |--------|------|---------|-------------|
159
- | `output` | `string` | `'tasty.css'` | Path for generated CSS file |
160
- | `mode` | `'file' \| 'inject'` | `'file'` | `'file'` writes CSS to disk; `'inject'` embeds CSS inline in JS (see [Inject Mode](#inject-mode)) |
161
- | `configFile` | `string` | — | Absolute path to a TS/JS module that default-exports a `TastyZeroConfig` object. JSON-serializable alternative to `config` — required for Turbopack. |
162
- | `config` | `TastyZeroConfig \| () => TastyZeroConfig` | `{}` | Inline config object or factory function. Takes precedence over `configFile`. |
163
- | `configDeps` | `string[]` | `[]` | Absolute file paths that affect config (for cache invalidation) |
164
- | `injectImport` | `boolean` | `true` | Replace `@tenphi/tasty/static` imports with an import of the generated CSS file. Set to `false` to manage CSS imports manually. |
165
- | `config.states` | `Record<string, string>` | `{}` | Predefined state aliases (e.g. `{ '@mobile': '@media(w < 768px)' }`) |
166
- | `config.devMode` | `boolean` | `false` | Add source comments to CSS |
167
- | `config.tokens` | `ConfigTokens` | — | Design tokens injected as CSS custom properties on `:root`. Values are parsed through the Tasty DSL. Supports state maps for responsive/themed tokens. |
168
- | `config.replaceTokens` | `Record<string, string \| number>` | — | Parse-time token substitution. Keys use `$name` for custom properties and `#name` for color tokens. |
169
- | `config.recipes` | `Record<string, RecipeStyles>` | `{}` | Predefined style recipes |
170
- | `config.keyframes` | `Record<string, KeyframesSteps>` | — | Global `@keyframes` definitions available to all `tastyStatic` calls |
171
- | `config.fontFaces` | `Record<string, FontFaceInput>` | — | Global `@font-face` definitions |
172
- | `config.counterStyles` | `Record<string, CounterStyleDescriptors>` | — | Global `@counter-style` definitions |
173
- | `config.units` | `Record<string, string \| UnitHandler>` | — | Custom units for the style parser (merged with built-ins). E.g. `{ em: 'em', vw: 'vw' }` |
174
- | `config.functions` | `Record<string, FunctionDefinition \| Function>` | — | Custom functions (merged). Bare keys → parse functions; `$$name` keys → declarative CSS `@function` definitions |
175
- | `config.polyfills` | `{ functions?: boolean }` | `{}` | Opt-in polyfills. `functions: true` inlines `@function` calls into plain CSS at build time |
176
- | `config.plugins` | `TastyPlugin[]` | — | Plugins that extend tasty with custom functions, units, states, and handlers |
177
- | `config.handlers` | `Record<string, StyleHandlerDefinition>` | — | Custom style handlers that transform style properties into CSS declarations |
178
- | `config.presets` | `Record<string, TypographyPreset>` | — | Typography presets — shorthand for `generateTypographyTokens()`. Generated tokens merge under explicit `tokens`. |
179
- | `config.globalStyles` | `Record<string, Styles>` | — | Global Tasty styles keyed by CSS selector. Supports the full style syntax. |
180
- | `config.autoPropertyTypes` | `boolean` | `true` | Automatically infer and register CSS `@property` declarations from values |
181
- | `config.parserCacheSize` | `number` | `1000` | Parser LRU cache size. Larger values improve performance for builds with many unique style values |
182
- | `config.namePrefix` | `string` | `'ts'` | Prefix prepended to every generated identifier. Defaults to `'ts'` so static classes never collide with runtime (`'t'`) classes. See [Configuration: Name prefix](configuration.md#name-prefix). |
183
-
184
- ### Coexisting with runtime tasty
185
-
186
- When a page mixes `tastyStatic` with runtime `tasty`, both must use **different** `namePrefix` values. The defaults handle this automatically (`'t'` for runtime, `'ts'` for zero-runtime). If you customize one, customize the other:
165
+ | Option | Type | Default | Description |
166
+ | -------------------------- | ------------------------------------------------ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
167
+ | `output` | `string` | `'tasty.css'` | Path for generated CSS file |
168
+ | `mode` | `'file' \| 'inject'` | `'file'` | `'file'` writes CSS to disk; `'inject'` embeds CSS inline in JS (see [Inject Mode](#inject-mode)) |
169
+ | `configFile` | `string` | — | Absolute path to a TS/JS module that default-exports a `TastyZeroConfig` object. JSON-serializable alternative to `config` — required for Turbopack. |
170
+ | `config` | `TastyZeroConfig \| () => TastyZeroConfig` | `{}` | Inline config object or factory function. Takes precedence over `configFile`. |
171
+ | `configDeps` | `string[]` | `[]` | Absolute file paths that affect config (for cache invalidation) |
172
+ | `injectImport` | `boolean` | `true` | Replace `@tenphi/tasty/static` imports with an import of the generated CSS file. Set to `false` to manage CSS imports manually. |
173
+ | `config.states` | `Record<string, string>` | `{}` | Predefined state aliases (e.g. `{ '@mobile': '@media(w < 768px)' }`) |
174
+ | `config.devMode` | `boolean` | `false` | Add source comments to CSS |
175
+ | `config.tokens` | `ConfigTokens` | — | Design tokens injected as CSS custom properties on `:root`. Values are parsed through the Tasty DSL. Supports state maps for responsive/themed tokens. |
176
+ | `config.replaceTokens` | `Record<string, string \| number>` | — | Parse-time token substitution. Keys use `$name` for custom properties and `#name` for color tokens. |
177
+ | `config.recipes` | `Record<string, RecipeStyles>` | `{}` | Predefined style recipes |
178
+ | `config.keyframes` | `Record<string, KeyframesSteps>` | — | Global `@keyframes` definitions available to all `tastyStatic` calls |
179
+ | `config.fontFaces` | `Record<string, FontFaceInput>` | — | Global `@font-face` definitions |
180
+ | `config.counterStyles` | `Record<string, CounterStyleDescriptors>` | — | Global `@counter-style` definitions |
181
+ | `config.units` | `Record<string, string \| UnitHandler>` | — | Custom units for the style parser (merged with built-ins). E.g. `{ em: 'em', vw: 'vw' }` |
182
+ | `config.functions` | `Record<string, FunctionDefinition \| Function>` | — | Custom functions (merged). Bare keys → parse functions; `$$name` keys → declarative CSS `@function` definitions |
183
+ | `config.polyfills` | `{ functions?: boolean }` | `{}` | Opt-in polyfills. `functions: true` inlines `@function` calls into plain CSS at build time |
184
+ | `config.plugins` | `TastyPlugin[]` | — | Plugins that extend tasty with custom functions, units, states, and handlers |
185
+ | `config.handlers` | `Record<string, StyleHandlerDefinition>` | — | Custom style handlers that transform style properties into CSS declarations |
186
+ | `config.presets` | `Record<string, TypographyPreset>` | — | Typography presets — shorthand for `generateTypographyTokens()`. Generated tokens merge under explicit `tokens`. |
187
+ | `config.globalStyles` | `Record<string, Styles>` | — | Global Tasty styles keyed by CSS selector. Supports the full style syntax. |
188
+ | `config.autoPropertyTypes` | `boolean` | `true` | Automatically infer and register CSS `@property` declarations from values |
189
+ | `config.parserCacheSize` | `number` | `1000` | Parser LRU cache size. Larger values improve performance for builds with many unique style values |
190
+ | `config.namePrefix` | `string` | `'ts'` | Prefix prepended to every generated identifier. Defaults to `'ts'` so static classes never collide with runtime (`'t'`) classes. See [Configuration: Name prefix](configuration.md#name-prefix). |
191
+
192
+ ### Coexisting with `tasty()`
193
+
194
+ When a page mixes `tastyStatic` with `tasty`, both must use **different** `namePrefix` values. The defaults handle this automatically (`'t'` for React rendering, `'ts'` for build-time extraction). If you customize one, customize the other:
187
195
 
188
196
  ```ts
189
197
  // tasty-zero.config.ts (Babel plugin)
@@ -191,7 +199,7 @@ export default { namePrefix: 'mbs' };
191
199
  ```
192
200
 
193
201
  ```ts
194
- // app entry (runtime configure)
202
+ // app entry (`tasty()` configure)
195
203
  configure({ namePrefix: 'mb' });
196
204
  ```
197
205
 
@@ -205,16 +213,24 @@ Recipes work with `tastyStatic` the same way as with runtime `tasty`:
205
213
  // babel.config.js
206
214
  module.exports = {
207
215
  plugins: [
208
- ['@tenphi/tasty/babel-plugin', {
209
- output: 'public/tasty.css',
210
- config: {
211
- recipes: {
212
- card: { padding: '4x', fill: '#surface', radius: '1r', border: true },
213
- elevated: { shadow: '2x 2x 4x #shadow' },
216
+ [
217
+ '@tenphi/tasty/babel-plugin',
218
+ {
219
+ output: 'public/tasty.css',
220
+ config: {
221
+ recipes: {
222
+ card: {
223
+ padding: '4x',
224
+ fill: '#surface',
225
+ radius: '1r',
226
+ border: true,
227
+ },
228
+ elevated: { shadow: '2x 2x 4x #shadow' },
229
+ },
214
230
  },
215
231
  },
216
- }]
217
- ]
232
+ ],
233
+ ],
218
234
  };
219
235
  ```
220
236
 
@@ -226,7 +242,7 @@ const card = tastyStatic({
226
242
  color: '#text',
227
243
  });
228
244
 
229
- <div className={card}>Styled card</div>
245
+ <div className={card}>Styled card</div>;
230
246
  ```
231
247
 
232
248
  ---
@@ -250,14 +266,14 @@ export default withTastyZero({
250
266
 
251
267
  ### `withTastyZero` Options
252
268
 
253
- | Option | Type | Default | Description |
254
- |--------|------|---------|-------------|
255
- | `output` | `string` | `'public/tasty.css'` | Output path for CSS relative to project root |
256
- | `mode` | `'file' \| 'inject'` | `'file'` | `'file'` writes CSS to disk; `'inject'` embeds CSS inline in JS |
257
- | `enabled` | `boolean` | `true` | Enable/disable the plugin |
258
- | `configFile` | `string` | — | Path to a TS/JS module that default-exports `TastyZeroConfig`. Recommended for Turbopack compatibility. |
259
- | `config` | `TastyZeroConfig` | — | Inline config object. For static configs that don't change during dev. |
260
- | `configDeps` | `string[]` | `[]` | Extra files the config depends on (for cache invalidation) |
269
+ | Option | Type | Default | Description |
270
+ | ------------ | -------------------- | -------------------- | ------------------------------------------------------------------------------------------------------- |
271
+ | `output` | `string` | `'public/tasty.css'` | Output path for CSS relative to project root |
272
+ | `mode` | `'file' \| 'inject'` | `'file'` | `'file'` writes CSS to disk; `'inject'` embeds CSS inline in JS |
273
+ | `enabled` | `boolean` | `true` | Enable/disable the plugin |
274
+ | `configFile` | `string` | — | Path to a TS/JS module that default-exports `TastyZeroConfig`. Recommended for Turbopack compatibility. |
275
+ | `config` | `TastyZeroConfig` | — | Inline config object. For static configs that don't change during dev. |
276
+ | `configDeps` | `string[]` | `[]` | Extra files the config depends on (for cache invalidation) |
261
277
 
262
278
  ### Turbopack Support
263
279
 
@@ -295,12 +311,15 @@ export default defineConfig({
295
311
  react({
296
312
  babel: {
297
313
  plugins: [
298
- ['@tenphi/tasty/babel-plugin', {
299
- output: 'public/tasty.css',
300
- config: {
301
- states: { '@mobile': '@media(w < 768px)' },
314
+ [
315
+ '@tenphi/tasty/babel-plugin',
316
+ {
317
+ output: 'public/tasty.css',
318
+ config: {
319
+ states: { '@mobile': '@media(w < 768px)' },
320
+ },
302
321
  },
303
- }],
322
+ ],
304
323
  ],
305
324
  },
306
325
  }),
@@ -334,7 +353,9 @@ export const Button = () => <button className={button}>Click</button>;
334
353
  const button = {
335
354
  className: 'ts3f2a1b ts8c4d2e',
336
355
  styles: { padding: '2x 4x', fill: '#purple', color: '#white' },
337
- toString() { return this.className; }
356
+ toString() {
357
+ return this.className;
358
+ },
338
359
  };
339
360
 
340
361
  export const Button = () => <button className={button}>Click</button>;
@@ -382,13 +403,16 @@ This is ideal for **reusable components**, **extensions**, and **libraries** whe
382
403
  // babel.config.js
383
404
  module.exports = {
384
405
  plugins: [
385
- ['@tenphi/tasty/babel-plugin', {
386
- mode: 'inject',
387
- config: {
388
- states: { '@mobile': '@media(w < 768px)' },
406
+ [
407
+ '@tenphi/tasty/babel-plugin',
408
+ {
409
+ mode: 'inject',
410
+ config: {
411
+ states: { '@mobile': '@media(w < 768px)' },
412
+ },
389
413
  },
390
- }]
391
- ]
414
+ ],
415
+ ],
392
416
  };
393
417
  ```
394
418
 
@@ -428,13 +452,20 @@ tastyStatic('.heading', { preset: 'h1' });
428
452
  ```tsx
429
453
  import { injectCSS as _$i } from '@tenphi/tasty/static/inject';
430
454
 
431
- const button = (_$i("ts3f2a1b ts8c4d2e", ".ts3f2a1b.ts3f2a1b{padding:16px 32px}\n.ts8c4d2e.ts8c4d2e{background:#9370db}"), {
432
- className: 'ts3f2a1b ts8c4d2e',
433
- styles: { padding: '2x 4x', fill: '#purple' },
434
- toString() { return this.className; }
435
- });
436
-
437
- _$i(".heading", ".heading{font-size:2.5rem;font-weight:700;line-height:1.2}");
455
+ const button =
456
+ (_$i(
457
+ 'ts3f2a1b ts8c4d2e',
458
+ '.ts3f2a1b.ts3f2a1b{padding:16px 32px}\n.ts8c4d2e.ts8c4d2e{background:#9370db}',
459
+ ),
460
+ {
461
+ className: 'ts3f2a1b ts8c4d2e',
462
+ styles: { padding: '2x 4x', fill: '#purple' },
463
+ toString() {
464
+ return this.className;
465
+ },
466
+ });
467
+
468
+ _$i('.heading', '.heading{font-size:2.5rem;font-weight:700;line-height:1.2}');
438
469
  ```
439
470
 
440
471
  ### Dev Mode / HMR
@@ -443,7 +474,7 @@ Class names are content-hashed (`ts` + MD5). When styles change, a new hash prod
443
474
 
444
475
  ### Limitations (inject mode)
445
476
 
446
- - **Client-side only** — Styles are injected via the DOM, so they are not available during SSR. For server-rendered apps, use `mode: 'file'` or the runtime `tasty()`.
477
+ - **Client-side only** — Styles are injected via the DOM, so they are not available during SSR. For server-rendered apps, use `mode: 'file'` or React `tasty()`.
447
478
  - **Larger JS bundle** — CSS is embedded in JavaScript, increasing bundle size. Best suited for components and extensions, not full-app styling.
448
479
 
449
480
  ---
@@ -512,13 +543,15 @@ For dynamic styling needs, combine with regular CSS or CSS variables:
512
543
  ```tsx
513
544
  const card = tastyStatic({
514
545
  padding: '4x',
515
- fill: 'var(--card-bg, #white)',
546
+ fill: '($card-bg, #white)',
516
547
  });
517
548
 
518
549
  <div
519
550
  className={card}
520
- style={{ '--card-bg': isActive ? '#purple' : '#white' }}
521
- />
551
+ style={{
552
+ '--card-bg': isActive ? 'var(--purple-color)' : 'var(--white-color)',
553
+ }}
554
+ />;
522
555
  ```
523
556
 
524
557
  ---
@@ -535,15 +568,15 @@ const card = tastyStatic({
535
568
  ## Common Issues
536
569
 
537
570
  - No CSS file is generated: make sure the Babel plugin actually runs for files importing `@tenphi/tasty/static`, and verify the `output` path is writable.
538
- - Styles stay dynamic by mistake: `tastyStatic()` only supports build-time-known values. Move runtime values to CSS variables or switch that component to runtime `tasty()`.
571
+ - Styles stay dynamic by mistake: `tastyStatic()` only supports build-time-known values. Move dynamic values to CSS variables or switch that component to React `tasty()`.
539
572
  - Turbopack config behaves inconsistently: prefer `configFile` over inline functions so the setup stays JSON-serializable.
540
573
 
541
574
  ---
542
575
 
543
576
  ## Related
544
577
 
545
- - [Docs Hub](README.md) — Choose the right guide by task or rendering mode
578
+ - [Docs Hub](README.md) — Choose the right guide by task or CSS delivery model
546
579
  - [Style DSL](dsl.md) — State maps, tokens, units, extending semantics (shared by runtime and static)
547
- - [React API](react-api.md) — Runtime styling: `tasty()` factory, component props, variants, sub-elements, style functions
580
+ - [React API](react-api.md) — Client or server rendering with `tasty()`: component props, variants, sub-elements, and style functions
548
581
  - [Configuration](configuration.md) — Global configuration: tokens, recipes, custom units, and style handlers
549
582
  - [Plugins & Extension Points](plugins.md) — Which extension points work at build time (`functions`, `units`, `states`, `handlers`, `recipes`) and which do not (`propHandlers` — `tastyStatic()` takes styles objects, not props)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenphi/tasty",
3
- "version": "3.4.0",
3
+ "version": "3.5.0",
4
4
  "description": "A design-system-integrated styling system and DSL for concise, state-aware UI styling",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -72,6 +72,16 @@
72
72
  "import": "./dist/ssr/astro-middleware-static.js",
73
73
  "default": "./dist/ssr/astro-middleware-static.js"
74
74
  },
75
+ "./ssr/astro-middleware-extract": {
76
+ "types": "./dist/ssr/astro-middleware-extract.d.ts",
77
+ "import": "./dist/ssr/astro-middleware-extract.js",
78
+ "default": "./dist/ssr/astro-middleware-extract.js"
79
+ },
80
+ "./ssr/astro-middleware-extract-static": {
81
+ "types": "./dist/ssr/astro-middleware-extract-static.d.ts",
82
+ "import": "./dist/ssr/astro-middleware-extract-static.js",
83
+ "default": "./dist/ssr/astro-middleware-extract-static.js"
84
+ },
75
85
  "./tasty.config": "./tasty.config.ts"
76
86
  },
77
87
  "files": [
@@ -85,7 +95,9 @@
85
95
  "./dist/ssr/astro.js",
86
96
  "./dist/ssr/astro-client.js",
87
97
  "./dist/ssr/astro-middleware.js",
88
- "./dist/ssr/astro-middleware-static.js"
98
+ "./dist/ssr/astro-middleware-static.js",
99
+ "./dist/ssr/astro-middleware-extract.js",
100
+ "./dist/ssr/astro-middleware-extract-static.js"
89
101
  ],
90
102
  "engines": {
91
103
  "node": ">=20"
@@ -143,6 +155,7 @@
143
155
  "csstype": "^3.1.0"
144
156
  },
145
157
  "devDependencies": {
158
+ "@astrojs/react": "^4.4.2",
146
159
  "@babel/core": "^7.24.0",
147
160
  "@babel/helper-plugin-utils": "^7.24.0",
148
161
  "@babel/types": "^7.24.0",
@@ -151,22 +164,25 @@
151
164
  "@eslint/js": "^10.0.1",
152
165
  "@size-limit/esbuild": "^12.0.0",
153
166
  "@size-limit/file": "^12.0.0",
154
- "@tenphi/eslint-plugin-tasty": "^0.11.3",
167
+ "@tenphi/eslint-plugin-tasty": "^1.0.5",
155
168
  "@testing-library/jest-dom": "^6.9.1",
156
169
  "@testing-library/react": "^16.3.2",
157
170
  "@types/babel__core": "^7.20.5",
158
171
  "@types/babel__helper-plugin-utils": "^7.10.3",
159
- "@types/node": "^25.3.2",
172
+ "@types/node": "^24.13.3",
160
173
  "@types/react": "^19.0.0",
174
+ "@types/react-dom": "^19.0.0",
161
175
  "@vitest/browser-playwright": "^4.1.10",
176
+ "astro": "^5.18.2",
162
177
  "eslint": "^10.0.0",
163
178
  "eslint-config-prettier": "^10.1.8",
164
179
  "jiti": "^2.6.1",
165
180
  "knip": "^5.61.3",
166
181
  "next": "^16.0.0",
167
182
  "playwright": "^1.62.1",
168
- "prettier": "^3.8.1",
183
+ "prettier": "^3.9.6",
169
184
  "react": "^19.0.0",
185
+ "react-dom": "^19.0.0",
170
186
  "size-limit": "^12.0.0",
171
187
  "tsdown": "^0.22.0",
172
188
  "typescript": "^6.0.2",
@@ -178,13 +194,13 @@
178
194
  "name": "main (import *)",
179
195
  "path": "dist/index.js",
180
196
  "import": "*",
181
- "limit": "56.6 kB"
197
+ "limit": "57.5 kB"
182
198
  },
183
199
  {
184
200
  "name": "core (import *)",
185
201
  "path": "dist/core/index.js",
186
202
  "import": "*",
187
- "limit": "53.65 kB"
203
+ "limit": "54.5 kB"
188
204
  },
189
205
  {
190
206
  "name": "static",
@@ -212,7 +228,7 @@
212
228
  "path",
213
229
  "crypto"
214
230
  ],
215
- "limit": "49.35 kB"
231
+ "limit": "49.6 kB"
216
232
  }
217
233
  ],
218
234
  "scripts": {
@@ -231,6 +247,8 @@
231
247
  "size": "size-limit",
232
248
  "bench": "vitest bench --project node",
233
249
  "bench:browser": "vitest bench --project browser",
250
+ "bench:overhead": "vitest bench --config vitest.production-browser-bench.config.ts src/tasty-overhead.bench.tsx",
251
+ "bench:injection": "vitest bench --config vitest.production-browser-bench.config.ts src/tasty-injection.bench.ts",
234
252
  "changeset": "changeset",
235
253
  "version": "changeset version",
236
254
  "release": "changeset publish",
@@ -1 +0,0 @@
1
- {"version":3,"file":"collector-DTahQUiV.js","names":[],"sources":["../src/ssr/collector.ts"],"sourcesContent":["/**\n * ServerStyleCollector — server-safe style collector for SSR.\n *\n * Accumulates CSS rules and cache metadata during server rendering.\n * This is the server-side counterpart to StyleInjector: it allocates\n * hash-based class names using the configured `namePrefix` (defaults\n * to `'t'`), formats CSS rules into text, and tracks rendered class\n * names for lightweight client transfer.\n *\n * One instance is created per HTTP request. Concurrent requests\n * each get their own collector (via AsyncLocalStorage or React context).\n */\n\nimport {\n getEffectiveProperties,\n getGlobalStyles,\n getGlobalCounterStyles,\n getGlobalFontFaces,\n getGlobalFunctions,\n getGlobalConfigTokens,\n getNamePrefix,\n} from '../config';\nimport { formatCounterStyleRule } from '../counter-style';\nimport { fontFaceContentHash, formatFontFaceRule } from '../font-face';\nimport { formatFunctionRule, parseFunctionName } from '../functions';\nimport { renderStyles } from '../pipeline';\nimport type { StyleResult } from '../pipeline';\nimport { hashString } from '../utils/hash';\nimport {\n makeClassName,\n makeCounterStyleName,\n makeKeyframeName,\n validateNamePrefix,\n} from '../utils/name-prefix';\nimport { formatPropertyCSS } from './format-property';\nimport { formatGlobalRules } from './format-global-rules';\nimport { formatRules } from './format-rules';\n\nexport class ServerStyleCollector {\n private chunks = new Map<string, string>();\n private cacheKeyToClassName = new Map<string, string>();\n private flushedKeys = new Set<string>();\n private propertyRules = new Map<string, string>();\n private flushedPropertyKeys = new Set<string>();\n private keyframeRules = new Map<string, string>();\n private flushedKeyframeKeys = new Set<string>();\n private globalStyles = new Map<string, string>();\n private flushedGlobalKeys = new Set<string>();\n private rawCSS = new Map<string, string>();\n private flushedRawKeys = new Set<string>();\n private fontFaceRules = new Map<string, string>();\n private flushedFontFaceKeys = new Set<string>();\n private counterStyleRules = new Map<string, string>();\n private flushedCounterStyleKeys = new Set<string>();\n private functionRules = new Map<string, string>();\n private flushedFunctionKeys = new Set<string>();\n private keyframesCounter = 0;\n private counterStyleCounter = 0;\n private internalsCollected = false;\n private namePrefix: string;\n\n /**\n * @param namePrefix - Optional override for the configured prefix.\n * Defaults to the value from `configure({ namePrefix })` (or `'t'`).\n * Pass an explicit prefix when constructing a collector outside the\n * normal configure() lifecycle (e.g. in tests). Validated eagerly\n * so misconfiguration fails before any CSS is collected.\n */\n constructor(namePrefix?: string) {\n if (namePrefix !== undefined) {\n validateNamePrefix(namePrefix);\n }\n this.namePrefix = namePrefix ?? getNamePrefix();\n }\n\n private generateClassName(cacheKey: string): string {\n return makeClassName(this.namePrefix, hashString(cacheKey));\n }\n\n /**\n * Collect internal @property rules and :root token defaults.\n * Mirrors markStylesGenerated() from the client-side injector.\n * Called automatically on first chunk collection; idempotent.\n *\n * Internals are always emitted here — the RSC path deliberately\n * defers to SSR so that tokens appear exactly once per page in\n * <style data-tasty-ssr> (avoiding duplication of large token sets).\n */\n collectInternals(): void {\n if (this.internalsCollected) return;\n this.internalsCollected = true;\n\n for (const [token, definition] of Object.entries(\n getEffectiveProperties(),\n )) {\n const css = formatPropertyCSS(token, definition);\n if (css) {\n this.collectProperty(`__prop:${token}`, css);\n }\n }\n\n const tokenStyles = getGlobalConfigTokens();\n if (tokenStyles && Object.keys(tokenStyles).length > 0) {\n const tokenRules = renderStyles(tokenStyles, ':root') as StyleResult[];\n if (tokenRules.length > 0) {\n const css = formatGlobalRules(tokenRules);\n if (css) {\n this.collectGlobalStyles('__global:tokens', css);\n }\n }\n }\n\n const globalFF = getGlobalFontFaces();\n if (globalFF) {\n for (const [family, input] of Object.entries(globalFF)) {\n const descriptors = Array.isArray(input) ? input : [input];\n for (const desc of descriptors) {\n const hash = fontFaceContentHash(family, desc);\n const css = formatFontFaceRule(family, desc);\n this.collectFontFace(hash, css);\n }\n }\n }\n\n const globalCS = getGlobalCounterStyles();\n if (globalCS) {\n for (const [name, descriptors] of Object.entries(globalCS)) {\n const css = formatCounterStyleRule(name, descriptors);\n this.collectCounterStyle(name, css, { weak: true });\n }\n }\n\n const globalFn = getGlobalFunctions();\n if (globalFn) {\n for (const [name, definition] of Object.entries(globalFn)) {\n const css = formatFunctionRule(name, definition);\n this.collectFunction(parseFunctionName(name), css, { weak: true });\n }\n }\n\n const globalStyles = getGlobalStyles();\n if (globalStyles) {\n for (const [selector, styles] of Object.entries(globalStyles)) {\n if (Object.keys(styles).length > 0) {\n const rules = renderStyles(styles, selector) as StyleResult[];\n if (rules.length > 0) {\n const css = formatGlobalRules(rules);\n if (css) {\n this.collectGlobalStyles(`__global:styles:${selector}`, css);\n }\n }\n }\n }\n }\n }\n\n /**\n * Allocate a className for a cache key, server-side.\n * Mirrors StyleInjector.allocateClassName but without DOM access.\n */\n allocateClassName(cacheKey: string): {\n className: string;\n isNewAllocation: boolean;\n } {\n const existing = this.cacheKeyToClassName.get(cacheKey);\n if (existing) {\n return { className: existing, isNewAllocation: false };\n }\n\n const className = this.generateClassName(cacheKey);\n this.cacheKeyToClassName.set(cacheKey, className);\n\n return { className, isNewAllocation: true };\n }\n\n /**\n * Record CSS rules for a chunk.\n * Called by useStyles during server render.\n */\n collectChunk(\n cacheKey: string,\n className: string,\n rules: StyleResult[],\n ): void {\n if (this.chunks.has(cacheKey)) return;\n const css = formatRules(rules, className);\n if (css) {\n this.chunks.set(cacheKey, css);\n }\n }\n\n /**\n * Record a @property rule. Deduplicated by name.\n */\n collectProperty(name: string, css: string): void {\n if (!this.propertyRules.has(name)) {\n this.propertyRules.set(name, css);\n }\n }\n\n /**\n * Record a @keyframes rule. Deduplicated by name.\n */\n collectKeyframes(name: string, css: string): void {\n if (!this.keyframeRules.has(name)) {\n this.keyframeRules.set(name, css);\n }\n }\n\n /**\n * Allocate a keyframe name for SSR. Uses provided name or generates one.\n */\n allocateKeyframeName(providedName?: string): string {\n return (\n providedName ??\n makeKeyframeName(this.namePrefix, String(this.keyframesCounter++))\n );\n }\n\n /**\n * Record a @font-face rule. Deduplicated by key (content hash).\n */\n collectFontFace(key: string, css: string): void {\n if (!this.fontFaceRules.has(key)) {\n this.fontFaceRules.set(key, css);\n }\n }\n\n /**\n * Record a @counter-style rule. Deduplicated by name and overrides an\n * existing rule by default. Pass `weak: true` for global `configure()`\n * definitions, which never clobber an existing rule.\n */\n collectCounterStyle(\n name: string,\n css: string,\n options?: { weak?: boolean },\n ): void {\n const existing = this.counterStyleRules.get(name);\n if (existing === undefined) {\n this.counterStyleRules.set(name, css);\n return;\n }\n if (options?.weak || existing === css) return;\n this.counterStyleRules.set(name, css);\n // If a rule with this name was already flushed (streaming), allow the\n // overriding rule to be flushed again so it wins by source order.\n this.flushedCounterStyleKeys.delete(name);\n }\n\n /**\n * Record a @function rule. Deduplicated by CSS function name and overrides an\n * existing rule by default. Pass `weak: true` for global `configure()`\n * definitions, which never clobber an existing rule.\n */\n collectFunction(\n name: string,\n css: string,\n options?: { weak?: boolean },\n ): void {\n const existing = this.functionRules.get(name);\n if (existing === undefined) {\n this.functionRules.set(name, css);\n return;\n }\n if (options?.weak || existing === css) return;\n this.functionRules.set(name, css);\n // If a rule with this name was already flushed (streaming), allow the\n // overriding rule to be flushed again so it wins by source order.\n this.flushedFunctionKeys.delete(name);\n }\n\n /**\n * Allocate a counter-style name for SSR. Uses provided name or generates one.\n */\n allocateCounterStyleName(providedName?: string): string {\n return (\n providedName ??\n makeCounterStyleName(this.namePrefix, String(this.counterStyleCounter++))\n );\n }\n\n /**\n * Record global styles (from useGlobalStyles). Deduplicated by key.\n *\n * Pass `replace` for slot-keyed entries (an explicit `id`), where the last\n * write must win to match the client's update-tracking behavior.\n */\n collectGlobalStyles(key: string, css: string, replace?: boolean): void {\n if (replace || !this.globalStyles.has(key)) {\n this.globalStyles.set(key, css);\n }\n }\n\n /**\n * Record raw CSS text (from useRawCSS). Deduplicated by key.\n *\n * Pass `replace` for slot-keyed entries (an explicit `id`), where the last\n * write must win to match the client's update-tracking behavior.\n */\n collectRawCSS(key: string, css: string, replace?: boolean): void {\n if (replace || !this.rawCSS.has(key)) {\n this.rawCSS.set(key, css);\n }\n }\n\n /**\n * Extract all CSS collected so far as a single string.\n * Includes @property and @keyframes rules.\n * Used for non-streaming SSR (renderToString).\n */\n getCSS(): string {\n const parts: string[] = [];\n\n for (const css of this.propertyRules.values()) {\n parts.push(css);\n }\n\n for (const css of this.fontFaceRules.values()) {\n parts.push(css);\n }\n\n for (const css of this.counterStyleRules.values()) {\n parts.push(css);\n }\n\n for (const css of this.functionRules.values()) {\n parts.push(css);\n }\n\n for (const css of this.rawCSS.values()) {\n parts.push(css);\n }\n\n for (const css of this.globalStyles.values()) {\n parts.push(css);\n }\n\n for (const css of this.chunks.values()) {\n parts.push(css);\n }\n\n for (const css of this.keyframeRules.values()) {\n parts.push(css);\n }\n\n return parts.join('\\n');\n }\n\n /**\n * Flush only newly collected CSS since the last flush.\n * Used for streaming SSR (renderToPipeableStream + useServerInsertedHTML).\n */\n flushCSS(): string {\n const parts: string[] = [];\n\n for (const [name, css] of this.propertyRules) {\n if (!this.flushedPropertyKeys.has(name)) {\n parts.push(css);\n this.flushedPropertyKeys.add(name);\n }\n }\n\n for (const [key, css] of this.fontFaceRules) {\n if (!this.flushedFontFaceKeys.has(key)) {\n parts.push(css);\n this.flushedFontFaceKeys.add(key);\n }\n }\n\n for (const [key, css] of this.counterStyleRules) {\n if (!this.flushedCounterStyleKeys.has(key)) {\n parts.push(css);\n this.flushedCounterStyleKeys.add(key);\n }\n }\n\n for (const [key, css] of this.functionRules) {\n if (!this.flushedFunctionKeys.has(key)) {\n parts.push(css);\n this.flushedFunctionKeys.add(key);\n }\n }\n\n for (const [key, css] of this.rawCSS) {\n if (!this.flushedRawKeys.has(key)) {\n parts.push(css);\n this.flushedRawKeys.add(key);\n }\n }\n\n for (const [key, css] of this.globalStyles) {\n if (!this.flushedGlobalKeys.has(key)) {\n parts.push(css);\n this.flushedGlobalKeys.add(key);\n }\n }\n\n for (const [key, css] of this.chunks) {\n if (!this.flushedKeys.has(key)) {\n parts.push(css);\n this.flushedKeys.add(key);\n }\n }\n\n for (const [name, css] of this.keyframeRules) {\n if (!this.flushedKeyframeKeys.has(name)) {\n parts.push(css);\n this.flushedKeyframeKeys.add(name);\n }\n }\n\n return parts.join('\\n');\n }\n\n private flushedClassNames = new Set<string>();\n\n /**\n * Return class names rendered since the last call (for streaming).\n * Used to emit lightweight class-list scripts for client hydration.\n */\n getRenderedClassNames(): string[] {\n const names: string[] = [];\n for (const className of this.cacheKeyToClassName.values()) {\n if (!this.flushedClassNames.has(className)) {\n this.flushedClassNames.add(className);\n names.push(className);\n }\n }\n return names;\n }\n}\n\n/**\n * Factory for creating a {@link ServerStyleCollector} instance.\n *\n * Canonical functional entry point; the `ServerStyleCollector` class remains\n * exported for advanced/internal use.\n *\n * @param namePrefix - Optional override for the configured class-name prefix.\n * Defaults to the value from `configure({ namePrefix })` (or `'t'`).\n *\n * @example\n * ```ts\n * import { createServerStyleCollector } from '@tenphi/tasty/ssr';\n *\n * const collector = createServerStyleCollector();\n * ```\n */\nexport function createServerStyleCollector(\n namePrefix?: string,\n): ServerStyleCollector {\n return new ServerStyleCollector(namePrefix);\n}\n"],"mappings":"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"}