@tenphi/tasty 0.0.0-snapshot.06c8a41 → 0.0.0-snapshot.07ea4a0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +408 -385
- package/dist/babel-types-BKDTT4--.d.ts +18 -0
- package/dist/chunks/async-storage-f9j9JRtq.js +37 -0
- package/dist/chunks/async-storage-f9j9JRtq.js.map +1 -0
- package/dist/chunks/build-config-CW4k04_V.js +60 -0
- package/dist/chunks/build-config-CW4k04_V.js.map +1 -0
- package/dist/{collector-B3OsM252.js → chunks/collector-DKWQXEwN.js} +10 -127
- package/dist/chunks/collector-DKWQXEwN.js.map +1 -0
- package/dist/chunks/config-engine-DTglOA78.js +272 -0
- package/dist/chunks/config-engine-DTglOA78.js.map +1 -0
- package/dist/chunks/config-file-DlZXJMj8.js +25 -0
- package/dist/chunks/config-file-DlZXJMj8.js.map +1 -0
- package/dist/chunks/css-definitions-_C7C-daI.js +910 -0
- package/dist/chunks/css-definitions-_C7C-daI.js.map +1 -0
- package/dist/{css-resources-Cyl_axbI.js → chunks/css-resources-P7IBUUZs.js} +2 -9
- package/dist/chunks/css-resources-P7IBUUZs.js.map +1 -0
- package/dist/chunks/debug-BapJwy9w.js +502 -0
- package/dist/chunks/debug-BapJwy9w.js.map +1 -0
- package/dist/chunks/dsl-DYSJc1-T.js +1383 -0
- package/dist/chunks/dsl-DYSJc1-T.js.map +1 -0
- package/dist/chunks/hydration-TRcz5vnk.js +41 -0
- package/dist/chunks/hydration-TRcz5vnk.js.map +1 -0
- package/dist/chunks/react-runtime-DayjnPuC.js +689 -0
- package/dist/chunks/react-runtime-DayjnPuC.js.map +1 -0
- package/dist/chunks/runtime-engine-IwGbSMb4.js +2432 -0
- package/dist/chunks/runtime-engine-IwGbSMb4.js.map +1 -0
- package/dist/chunks/runtime-state-CVrL92zX.js +152 -0
- package/dist/chunks/runtime-state-CVrL92zX.js.map +1 -0
- package/dist/{merge-styles-DuoZEsm9.js → chunks/shared-utils-y-HjvPN4.js} +60 -55
- package/dist/chunks/shared-utils-y-HjvPN4.js.map +1 -0
- package/dist/chunks/style-engine-CVmNIjow.js +5521 -0
- package/dist/chunks/style-engine-CVmNIjow.js.map +1 -0
- package/dist/{css-writer-Bgowobbx.js → chunks/zero-engine-igHhzYM6.js} +65 -145
- package/dist/chunks/zero-engine-igHhzYM6.js.map +1 -0
- package/dist/{collector-BTVXj2hS.d.ts → collector-CTU-nl0g.d.ts} +6 -5
- package/dist/{config-LfIDmVHx.d.ts → config-CcxqSZI2.d.ts} +45 -40
- package/dist/core/index.d.ts +5 -5
- package/dist/core/index.js +9 -6
- package/dist/hydrate-CRyjjAyl.d.ts +22 -0
- package/dist/{index-Bm9ltvdH.d.ts → index--GdqCPlp.d.ts} +271 -242
- package/dist/index.d.ts +5 -5
- package/dist/index.js +11 -922
- package/dist/{merge-styles-BcLl1u_d.d.ts → merge-styles-DSkVc6CD.d.ts} +2 -3
- package/dist/ssr/astro-client.d.ts +1 -1
- package/dist/ssr/astro-client.js +1 -14
- package/dist/ssr/astro-client.js.map +1 -1
- package/dist/ssr/astro-middleware-extract-static.d.ts +1 -2
- package/dist/ssr/astro-middleware-extract-static.js +1 -1
- package/dist/ssr/astro-middleware-extract.d.ts +1 -2
- package/dist/ssr/astro-middleware-extract.js +1 -1
- package/dist/ssr/astro-middleware-static.d.ts +1 -2
- package/dist/ssr/astro-middleware-static.js +1 -12
- package/dist/ssr/astro-middleware-static.js.map +1 -1
- package/dist/ssr/astro-middleware.d.ts +1 -2
- package/dist/ssr/astro-middleware.js +1 -13
- package/dist/ssr/astro-middleware.js.map +1 -1
- package/dist/ssr/astro.d.ts +8 -16
- package/dist/ssr/astro.js +220 -1
- package/dist/ssr/astro.js.map +1 -0
- package/dist/ssr/client.d.ts +2 -0
- package/dist/ssr/client.js +2 -0
- package/dist/ssr/index.d.ts +5 -25
- package/dist/ssr/index.js +5 -5
- package/dist/ssr/index.js.map +1 -1
- package/dist/ssr/next-config.d.ts +3 -5
- package/dist/ssr/next-config.js +43 -35
- package/dist/ssr/next-config.js.map +1 -1
- package/dist/ssr/next.d.ts +3 -9
- package/dist/ssr/next.js +7 -38
- package/dist/ssr/next.js.map +1 -1
- package/dist/static/index.d.ts +8 -9
- package/dist/static/index.js +4 -22
- package/dist/static/index.js.map +1 -1
- package/dist/static/inject.d.ts +1 -2
- package/dist/{index-BmogEzTo.d.ts → types-DJEM_BOJ.d.ts} +852 -860
- package/dist/zero/babel.d.ts +67 -2
- package/dist/zero/babel.js +77 -140
- package/dist/zero/babel.js.map +1 -1
- package/dist/zero/index.d.ts +62 -8
- package/dist/zero/index.js +24 -2
- package/dist/zero/index.js.map +1 -0
- package/dist/zero/next.d.ts +4 -5
- package/dist/zero/next.js +3 -38
- package/dist/zero/next.js.map +1 -1
- package/docs/README.md +4 -1
- package/docs/adoption.md +25 -17
- package/docs/ai-agents.md +66 -33
- package/docs/comparison.md +9 -8
- package/docs/configuration.md +192 -60
- package/docs/debug.md +5 -7
- package/docs/design-system.md +117 -15
- package/docs/dsl.md +204 -82
- package/docs/getting-started.md +81 -22
- package/docs/injector.md +41 -40
- package/docs/methodology.md +48 -23
- package/docs/migration-v3.md +3 -3
- package/docs/pipeline.md +54 -27
- package/docs/plugins.md +22 -42
- package/docs/react-api.md +72 -9
- package/docs/runtime-benchmarks.md +146 -91
- package/docs/ssr.md +92 -66
- package/docs/styles.md +117 -31
- package/docs/tasty-static.md +162 -58
- package/package.json +96 -38
- package/dist/astro-CeYENy2x.js +0 -301
- package/dist/astro-CeYENy2x.js.map +0 -1
- package/dist/async-storage-DKK-wTD4.js +0 -44
- package/dist/async-storage-DKK-wTD4.js.map +0 -1
- package/dist/babel-DWCsafYw.d.ts +0 -83
- package/dist/collector-B3OsM252.js.map +0 -1
- package/dist/config-B3gPdCqd.js +0 -12421
- package/dist/config-B3gPdCqd.js.map +0 -1
- package/dist/context-CA8YKeMn.js +0 -24
- package/dist/context-CA8YKeMn.js.map +0 -1
- package/dist/core-DGm0CFHP.js +0 -1732
- package/dist/core-DGm0CFHP.js.map +0 -1
- package/dist/css-resources-Cyl_axbI.js.map +0 -1
- package/dist/css-writer-Bgowobbx.js.map +0 -1
- package/dist/format-global-rules-DklyaXv-.js +0 -22
- package/dist/format-global-rules-DklyaXv-.js.map +0 -1
- package/dist/format-rules-XRw9u7d4.js +0 -104
- package/dist/format-rules-XRw9u7d4.js.map +0 -1
- package/dist/hydrate-CNOmZprz.js +0 -37
- package/dist/hydrate-CNOmZprz.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/keyframes-DE-OE76F.js +0 -535
- package/dist/keyframes-DE-OE76F.js.map +0 -1
- package/dist/merge-styles-DuoZEsm9.js.map +0 -1
- package/dist/resolve-recipes-H9NqOQuP.js +0 -144
- package/dist/resolve-recipes-H9NqOQuP.js.map +0 -1
- package/dist/ssr-collector-ref-COs_ioWl.js +0 -29
- package/dist/ssr-collector-ref-COs_ioWl.js.map +0 -1
package/docs/tasty-static.md
CHANGED
|
@@ -8,30 +8,36 @@ Choose `tastyStatic` specifically when styles must be generated during the build
|
|
|
8
8
|
|
|
9
9
|
## When to Use
|
|
10
10
|
|
|
11
|
-
- **
|
|
12
|
-
- **No
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
11
|
+
- **CSS that must exist before anything renders** — static sites, landing pages, and HTML served from a CDN get one stylesheet generated at build time
|
|
12
|
+
- **No React at runtime** — `tastyStatic` returns class names, so plain DOM code or any templating that sets `class` can use them; the [Programmatic API](#programmatic-api) covers frameworks Babel does not process
|
|
13
|
+
- **Class-name objects instead of components** — extend them with `tastyStatic(base, styles)` and pass them wherever a `className` goes
|
|
14
|
+
- **Self-contained packages** — [inject mode](#inject-mode) embeds the CSS in the JavaScript, so consumers have no stylesheet to wire up
|
|
15
|
+
|
|
16
|
+
If the styles depend on props, keep `tasty()`: rendered only on the server, it also ships no styling runtime (see [Server-Side Rendering](ssr.md)).
|
|
15
17
|
|
|
16
18
|
## Requirements
|
|
17
19
|
|
|
18
20
|
Build-time extraction is part of the main `@tenphi/tasty` package but requires additional peer dependencies depending on your setup:
|
|
19
21
|
|
|
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 |
|
|
22
|
+
| Dependency | Version | Required for |
|
|
23
|
+
| ---------------------------- | ------- | ---------------------------------------------------- |
|
|
24
|
+
| `@babel/core` | >= 7.24 | Babel plugin (`@tenphi/tasty/babel-plugin`) |
|
|
25
|
+
| `@babel/helper-plugin-utils` | >= 7.24 | Babel plugin |
|
|
26
|
+
| `@babel/types` | >= 7.24 | Babel plugin |
|
|
27
|
+
| `jiti` | >= 2.6 | `configFile` only (Babel plugin and Next.js wrapper) |
|
|
28
|
+
| `babel-loader` | any | Next.js wrapper (`@tenphi/tasty/zero/next`) |
|
|
26
29
|
|
|
27
|
-
|
|
30
|
+
The Babel packages and `jiti` are optional peer dependencies of `@tenphi/tasty`; `babel-loader` is resolved from your project. `jiti` is loaded only to read a `configFile`, so a setup that passes `config` directly does not need it; without it, `configFile` fails with an error that says to install it. Install what your setup requires:
|
|
28
31
|
|
|
29
32
|
```bash
|
|
30
|
-
#
|
|
33
|
+
# Any Babel-based setup (Vite, custom Babel config, etc.)
|
|
31
34
|
pnpm add -D @babel/core @babel/helper-plugin-utils @babel/types
|
|
32
35
|
|
|
33
|
-
#
|
|
34
|
-
pnpm add -D @babel/core @babel/helper-plugin-utils @babel/types
|
|
36
|
+
# Next.js
|
|
37
|
+
pnpm add -D @babel/core @babel/helper-plugin-utils @babel/types babel-loader
|
|
38
|
+
|
|
39
|
+
# Either, with configFile
|
|
40
|
+
pnpm add -D jiti
|
|
35
41
|
```
|
|
36
42
|
|
|
37
43
|
---
|
|
@@ -52,13 +58,15 @@ const button = tastyStatic({
|
|
|
52
58
|
radius: '1r',
|
|
53
59
|
});
|
|
54
60
|
|
|
55
|
-
// Use in JSX
|
|
56
|
-
<button className={button}>Click me</button
|
|
61
|
+
// Use in JSX
|
|
62
|
+
<button className={button.className}>Click me</button>;
|
|
57
63
|
|
|
58
|
-
//
|
|
59
|
-
|
|
64
|
+
// toString() returns the class name, for template strings and plain JS
|
|
65
|
+
element.className = `${button} is-active`;
|
|
60
66
|
```
|
|
61
67
|
|
|
68
|
+
In TSX, pass `button.className`: React types `className` as `string`, so passing the `StaticStyle` object fails type-checking even though it works at runtime.
|
|
69
|
+
|
|
62
70
|
---
|
|
63
71
|
|
|
64
72
|
## API Reference
|
|
@@ -105,6 +113,8 @@ tastyStatic('body', {
|
|
|
105
113
|
});
|
|
106
114
|
```
|
|
107
115
|
|
|
116
|
+
The selector can be a list (`'.uni-checkbox, uni-checkbox'`): every item gets the states and sub-elements. Local `@keyframes` and `@starting` states work as in `tastyStatic(styles)`.
|
|
117
|
+
|
|
108
118
|
---
|
|
109
119
|
|
|
110
120
|
## StaticStyle Object
|
|
@@ -166,7 +176,7 @@ module.exports = {
|
|
|
166
176
|
| -------------------------- | ------------------------------------------------ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
167
177
|
| `output` | `string` | `'tasty.css'` | Path for generated CSS file |
|
|
168
178
|
| `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.
|
|
179
|
+
| `configFile` | `string` | — | Absolute path to a TS/JS module that default-exports a `TastyZeroConfig` object. JSON-serializable alternative to `config` — required for Turbopack. Loaded with the optional `jiti` peer. |
|
|
170
180
|
| `config` | `TastyZeroConfig \| () => TastyZeroConfig` | `{}` | Inline config object or factory function. Takes precedence over `configFile`. |
|
|
171
181
|
| `configDeps` | `string[]` | `[]` | Absolute file paths that affect config (for cache invalidation) |
|
|
172
182
|
| `injectImport` | `boolean` | `true` | Replace `@tenphi/tasty/static` imports with an import of the generated CSS file. Set to `false` to manage CSS imports manually. |
|
|
@@ -178,7 +188,8 @@ module.exports = {
|
|
|
178
188
|
| `config.keyframes` | `Record<string, KeyframesSteps>` | — | Global `@keyframes` definitions available to all `tastyStatic` calls |
|
|
179
189
|
| `config.fontFaces` | `Record<string, FontFaceInput>` | — | Global `@font-face` definitions |
|
|
180
190
|
| `config.counterStyles` | `Record<string, CounterStyleDescriptors>` | — | Global `@counter-style` definitions |
|
|
181
|
-
| `config.
|
|
191
|
+
| `config.properties` | `Record<string, PropertyDefinition>` | — | Global `@property` definitions, written with Tasty's [default properties](dsl.md#default-properties) |
|
|
192
|
+
| `config.units` | `Record<string, string \| UnitHandler>` | — | Custom units for the style parser (merged with built-ins). E.g. `{ u: 'var(--u)' }` makes `2u` compile to `calc(2 * var(--u))`. Native CSS units need no entry |
|
|
182
193
|
| `config.functions` | `Record<string, FunctionDefinition \| Function>` | — | Custom functions (merged). Bare keys → parse functions; `$$name` keys → declarative CSS `@function` definitions |
|
|
183
194
|
| `config.polyfills` | `{ functions?: boolean }` | `{}` | Opt-in polyfills. `functions: true` inlines `@function` calls into plain CSS at build time |
|
|
184
195
|
| `config.plugins` | `TastyPlugin[]` | — | Plugins that extend tasty with custom functions, units, states, and handlers |
|
|
@@ -242,7 +253,7 @@ const card = tastyStatic({
|
|
|
242
253
|
color: '#text',
|
|
243
254
|
});
|
|
244
255
|
|
|
245
|
-
<div className={card}>Styled card</div>;
|
|
256
|
+
<div className={card.className}>Styled card</div>;
|
|
246
257
|
```
|
|
247
258
|
|
|
248
259
|
---
|
|
@@ -279,12 +290,12 @@ export default withTastyZero({
|
|
|
279
290
|
|
|
280
291
|
Starting with Next.js 16, Turbopack is the default bundler. `withTastyZero` supports it out of the box by injecting `turbopack.rules` with `babel-loader` and JSON-serializable options.
|
|
281
292
|
|
|
282
|
-
The `configFile` option is key for Turbopack — it passes a file path (JSON-serializable) instead of a function, and the Babel plugin loads the config internally via jiti.
|
|
293
|
+
The `configFile` option is key for Turbopack — it passes a file path (JSON-serializable) instead of a function, and the Babel plugin loads the config internally via `jiti`, which must be installed.
|
|
283
294
|
|
|
284
|
-
**Requirements**: `babel-loader` must be installed in your project:
|
|
295
|
+
**Requirements**: `babel-loader` must be installed in your project (see [Requirements](#requirements)):
|
|
285
296
|
|
|
286
297
|
```bash
|
|
287
|
-
pnpm add babel-loader
|
|
298
|
+
pnpm add -D babel-loader
|
|
288
299
|
```
|
|
289
300
|
|
|
290
301
|
### CSS Injection
|
|
@@ -299,34 +310,95 @@ public/tasty.css
|
|
|
299
310
|
|
|
300
311
|
---
|
|
301
312
|
|
|
313
|
+
## Programmatic API
|
|
314
|
+
|
|
315
|
+
`@tenphi/tasty/zero` turns styles into CSS in Node without Babel or React — for build scripts, or CSS shipped to other frameworks:
|
|
316
|
+
|
|
317
|
+
```ts
|
|
318
|
+
import {
|
|
319
|
+
configure,
|
|
320
|
+
extractStylesForSelector,
|
|
321
|
+
extractStylesWithChunks,
|
|
322
|
+
} from '@tenphi/tasty/zero';
|
|
323
|
+
|
|
324
|
+
configure({
|
|
325
|
+
units: { u: 'var(--unit)' },
|
|
326
|
+
states: { '@dark': '@root(schema=dark)' },
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
extractStylesForSelector('.card', { padding: '2u', fill: '#surface' }).css;
|
|
330
|
+
// → '.card { padding: calc(2 * var(--unit)); background-color: … }'
|
|
331
|
+
|
|
332
|
+
extractStylesWithChunks({ display: 'flex', gap: '1u' });
|
|
333
|
+
// → [{ className: 'ts…', css: '.ts….ts… { … }' }]
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
`configure()` takes the same options as the Babel plugin's `config` (see [Plugin Options](#plugin-options)) and replaces the previous configuration on each call; `resetConfig()` returns to the defaults. Options that shape the extracted rules apply — `states`, `units`, parse `functions`, `handlers`, `replaceTokens`, `recipes`, `plugins`, `namePrefix`. Definitions that become at-rules or global CSS of their own are not emitted with the rules: `tokens`, `globalStyles`, `fontFaces`, `counterStyles`, `properties` and `$$` `@function` definitions. Render tokens and global styles with `extractStylesForSelector(':root', tokens)` and `extractStylesForSelector(selector, styles)`.
|
|
337
|
+
|
|
338
|
+
`extractConfigProperties()` returns the `@property` rules the Babel plugin writes at the top of its output: Tasty's [default set](dsl.md#default-properties) (`#border`, `#outline`, `$gap`, `$radius`, `$transition`, …) overlaid with the configured `properties`, or with the `properties` map you pass it. Emit them once per stylesheet. Style handlers read several of these tokens with no fallback of their own (`border: true` reads `#border`, `transition` reads `$transition`), so without them those declarations resolve to nothing.
|
|
339
|
+
|
|
340
|
+
The configuration is process-wide and shared with the Babel plugin and the runtime `configure()` from `@tenphi/tasty`. The extractors re-apply their own configuration when the Babel plugin replaced it, but not after a runtime `configure()` — don't mix the runtime and build-time configuration in one process.
|
|
341
|
+
|
|
342
|
+
`extractStylesForSelector()` output starts with the `@keyframes` its rules animate — local `@keyframes` and configured `keyframes` — under hashed names the `animation` declarations are rewritten to. `extractStylesWithChunks()` returns class rules only.
|
|
343
|
+
|
|
344
|
+
To write the results to a file the way the Babel plugin does, use `createCSSWriter()`. It deduplicates blocks by key and prepends the generated-file header:
|
|
345
|
+
|
|
346
|
+
```ts
|
|
347
|
+
import {
|
|
348
|
+
createCSSWriter,
|
|
349
|
+
extractConfigProperties,
|
|
350
|
+
extractStylesWithChunks,
|
|
351
|
+
} from '@tenphi/tasty/zero';
|
|
352
|
+
|
|
353
|
+
const writer = createCSSWriter('dist/tasty.css', { devMode: true });
|
|
354
|
+
|
|
355
|
+
for (const prop of extractConfigProperties()) {
|
|
356
|
+
writer.add(`@property:${prop.name}`, prop.css);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
for (const chunk of extractStylesWithChunks({ display: 'flex', gap: '1u' })) {
|
|
360
|
+
writer.add(chunk.className, chunk.css, 'src/card.ts'); // source is a devMode comment
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
writer.write(); // also: has(key), getCSS(), clear(), getOutputPath()
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
302
368
|
## Vite Integration
|
|
303
369
|
|
|
370
|
+
`@vitejs/plugin-react` 6 no longer runs Babel; add it with `@rolldown/plugin-babel` (`pnpm add -D @rolldown/plugin-babel`):
|
|
371
|
+
|
|
304
372
|
```typescript
|
|
305
373
|
// vite.config.ts
|
|
306
374
|
import { defineConfig } from 'vite';
|
|
307
375
|
import react from '@vitejs/plugin-react';
|
|
376
|
+
import babel from '@rolldown/plugin-babel';
|
|
308
377
|
|
|
309
378
|
export default defineConfig({
|
|
310
379
|
plugins: [
|
|
311
|
-
react(
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
},
|
|
380
|
+
react(),
|
|
381
|
+
babel({
|
|
382
|
+
plugins: [
|
|
383
|
+
[
|
|
384
|
+
'@tenphi/tasty/babel-plugin',
|
|
385
|
+
{
|
|
386
|
+
output: 'src/tasty.css',
|
|
387
|
+
config: {
|
|
388
|
+
states: { '@mobile': '@media(w < 768px)' },
|
|
321
389
|
},
|
|
322
|
-
|
|
390
|
+
},
|
|
323
391
|
],
|
|
324
|
-
|
|
392
|
+
],
|
|
325
393
|
}),
|
|
326
394
|
],
|
|
327
395
|
});
|
|
328
396
|
```
|
|
329
397
|
|
|
398
|
+
With `@vitejs/plugin-react` 5 or older, pass the same `plugins` array to `react({ babel: { plugins } })` instead.
|
|
399
|
+
|
|
400
|
+
Keep `output` outside `public/`: the plugin replaces each `@tenphi/tasty/static` import with an import of the generated file, and Vite does not let JavaScript import files from `public/`. Vite then bundles the CSS like any other stylesheet.
|
|
401
|
+
|
|
330
402
|
---
|
|
331
403
|
|
|
332
404
|
## Build Transformation
|
|
@@ -344,44 +416,67 @@ const button = tastyStatic({
|
|
|
344
416
|
|
|
345
417
|
tastyStatic('.heading', { preset: 'h1' });
|
|
346
418
|
|
|
347
|
-
export const Button = () => <button className={button}>Click</button>;
|
|
419
|
+
export const Button = () => <button className={button.className}>Click</button>;
|
|
348
420
|
```
|
|
349
421
|
|
|
350
422
|
### After (Production Build)
|
|
351
423
|
|
|
352
424
|
```tsx
|
|
425
|
+
import './tasty.css';
|
|
426
|
+
|
|
353
427
|
const button = {
|
|
354
|
-
className: '
|
|
428
|
+
className: 'ts3f783b ts070801',
|
|
355
429
|
styles: { padding: '2x 4x', fill: '#purple', color: '#white' },
|
|
356
430
|
toString() {
|
|
357
431
|
return this.className;
|
|
358
432
|
},
|
|
359
433
|
};
|
|
360
434
|
|
|
361
|
-
export const Button = () => <button className={button}>Click</button>;
|
|
435
|
+
export const Button = () => <button className={button.className}>Click</button>;
|
|
362
436
|
```
|
|
363
437
|
|
|
438
|
+
The `@tenphi/tasty/static` import becomes an import of the generated file (relative to the source file), and the selector-mode call is removed.
|
|
439
|
+
|
|
364
440
|
### Generated CSS (tasty.css)
|
|
365
441
|
|
|
366
442
|
```css
|
|
367
443
|
/* Generated by @tenphi/tasty/zero - DO NOT EDIT */
|
|
368
444
|
|
|
369
|
-
|
|
370
|
-
|
|
445
|
+
@property --tasty-second-fill-color {
|
|
446
|
+
syntax: '<color>';
|
|
447
|
+
inherits: false;
|
|
448
|
+
initial-value: transparent;
|
|
371
449
|
}
|
|
372
450
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
451
|
+
@property --current-color {
|
|
452
|
+
syntax: '<color>';
|
|
453
|
+
inherits: true;
|
|
454
|
+
initial-value: currentcolor;
|
|
376
455
|
}
|
|
377
456
|
|
|
378
|
-
.
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
457
|
+
/* …the rest of Tasty's default @property rules (--white-color, --border-color, --gap, --transition, …), then config.properties */
|
|
458
|
+
|
|
459
|
+
.ts3f783b.ts3f783b {
|
|
460
|
+
color: var(--white-color);
|
|
461
|
+
--current-color: var(--white-color);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.ts3f783b.ts3f783b {
|
|
465
|
+
background-color: var(--purple-color);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.ts070801.ts070801 {
|
|
469
|
+
padding: 16px 32px;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.heading {
|
|
473
|
+
font-size: var(--h1-font-size, var(--default-font-size));
|
|
474
|
+
/* …line-height, letter-spacing, font-weight, and the rest of the h1 preset */
|
|
382
475
|
}
|
|
383
476
|
```
|
|
384
477
|
|
|
478
|
+
The file starts with Tasty's [default `@property` rules](dsl.md#default-properties) and your `config.properties`, written once. Styles are split into chunks (here appearance and spacing), each with its own class. Color tokens compile to custom properties: `#purple` becomes `var(--purple-color)`. Define it in `config.tokens` and the plugin writes it to `:root` at the top of the same file. Units are literal (`2x` is `16px`). Selector-mode rules use the selector as written, without the doubled specificity of class rules.
|
|
479
|
+
|
|
385
480
|
---
|
|
386
481
|
|
|
387
482
|
## Inject Mode
|
|
@@ -452,22 +547,29 @@ tastyStatic('.heading', { preset: 'h1' });
|
|
|
452
547
|
```tsx
|
|
453
548
|
import { injectCSS as _$i } from '@tenphi/tasty/static/inject';
|
|
454
549
|
|
|
550
|
+
_$i(
|
|
551
|
+
'@property',
|
|
552
|
+
'@property --tasty-second-fill-color { … }\n@property --current-color { … }\n…',
|
|
553
|
+
);
|
|
554
|
+
|
|
455
555
|
const button =
|
|
456
556
|
(_$i(
|
|
457
|
-
'
|
|
458
|
-
'.
|
|
557
|
+
'tsa903d6 ts070801',
|
|
558
|
+
'.tsa903d6.tsa903d6 { background-color: var(--purple-color); }\n.ts070801.ts070801 { padding: 16px 32px; }',
|
|
459
559
|
),
|
|
460
560
|
{
|
|
461
|
-
className: '
|
|
561
|
+
className: 'tsa903d6 ts070801',
|
|
462
562
|
styles: { padding: '2x 4x', fill: '#purple' },
|
|
463
563
|
toString() {
|
|
464
564
|
return this.className;
|
|
465
565
|
},
|
|
466
566
|
});
|
|
467
567
|
|
|
468
|
-
_$i('.heading', '.heading{font-size:
|
|
568
|
+
_$i('.heading', '.heading { font-size: var(--h1-font-size, …); … }');
|
|
469
569
|
```
|
|
470
570
|
|
|
571
|
+
Each file that calls `tastyStatic()` starts by injecting Tasty's default `@property` rules and your `config.properties` under the `@property` id. `injectCSS()` skips an id it has already injected, so the page gets them once.
|
|
572
|
+
|
|
471
573
|
### Dev Mode / HMR
|
|
472
574
|
|
|
473
575
|
Class names are content-hashed (`ts` + MD5). When styles change, a new hash produces a new `_$i` call that injects fresh CSS. The injector deduplicates by id, so unchanged styles are skipped. Old CSS stays in the DOM but is harmless since no elements reference those class names.
|
|
@@ -489,7 +591,7 @@ const button = tastyStatic({
|
|
|
489
591
|
radius: '1r',
|
|
490
592
|
fill: '#gray.20',
|
|
491
593
|
color: '#text',
|
|
492
|
-
transition: 'fill
|
|
594
|
+
transition: 'fill',
|
|
493
595
|
});
|
|
494
596
|
|
|
495
597
|
// Variants
|
|
@@ -525,7 +627,7 @@ const card = tastyStatic({
|
|
|
525
627
|
|
|
526
628
|
## Extending Style Types (TypeScript)
|
|
527
629
|
|
|
528
|
-
If you add custom style properties, use module augmentation so `tastyStatic` recognizes them too. See [
|
|
630
|
+
If you add custom style properties, use module augmentation so `tastyStatic` recognizes them too. See [TypeScript](configuration.md#typescript) in the configuration docs.
|
|
529
631
|
|
|
530
632
|
---
|
|
531
633
|
|
|
@@ -547,10 +649,12 @@ const card = tastyStatic({
|
|
|
547
649
|
});
|
|
548
650
|
|
|
549
651
|
<div
|
|
550
|
-
className={card}
|
|
551
|
-
style={
|
|
552
|
-
|
|
553
|
-
|
|
652
|
+
className={card.className}
|
|
653
|
+
style={
|
|
654
|
+
{
|
|
655
|
+
'--card-bg': isActive ? 'var(--purple-color)' : 'var(--white-color)',
|
|
656
|
+
} as React.CSSProperties
|
|
657
|
+
}
|
|
554
658
|
/>;
|
|
555
659
|
```
|
|
556
660
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tenphi/tasty",
|
|
3
|
-
"version": "0.0.0-snapshot.
|
|
3
|
+
"version": "0.0.0-snapshot.07ea4a0",
|
|
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",
|
|
@@ -62,6 +62,11 @@
|
|
|
62
62
|
"import": "./dist/ssr/astro.js",
|
|
63
63
|
"default": "./dist/ssr/astro.js"
|
|
64
64
|
},
|
|
65
|
+
"./ssr/client": {
|
|
66
|
+
"types": "./dist/ssr/client.d.ts",
|
|
67
|
+
"import": "./dist/ssr/client.js",
|
|
68
|
+
"default": "./dist/ssr/client.js"
|
|
69
|
+
},
|
|
65
70
|
"./ssr/astro-client": {
|
|
66
71
|
"types": "./dist/ssr/astro-client.d.ts",
|
|
67
72
|
"import": "./dist/ssr/astro-client.js",
|
|
@@ -126,12 +131,15 @@
|
|
|
126
131
|
"bench:overhead": "vitest bench --config vitest.production-browser-bench.config.ts src/tasty-overhead.bench.tsx",
|
|
127
132
|
"bench:injection": "vitest bench --config vitest.production-browser-bench.config.ts src/tasty-injection.bench.ts",
|
|
128
133
|
"bench:interaction": "vitest bench --config vitest.production-browser-bench.config.ts src/tasty-interaction.bench.tsx",
|
|
134
|
+
"bench:tree": "vitest bench --config vitest.production-browser-bench.config.ts src/tasty-tree.bench.tsx",
|
|
129
135
|
"bench:cold-start": "node scripts/cold-start/index.mjs",
|
|
130
136
|
"prepublishOnly": "pnpm run build",
|
|
131
137
|
"changeset": "changeset",
|
|
132
138
|
"version": "changeset version",
|
|
133
139
|
"release": "changeset publish",
|
|
134
140
|
"knip": "knip",
|
|
141
|
+
"check:build": "node scripts/check-built-entries.mjs",
|
|
142
|
+
"check:react-compat": "node scripts/check-react-compat.mjs",
|
|
135
143
|
"check:test-only": "node scripts/check-test-only-code.mjs",
|
|
136
144
|
"hygiene": "pnpm lint && pnpm format:check && pnpm typecheck",
|
|
137
145
|
"hygiene:fix": "pnpm lint:fix && pnpm format && pnpm typecheck"
|
|
@@ -189,60 +197,84 @@
|
|
|
189
197
|
"csstype": "^3.1.0"
|
|
190
198
|
},
|
|
191
199
|
"devDependencies": {
|
|
192
|
-
"@astrojs/react": "^
|
|
193
|
-
"@babel/core": "^7.
|
|
194
|
-
"@babel/helper-plugin-utils": "^7.
|
|
195
|
-
"@babel/types": "^7.
|
|
196
|
-
"@changesets/changelog-github": "^0.
|
|
197
|
-
"@changesets/cli": "^
|
|
200
|
+
"@astrojs/react": "^7.0.0",
|
|
201
|
+
"@babel/core": "^7.29.7",
|
|
202
|
+
"@babel/helper-plugin-utils": "^7.29.7",
|
|
203
|
+
"@babel/types": "^7.29.8",
|
|
204
|
+
"@changesets/changelog-github": "^1.0.1",
|
|
205
|
+
"@changesets/cli": "^3.0.3",
|
|
198
206
|
"@eslint/js": "^10.0.1",
|
|
199
|
-
"@size-limit/esbuild": "^
|
|
200
|
-
"@size-limit/file": "^
|
|
201
|
-
"@tenphi/eslint-plugin-tasty": "^1.
|
|
202
|
-
"@testing-library/jest-dom": "^
|
|
203
|
-
"@testing-library/react": "^16.3.
|
|
207
|
+
"@size-limit/esbuild": "^14.0.0",
|
|
208
|
+
"@size-limit/file": "^14.0.0",
|
|
209
|
+
"@tenphi/eslint-plugin-tasty": "^1.5.1",
|
|
210
|
+
"@testing-library/jest-dom": "^7.0.1",
|
|
211
|
+
"@testing-library/react": "^16.3.3",
|
|
204
212
|
"@types/babel__core": "^7.20.5",
|
|
205
213
|
"@types/babel__helper-plugin-utils": "^7.10.3",
|
|
206
|
-
"@types/node": "^24.13.
|
|
207
|
-
"@types/react": "^19.
|
|
208
|
-
"@types/react-dom": "^19.
|
|
209
|
-
"@vitest/browser-playwright": "^4.1.
|
|
210
|
-
"astro": "^
|
|
211
|
-
"esbuild": "^0.
|
|
212
|
-
"eslint": "^10.
|
|
214
|
+
"@types/node": "^24.13.6",
|
|
215
|
+
"@types/react": "^19.3.0",
|
|
216
|
+
"@types/react-dom": "^19.3.0",
|
|
217
|
+
"@vitest/browser-playwright": "^4.1.11",
|
|
218
|
+
"astro": "^7.3.4",
|
|
219
|
+
"esbuild": "^0.28.2",
|
|
220
|
+
"eslint": "^10.11.0",
|
|
213
221
|
"eslint-config-prettier": "^10.1.8",
|
|
214
|
-
"jiti": "^2.
|
|
215
|
-
"knip": "^
|
|
216
|
-
"next": "^16.
|
|
217
|
-
"playwright": "^1.
|
|
218
|
-
"prettier": "^3.9.
|
|
219
|
-
"react": "^19.
|
|
220
|
-
"react-dom": "^19.
|
|
221
|
-
"size-limit": "^
|
|
222
|
-
"tsdown": "^0.
|
|
223
|
-
"typescript": "^6.0.
|
|
224
|
-
"typescript-eslint": "^8.
|
|
225
|
-
"vitest": "^4.1.
|
|
222
|
+
"jiti": "^2.7.0",
|
|
223
|
+
"knip": "^6.38.0",
|
|
224
|
+
"next": "^16.3.6",
|
|
225
|
+
"playwright": "^1.63.0",
|
|
226
|
+
"prettier": "^3.9.9",
|
|
227
|
+
"react": "^19.3.0",
|
|
228
|
+
"react-dom": "^19.3.0",
|
|
229
|
+
"size-limit": "^14.0.0",
|
|
230
|
+
"tsdown": "^0.23.0",
|
|
231
|
+
"typescript": "^6.0.3",
|
|
232
|
+
"typescript-eslint": "^8.70.1",
|
|
233
|
+
"vitest": "^4.1.11"
|
|
226
234
|
},
|
|
227
|
-
"packageManager": "pnpm@11.
|
|
235
|
+
"packageManager": "pnpm@11.27.1",
|
|
228
236
|
"size-limit": [
|
|
229
237
|
{
|
|
230
238
|
"name": "main (import *)",
|
|
231
239
|
"path": "dist/index.js",
|
|
232
240
|
"import": "*",
|
|
233
|
-
"limit": "
|
|
241
|
+
"limit": "59.65 kB"
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"name": "main (tasty)",
|
|
245
|
+
"path": "dist/index.js",
|
|
246
|
+
"import": "{ tasty }",
|
|
247
|
+
"limit": "52.67 kB"
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"name": "main (StyleParser)",
|
|
251
|
+
"path": "dist/index.js",
|
|
252
|
+
"import": "{ StyleParser }",
|
|
253
|
+
"limit": "6.94 kB"
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"name": "main (parseColor)",
|
|
257
|
+
"path": "dist/index.js",
|
|
258
|
+
"import": "{ parseColor }",
|
|
259
|
+
"limit": "7.59 kB"
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"name": "main (useRawCSS)",
|
|
263
|
+
"path": "dist/index.js",
|
|
264
|
+
"import": "{ useRawCSS }",
|
|
265
|
+
"limit": "32.48 kB"
|
|
234
266
|
},
|
|
235
267
|
{
|
|
236
268
|
"name": "core (import *)",
|
|
237
269
|
"path": "dist/core/index.js",
|
|
238
270
|
"import": "*",
|
|
239
|
-
"limit": "
|
|
271
|
+
"limit": "56.29 kB"
|
|
240
272
|
},
|
|
241
273
|
{
|
|
242
274
|
"name": "static",
|
|
243
275
|
"path": "dist/static/index.js",
|
|
244
276
|
"import": "*",
|
|
245
|
-
"limit": "
|
|
277
|
+
"limit": "1.08 kB"
|
|
246
278
|
},
|
|
247
279
|
{
|
|
248
280
|
"name": "zero",
|
|
@@ -253,7 +285,7 @@
|
|
|
253
285
|
"path",
|
|
254
286
|
"crypto"
|
|
255
287
|
],
|
|
256
|
-
"limit": "
|
|
288
|
+
"limit": "39.13 kB"
|
|
257
289
|
},
|
|
258
290
|
{
|
|
259
291
|
"name": "babel-plugin",
|
|
@@ -262,9 +294,35 @@
|
|
|
262
294
|
"ignore": [
|
|
263
295
|
"fs",
|
|
264
296
|
"path",
|
|
265
|
-
"crypto"
|
|
297
|
+
"crypto",
|
|
298
|
+
"node:module",
|
|
299
|
+
"node:path"
|
|
300
|
+
],
|
|
301
|
+
"limit": "43.55 kB"
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
"name": "astro client",
|
|
305
|
+
"path": "dist/ssr/astro-client.js",
|
|
306
|
+
"import": "*",
|
|
307
|
+
"limit": "0.5 kB"
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
"name": "ssr client",
|
|
311
|
+
"path": "dist/ssr/client.js",
|
|
312
|
+
"import": "{ hydrateTastyClasses }",
|
|
313
|
+
"limit": "0.5 kB"
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
"name": "Next.js config",
|
|
317
|
+
"path": "dist/ssr/next-config.js",
|
|
318
|
+
"import": "{ withTastyNext }",
|
|
319
|
+
"ignore": [
|
|
320
|
+
"node:crypto",
|
|
321
|
+
"node:fs",
|
|
322
|
+
"node:module",
|
|
323
|
+
"node:path"
|
|
266
324
|
],
|
|
267
|
-
"limit": "
|
|
325
|
+
"limit": "40.18 kB"
|
|
268
326
|
}
|
|
269
327
|
]
|
|
270
328
|
}
|