@tenphi/tasty 1.1.0 → 1.3.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.
- package/dist/_virtual/_rolldown/runtime.js +1 -2
- package/dist/chunks/cacheKey.d.ts +1 -0
- package/dist/chunks/cacheKey.js +1 -2
- package/dist/chunks/cacheKey.js.map +1 -1
- package/dist/chunks/definitions.js +1 -2
- package/dist/chunks/definitions.js.map +1 -1
- package/dist/chunks/index.d.ts +1 -0
- package/dist/chunks/renderChunk.d.ts +1 -0
- package/dist/chunks/renderChunk.js +1 -2
- package/dist/chunks/renderChunk.js.map +1 -1
- package/dist/compute-styles.d.ts +31 -0
- package/dist/compute-styles.js +356 -0
- package/dist/compute-styles.js.map +1 -0
- package/dist/config.d.ts +7 -1
- package/dist/config.js +25 -22
- package/dist/config.js.map +1 -1
- package/dist/core/index.d.ts +4 -4
- package/dist/core/index.js +6 -6
- package/dist/counter-style/index.js +1 -1
- package/dist/counter-style/index.js.map +1 -1
- package/dist/debug.js +1 -2
- package/dist/debug.js.map +1 -1
- package/dist/font-face/index.js +1 -1
- package/dist/font-face/index.js.map +1 -1
- package/dist/hooks/index.d.ts +7 -0
- package/dist/hooks/resolve-ssr-collector.js +1 -2
- package/dist/hooks/resolve-ssr-collector.js.map +1 -1
- package/dist/hooks/useCounterStyle.js +2 -3
- package/dist/hooks/useCounterStyle.js.map +1 -1
- package/dist/hooks/useFontFace.js +2 -3
- package/dist/hooks/useFontFace.js.map +1 -1
- package/dist/hooks/useGlobalStyles.js +4 -5
- package/dist/hooks/useGlobalStyles.js.map +1 -1
- package/dist/hooks/useKeyframes.js +3 -4
- package/dist/hooks/useKeyframes.js.map +1 -1
- package/dist/hooks/useProperty.js +2 -3
- package/dist/hooks/useProperty.js.map +1 -1
- package/dist/hooks/useRawCSS.js +2 -3
- package/dist/hooks/useRawCSS.js.map +1 -1
- package/dist/hooks/useStyles.d.ts +3 -8
- package/dist/hooks/useStyles.js +7 -214
- package/dist/hooks/useStyles.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +7 -7
- package/dist/injector/index.d.ts +1 -18
- package/dist/injector/index.js +5 -19
- package/dist/injector/index.js.map +1 -1
- package/dist/injector/injector.js +1 -2
- package/dist/injector/injector.js.map +1 -1
- package/dist/injector/sheet-manager.js +1 -2
- package/dist/injector/sheet-manager.js.map +1 -1
- package/dist/keyframes/index.js +1 -1
- package/dist/parser/classify.js +1 -2
- package/dist/parser/classify.js.map +1 -1
- package/dist/parser/const.js +14 -3
- package/dist/parser/const.js.map +1 -1
- package/dist/parser/lru.js +1 -1
- package/dist/parser/lru.js.map +1 -1
- package/dist/parser/parser.js +1 -2
- package/dist/parser/parser.js.map +1 -1
- package/dist/parser/tokenizer.js +1 -1
- package/dist/parser/tokenizer.js.map +1 -1
- package/dist/parser/types.js +1 -1
- package/dist/parser/types.js.map +1 -1
- package/dist/pipeline/conditions.js +1 -1
- package/dist/pipeline/conditions.js.map +1 -1
- package/dist/pipeline/exclusive.js +1 -2
- package/dist/pipeline/exclusive.js.map +1 -1
- package/dist/pipeline/index.js +2 -3
- package/dist/pipeline/index.js.map +1 -1
- package/dist/pipeline/materialize.js +1 -2
- package/dist/pipeline/materialize.js.map +1 -1
- package/dist/pipeline/parseStateKey.js +1 -2
- package/dist/pipeline/parseStateKey.js.map +1 -1
- package/dist/pipeline/simplify.js +1 -2
- package/dist/pipeline/simplify.js.map +1 -1
- package/dist/pipeline/warnings.js +1 -1
- package/dist/plugins/index.d.ts +2 -0
- package/dist/plugins/okhsl-plugin.js +1 -2
- package/dist/plugins/okhsl-plugin.js.map +1 -1
- package/dist/properties/index.js +2 -3
- package/dist/properties/index.js.map +1 -1
- package/dist/properties/property-type-resolver.js +1 -2
- package/dist/properties/property-type-resolver.js.map +1 -1
- package/dist/ssr/astro.js +1 -2
- package/dist/ssr/astro.js.map +1 -1
- package/dist/ssr/async-storage.js +1 -2
- package/dist/ssr/async-storage.js.map +1 -1
- package/dist/ssr/collect-auto-properties.js +1 -2
- package/dist/ssr/collect-auto-properties.js.map +1 -1
- package/dist/ssr/collector.js +4 -12
- package/dist/ssr/collector.js.map +1 -1
- package/dist/ssr/context.d.ts +2 -2
- package/dist/ssr/context.js +1 -2
- package/dist/ssr/context.js.map +1 -1
- package/dist/ssr/format-global-rules.js +1 -1
- package/dist/ssr/format-keyframes.js +1 -2
- package/dist/ssr/format-keyframes.js.map +1 -1
- package/dist/ssr/format-property.js +1 -2
- package/dist/ssr/format-property.js.map +1 -1
- package/dist/ssr/format-rules.js +1 -1
- package/dist/ssr/hydrate.js +1 -2
- package/dist/ssr/hydrate.js.map +1 -1
- package/dist/ssr/index.js +1 -2
- package/dist/ssr/index.js.map +1 -1
- package/dist/ssr/next.d.ts +2 -2
- package/dist/ssr/next.js +9 -5
- package/dist/ssr/next.js.map +1 -1
- package/dist/ssr/ssr-collector-ref.js +1 -1
- package/dist/states/index.js +1 -2
- package/dist/states/index.js.map +1 -1
- package/dist/static/index.js +1 -2
- package/dist/static/inject.d.ts +5 -0
- package/dist/static/inject.js +17 -0
- package/dist/static/inject.js.map +1 -0
- package/dist/static/tastyStatic.js +1 -2
- package/dist/static/tastyStatic.js.map +1 -1
- package/dist/static/types.js +1 -1
- package/dist/styles/border.d.ts +1 -1
- package/dist/styles/border.js +28 -22
- package/dist/styles/border.js.map +1 -1
- package/dist/styles/color.d.ts +1 -1
- package/dist/styles/color.js +2 -3
- package/dist/styles/color.js.map +1 -1
- package/dist/styles/const.js +17 -0
- package/dist/styles/const.js.map +1 -0
- package/dist/styles/createStyle.js +4 -5
- package/dist/styles/createStyle.js.map +1 -1
- package/dist/styles/dimension.js +15 -3
- package/dist/styles/dimension.js.map +1 -1
- package/dist/styles/directional.js +133 -0
- package/dist/styles/directional.js.map +1 -0
- package/dist/styles/display.d.ts +3 -10
- package/dist/styles/display.js +45 -39
- package/dist/styles/display.js.map +1 -1
- package/dist/styles/fade.d.ts +1 -1
- package/dist/styles/fade.js +9 -5
- package/dist/styles/fade.js.map +1 -1
- package/dist/styles/fill.d.ts +2 -2
- package/dist/styles/fill.js +3 -4
- package/dist/styles/fill.js.map +1 -1
- package/dist/styles/flow.js +1 -1
- package/dist/styles/gap.d.ts +1 -1
- package/dist/styles/gap.js +4 -3
- package/dist/styles/gap.js.map +1 -1
- package/dist/styles/height.d.ts +1 -1
- package/dist/styles/height.js +1 -2
- package/dist/styles/height.js.map +1 -1
- package/dist/styles/index.d.ts +0 -1
- package/dist/styles/index.js +3 -4
- package/dist/styles/index.js.map +1 -1
- package/dist/styles/inset.d.ts +1 -29
- package/dist/styles/inset.js +19 -135
- package/dist/styles/inset.js.map +1 -1
- package/dist/styles/list.d.ts +5 -5
- package/dist/styles/list.js +4 -2
- package/dist/styles/list.js.map +1 -1
- package/dist/styles/margin.d.ts +1 -1
- package/dist/styles/margin.js +17 -89
- package/dist/styles/margin.js.map +1 -1
- package/dist/styles/outline.d.ts +1 -1
- package/dist/styles/outline.js +6 -16
- package/dist/styles/outline.js.map +1 -1
- package/dist/styles/padding.d.ts +1 -1
- package/dist/styles/padding.js +17 -89
- package/dist/styles/padding.js.map +1 -1
- package/dist/styles/placement.d.ts +37 -0
- package/dist/styles/placement.js +74 -0
- package/dist/styles/placement.js.map +1 -0
- package/dist/styles/predefined.d.ts +4 -4
- package/dist/styles/predefined.js +8 -9
- package/dist/styles/predefined.js.map +1 -1
- package/dist/styles/preset.d.ts +1 -1
- package/dist/styles/preset.js +7 -7
- package/dist/styles/preset.js.map +1 -1
- package/dist/styles/radius.d.ts +1 -3
- package/dist/styles/radius.js +38 -6
- package/dist/styles/radius.js.map +1 -1
- package/dist/styles/scrollMargin.d.ts +24 -0
- package/dist/styles/scrollMargin.js +32 -0
- package/dist/styles/scrollMargin.js.map +1 -0
- package/dist/styles/scrollbar.d.ts +1 -1
- package/dist/styles/scrollbar.js +8 -5
- package/dist/styles/scrollbar.js.map +1 -1
- package/dist/styles/shadow.d.ts +1 -1
- package/dist/styles/shadow.js +4 -3
- package/dist/styles/shadow.js.map +1 -1
- package/dist/styles/shared.js +17 -0
- package/dist/styles/shared.js.map +1 -0
- package/dist/styles/transition.d.ts +1 -1
- package/dist/styles/transition.js +5 -4
- package/dist/styles/transition.js.map +1 -1
- package/dist/styles/types.d.ts +24 -7
- package/dist/styles/width.d.ts +1 -1
- package/dist/styles/width.js +1 -2
- package/dist/styles/width.js.map +1 -1
- package/dist/tasty.d.ts +29 -14
- package/dist/tasty.js +70 -62
- package/dist/tasty.js.map +1 -1
- package/dist/utils/cache-wrapper.js +1 -2
- package/dist/utils/cache-wrapper.js.map +1 -1
- package/dist/utils/case-converter.js +1 -1
- package/dist/utils/color-math.js +1 -1
- package/dist/utils/color-math.js.map +1 -1
- package/dist/utils/color-space.js +1 -2
- package/dist/utils/color-space.js.map +1 -1
- package/dist/utils/colors.js +1 -2
- package/dist/utils/colors.js.map +1 -1
- package/dist/utils/dotize.js +1 -1
- package/dist/utils/dotize.js.map +1 -1
- package/dist/utils/filter-base-props.js +1 -1
- package/dist/utils/get-display-name.js +1 -1
- package/dist/utils/has-keys.js +1 -1
- package/dist/utils/is-dev-env.js +1 -1
- package/dist/utils/is-dev-env.js.map +1 -1
- package/dist/utils/is-valid-element-type.js +1 -1
- package/dist/utils/merge-styles.js +1 -2
- package/dist/utils/merge-styles.js.map +1 -1
- package/dist/utils/mod-attrs.d.ts +0 -2
- package/dist/utils/mod-attrs.js +1 -2
- package/dist/utils/mod-attrs.js.map +1 -1
- package/dist/utils/process-tokens.d.ts +1 -5
- package/dist/utils/process-tokens.js +3 -11
- package/dist/utils/process-tokens.js.map +1 -1
- package/dist/utils/resolve-recipes.js +1 -2
- package/dist/utils/resolve-recipes.js.map +1 -1
- package/dist/utils/selector-transform.js +1 -1
- package/dist/utils/string.js +1 -1
- package/dist/utils/styles.d.ts +1 -1
- package/dist/utils/styles.js +2 -3
- package/dist/utils/styles.js.map +1 -1
- package/dist/utils/typography.js +1 -1
- package/dist/utils/typography.js.map +1 -1
- package/dist/utils/warnings.js +1 -1
- package/dist/zero/babel.d.ts +17 -2
- package/dist/zero/babel.js +105 -41
- package/dist/zero/babel.js.map +1 -1
- package/dist/zero/css-writer.js +1 -2
- package/dist/zero/css-writer.js.map +1 -1
- package/dist/zero/extractor.js +1 -2
- package/dist/zero/extractor.js.map +1 -1
- package/dist/zero/index.js +1 -2
- package/dist/zero/next.d.ts +12 -0
- package/dist/zero/next.js +5 -4
- package/dist/zero/next.js.map +1 -1
- package/docs/methodology.md +50 -1
- package/docs/runtime.md +90 -3
- package/docs/ssr.md +10 -10
- package/docs/styles.md +17 -0
- package/docs/tasty-static.md +87 -0
- package/package.json +12 -7
- package/dist/styles/align.d.ts +0 -15
- package/dist/styles/align.js +0 -14
- package/dist/styles/align.js.map +0 -1
- package/dist/styles/justify.d.ts +0 -15
- package/dist/styles/justify.js +0 -14
- package/dist/styles/justify.js.map +0 -1
package/docs/runtime.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Runtime API
|
|
2
2
|
|
|
3
|
-
The React-specific `tasty()` component factory, component props, and hooks. For the shared style language (state maps, tokens, units, extending semantics), see [Style DSL](dsl.md). For global configuration, see [Configuration](configuration.md). For the broader docs map, see the [Docs Hub](README.md).
|
|
3
|
+
The React-specific `tasty()` component factory, component props, and hooks. `tasty()` components are hook-free and compatible with React Server Components — no `'use client'` directive needed. For the shared style language (state maps, tokens, units, extending semantics), see [Style DSL](dsl.md). For global configuration, see [Configuration](configuration.md). For the broader docs map, see the [Docs Hub](README.md).
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -154,6 +154,75 @@ For architecture guidance on when to use modifiers vs `styleProps`, see [Methodo
|
|
|
154
154
|
|
|
155
155
|
---
|
|
156
156
|
|
|
157
|
+
## Token Props
|
|
158
|
+
|
|
159
|
+
Use `tokenProps` to expose token keys as direct component props instead of requiring the `tokens` object:
|
|
160
|
+
|
|
161
|
+
```jsx
|
|
162
|
+
// Before: tokens object
|
|
163
|
+
<ProgressBar tokens={{ $progress: '75%', '#accent': '#purple' }} />
|
|
164
|
+
|
|
165
|
+
// After: token props
|
|
166
|
+
<ProgressBar progress="75%" accentColor="#purple" />
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Array form
|
|
170
|
+
|
|
171
|
+
List prop names. Names ending in `Color` map to `#` color tokens; everything else maps to `$` custom property tokens:
|
|
172
|
+
|
|
173
|
+
```jsx
|
|
174
|
+
const ProgressBar = tasty({
|
|
175
|
+
tokenProps: ['progress', 'accentColor'] as const,
|
|
176
|
+
styles: { width: '$progress', fill: '#accent' },
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
<ProgressBar progress="75%" accentColor="#purple" />
|
|
180
|
+
// 'progress' → $progress → --progress
|
|
181
|
+
// 'accentColor' → #accent → --accent-color + --accent-color-oklch
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Object form
|
|
185
|
+
|
|
186
|
+
Map prop names to explicit `$`/`#`-prefixed token keys:
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
const Card = tasty({
|
|
190
|
+
tokenProps: {
|
|
191
|
+
size: '$card-size',
|
|
192
|
+
color: '#card-accent',
|
|
193
|
+
},
|
|
194
|
+
styles: { padding: '$card-size', fill: '#card-accent' },
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
<Card size="4x" color="#purple" />
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Merge with `tokens`
|
|
201
|
+
|
|
202
|
+
Token props and the `tokens` prop can be used together. Token props take precedence over `tokens`, which takes precedence over default `tokens` in `tasty({...})`:
|
|
203
|
+
|
|
204
|
+
```jsx
|
|
205
|
+
const Bar = tasty({
|
|
206
|
+
tokenProps: ['progress'] as const,
|
|
207
|
+
tokens: { $progress: '0%' }, // default
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
<Bar tokens={{ $progress: '50%' }} progress="90%" />
|
|
211
|
+
// progress="90%" wins (from token prop)
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### When to use `tokenProps` vs `tokens`
|
|
215
|
+
|
|
216
|
+
| Use case | Recommendation |
|
|
217
|
+
|---|---|
|
|
218
|
+
| Component has a fixed set of known token keys | `tokenProps` — cleaner API, better TypeScript autocomplete |
|
|
219
|
+
| Component needs arbitrary/dynamic token values | `tokens` — open-ended `Record<string, TokenValue>` |
|
|
220
|
+
| Both fixed and dynamic | Combine: `tokenProps` for known keys, `tokens` for ad-hoc |
|
|
221
|
+
|
|
222
|
+
For architecture guidance, see [Methodology — tokenProps](methodology.md#tokenprops).
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
157
226
|
## Variants
|
|
158
227
|
|
|
159
228
|
Define named style variations. Only CSS for variants actually used at runtime is injected:
|
|
@@ -264,11 +333,29 @@ For the mental model behind sub-elements — how they share root state context a
|
|
|
264
333
|
|
|
265
334
|
---
|
|
266
335
|
|
|
336
|
+
## computeStyles
|
|
337
|
+
|
|
338
|
+
Hook-free, synchronous style computation. Can be used anywhere — including React Server Components, plain functions, and non-React code:
|
|
339
|
+
|
|
340
|
+
```tsx
|
|
341
|
+
import { computeStyles } from '@tenphi/tasty';
|
|
342
|
+
|
|
343
|
+
const { className } = computeStyles({
|
|
344
|
+
padding: '2x',
|
|
345
|
+
fill: '#surface',
|
|
346
|
+
radius: '1r',
|
|
347
|
+
});
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
On the client, CSS is injected synchronously into the DOM (idempotent via the injector cache). On the server, CSS is collected via the SSR collector if one is available. This is the same function that `tasty()` components use internally.
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
267
354
|
## Hooks
|
|
268
355
|
|
|
269
356
|
### useStyles
|
|
270
357
|
|
|
271
|
-
Generate a className from a style object:
|
|
358
|
+
Generate a className from a style object. Thin wrapper around `computeStyles()` that adds React context-based SSR collector discovery for backward compatibility:
|
|
272
359
|
|
|
273
360
|
```tsx
|
|
274
361
|
import { useStyles } from '@tenphi/tasty';
|
|
@@ -469,7 +556,7 @@ function useCounterStyle(
|
|
|
469
556
|
### Troubleshooting
|
|
470
557
|
|
|
471
558
|
- Styles are not updating: make sure `configure()` runs before first render, and verify the generated class name or global rule with [Debug Utilities](debug.md).
|
|
472
|
-
- SSR output looks wrong: check the [SSR guide](ssr.md)
|
|
559
|
+
- SSR output looks wrong: check the [SSR guide](ssr.md) for collector setup. `computeStyles()` discovers the SSR collector via `AsyncLocalStorage` or the global getter registered by `TastyRegistry`.
|
|
473
560
|
- Animation/custom property issues: prefer `useKeyframes()` and `useProperty()` over raw CSS when you want Tasty to manage injection and SSR collection for you.
|
|
474
561
|
|
|
475
562
|
---
|
package/docs/ssr.md
CHANGED
|
@@ -18,16 +18,16 @@ The Astro integration (`@tenphi/tasty/ssr/astro`) has no additional dependencies
|
|
|
18
18
|
|
|
19
19
|
## How It Works
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
`tasty()` components are hook-free and use `computeStyles()` internally — a synchronous, framework-agnostic function. On the server, `computeStyles()` detects a `ServerStyleCollector` (via `AsyncLocalStorage` or an explicit option) and collects CSS into it instead of trying to access the DOM. On the client, CSS is injected synchronously into the DOM during render; the injector's content-based cache makes this idempotent. The collector accumulates all styles, serializes them as `<style>` tags and a cache state script in the HTML. On the client, `hydrateTastyCache()` pre-populates the injector cache so that `computeStyles()` skips the rendering pipeline entirely during hydration.
|
|
22
22
|
|
|
23
23
|
```
|
|
24
24
|
Server Client
|
|
25
25
|
────── ──────
|
|
26
26
|
tasty() renders hydrateTastyCache() pre-populates cache
|
|
27
|
-
└─
|
|
27
|
+
└─ computeStyles() └─ cacheKey → className map ready
|
|
28
28
|
└─ collector.collect()
|
|
29
29
|
tasty() renders
|
|
30
|
-
After render: └─
|
|
30
|
+
After render: └─ computeStyles()
|
|
31
31
|
<style data-tasty-ssr> └─ cache hit → skip pipeline
|
|
32
32
|
<script data-tasty-cache> └─ no CSS re-injection
|
|
33
33
|
```
|
|
@@ -83,14 +83,14 @@ That's it. All `tasty()` components inside the tree automatically get SSR suppor
|
|
|
83
83
|
### How it works
|
|
84
84
|
|
|
85
85
|
- `TastyRegistry` is a `'use client'` component, but Next.js still server-renders it on initial page load.
|
|
86
|
-
- During SSR, `
|
|
86
|
+
- During SSR, `computeStyles()` finds the collector (registered globally by `TastyRegistry`) and collects CSS rules into it.
|
|
87
87
|
- `TastyRegistry` uses `useServerInsertedHTML` to flush collected CSS into the HTML stream as `<style data-tasty-ssr>` tags. This is fully streaming-compatible -- styles are injected alongside each Suspense boundary as it resolves.
|
|
88
88
|
- A companion `<script>` tag transfers the `cacheKey → className` mapping to the client.
|
|
89
|
-
- When the module loads on the client, `hydrateTastyCache()` runs automatically and pre-populates the injector cache. During hydration, `
|
|
89
|
+
- When the module loads on the client, `hydrateTastyCache()` runs automatically and pre-populates the injector cache. During hydration, `computeStyles()` hits the cache and skips the entire pipeline.
|
|
90
90
|
|
|
91
91
|
### Using tasty() in Server Components
|
|
92
92
|
|
|
93
|
-
`tasty()` components
|
|
93
|
+
`tasty()` components are hook-free and do not require `'use client'`. They can be used directly in React Server Components. Dynamic `styleProps` like `<Grid flow="column">` work normally in server components. During SSR, the `TastyRegistry` registers its collector globally so that `computeStyles()` can discover it without React context.
|
|
94
94
|
|
|
95
95
|
### Options
|
|
96
96
|
|
|
@@ -161,10 +161,10 @@ import Card from '../components/Card.tsx';
|
|
|
161
161
|
|
|
162
162
|
### How it works
|
|
163
163
|
|
|
164
|
-
Astro's `@astrojs/react` renderer calls `renderToString()` for each React component without wrapping the tree in a provider. The middleware uses `AsyncLocalStorage` to make the collector available to all `
|
|
164
|
+
Astro's `@astrojs/react` renderer calls `renderToString()` for each React component without wrapping the tree in a provider. The middleware uses `AsyncLocalStorage` to make the collector available to all `computeStyles()` calls within the request.
|
|
165
165
|
|
|
166
166
|
- **Static components** (no `client:*`): Styles are collected during `renderToString` and injected into `</head>`. No JavaScript is shipped for these components.
|
|
167
|
-
- **Islands** (`client:load`, `client:visible`, etc.): Styles are collected during SSR the same way. On the client, importing `@tenphi/tasty/ssr/astro` auto-hydrates the cache from `<script data-tasty-cache>`. The island's `
|
|
167
|
+
- **Islands** (`client:load`, `client:visible`, etc.): Styles are collected during SSR the same way. On the client, importing `@tenphi/tasty/ssr/astro` auto-hydrates the cache from `<script data-tasty-cache>`. The island's `computeStyles()` calls hit the cache during hydration.
|
|
168
168
|
|
|
169
169
|
### Client-side hydration for islands
|
|
170
170
|
|
|
@@ -325,7 +325,7 @@ Server-safe style collector. One instance per request.
|
|
|
325
325
|
|
|
326
326
|
### `TastySSRContext`
|
|
327
327
|
|
|
328
|
-
React context (`createContext<ServerStyleCollector | null>(null)`). Used by `useStyles()` to find the collector during SSR.
|
|
328
|
+
React context (`createContext<ServerStyleCollector | null>(null)`). Used by the `useStyles()` hook to find the collector during SSR. Not needed when using `computeStyles()` directly (which discovers the collector via `AsyncLocalStorage` or the global getter registered by `TastyRegistry`).
|
|
329
329
|
|
|
330
330
|
### `TastyRegistry`
|
|
331
331
|
|
|
@@ -350,7 +350,7 @@ Pre-populate the client injector cache. When called without arguments, reads fro
|
|
|
350
350
|
|
|
351
351
|
### `runWithCollector(collector, fn)`
|
|
352
352
|
|
|
353
|
-
Run a function with a `ServerStyleCollector` bound to the current async context via `AsyncLocalStorage`. All `useStyles()` calls within `fn` (and async continuations) will find this collector.
|
|
353
|
+
Run a function with a `ServerStyleCollector` bound to the current async context via `AsyncLocalStorage`. All `computeStyles()` and `useStyles()` calls within `fn` (and async continuations) will find this collector.
|
|
354
354
|
|
|
355
355
|
---
|
|
356
356
|
|
package/docs/styles.md
CHANGED
|
@@ -104,6 +104,8 @@ Element padding with directional modifiers and multi-group support. Use **comma-
|
|
|
104
104
|
|
|
105
105
|
**Direction modifiers:** `top`, `right`, `bottom`, `left`
|
|
106
106
|
|
|
107
|
+
**Output modifier:** `longhand` — forces output as individual CSS longhand properties (`padding-top`, `padding-right`, `padding-bottom`, `padding-left`) instead of the `padding` shorthand. Useful when children need to selectively inherit individual directions.
|
|
108
|
+
|
|
107
109
|
| Value | Effect |
|
|
108
110
|
|-------|--------|
|
|
109
111
|
| `"2x"` | All sides `2x` |
|
|
@@ -112,6 +114,7 @@ Element padding with directional modifiers and multi-group support. Use **comma-
|
|
|
112
114
|
| `"1x left right"` | Left and right `1x`, top/bottom `0` |
|
|
113
115
|
| `"1x, 2x top"` | All sides `1x`, then top overridden to `2x` |
|
|
114
116
|
| `"1x, 2x top bottom"` | Left/right `1x`, top/bottom `2x` |
|
|
117
|
+
| `"2x longhand"` | All sides `2x`, output as 4 individual `padding-*` properties |
|
|
115
118
|
| `true` | All sides `1x` |
|
|
116
119
|
| Number | Converted to `px` |
|
|
117
120
|
|
|
@@ -129,6 +132,8 @@ Element margin. Same syntax, modifiers, and multi-group support as `padding`.
|
|
|
129
132
|
|
|
130
133
|
**Direction modifiers:** `top`, `right`, `bottom`, `left`
|
|
131
134
|
|
|
135
|
+
**Output modifier:** `longhand` — forces output as individual CSS longhand properties (`margin-top`, etc.) instead of the `margin` shorthand.
|
|
136
|
+
|
|
132
137
|
| Value | Effect |
|
|
133
138
|
|-------|--------|
|
|
134
139
|
| `"2x"` | All sides `2x` |
|
|
@@ -190,12 +195,15 @@ Positioning offsets with directional modifiers and multi-group support. Same dir
|
|
|
190
195
|
|
|
191
196
|
**Direction modifiers:** `top`, `right`, `bottom`, `left`
|
|
192
197
|
|
|
198
|
+
**Output modifier:** `longhand` — forces output as individual CSS properties (`top`, `right`, `bottom`, `left`) instead of the `inset` shorthand.
|
|
199
|
+
|
|
193
200
|
| Value | Effect |
|
|
194
201
|
|-------|--------|
|
|
195
202
|
| `"0"` | All sides `0` |
|
|
196
203
|
| `"2x top"` | Top `2x`, right/bottom/left `auto` |
|
|
197
204
|
| `"1x left right"` | Left and right `1x`, top/bottom `auto` |
|
|
198
205
|
| `"0, 2x top"` | All sides `0`, then top overridden to `2x` |
|
|
206
|
+
| `"0 longhand"` | All sides `0`, output as individual `top`/`right`/`bottom`/`left` |
|
|
199
207
|
| `true` | All sides `0` |
|
|
200
208
|
|
|
201
209
|
Later comma-separated groups override earlier groups for conflicting directions.
|
|
@@ -281,12 +289,15 @@ Border shorthand with directional and multi-group support. Use **comma-separated
|
|
|
281
289
|
|
|
282
290
|
**Direction modifiers:** `top`, `right`, `bottom`, `left`
|
|
283
291
|
|
|
292
|
+
**Output modifier:** `longhand` — forces output as individual CSS properties (`border-top`, `border-right`, `border-bottom`, `border-left`) instead of the `border` shorthand. Useful when children need to selectively inherit individual sides.
|
|
293
|
+
|
|
284
294
|
| Value | Effect |
|
|
285
295
|
|-------|--------|
|
|
286
296
|
| `true` | Default border (`1bw solid #border`) on all sides |
|
|
287
297
|
| `"2bw dashed #purple"` | All sides: 2bw dashed purple |
|
|
288
298
|
| `"2bw top"` | Top only: 2bw solid `#border`, others: 0 |
|
|
289
299
|
| `"dotted #danger left right"` | Left/right: 1bw dotted `#danger`, others: 0 |
|
|
300
|
+
| `"1bw longhand"` | All sides: 1bw solid `#border`, output as 4 individual `border-*` properties |
|
|
290
301
|
| `"1bw #red, 2bw #blue top"` | All sides: 1bw solid `#red`, top overridden to 2bw solid `#blue` |
|
|
291
302
|
| `"1bw, dashed top bottom, #purple left right"` | Base: 1bw solid `#border`, top/bottom: 1bw dashed `#border`, left/right: 1bw solid `#purple` |
|
|
292
303
|
|
|
@@ -306,9 +317,12 @@ Border radius with shape presets and directional modifiers.
|
|
|
306
317
|
| `"ellipse"` | Circular (`50%`) |
|
|
307
318
|
| `"leaf"` | Asymmetric: sharp, round, sharp, round |
|
|
308
319
|
| `"backleaf"` | Asymmetric: round, sharp, round, sharp |
|
|
320
|
+
| `"inherit"` | Inherit border-radius from parent |
|
|
309
321
|
|
|
310
322
|
**Direction modifiers:** `top`, `right`, `bottom`, `left` — rounds only the specified corners.
|
|
311
323
|
|
|
324
|
+
**Output modifier:** `longhand` — forces output as individual CSS longhand properties (`border-top-left-radius`, `border-top-right-radius`, `border-bottom-right-radius`, `border-bottom-left-radius`) instead of the `border-radius` shorthand. Useful when children need to selectively inherit individual corners via `radius: 'inherit left'`.
|
|
325
|
+
|
|
312
326
|
| Value | Effect |
|
|
313
327
|
|-------|--------|
|
|
314
328
|
| `"2r"` | All corners `2r` |
|
|
@@ -317,6 +331,9 @@ Border radius with shape presets and directional modifiers.
|
|
|
317
331
|
| `"1r top"` | Top-left and top-right `1r`, bottom-left and bottom-right `0` |
|
|
318
332
|
| `"leaf"` | Alternating sharp/round corners (top-left `0`, top-right `1r`, bottom-right `0`, bottom-left `1r`) |
|
|
319
333
|
| `"backleaf"` | Inverse leaf (top-left `1r`, top-right `0`, bottom-right `1r`, bottom-left `0`) |
|
|
334
|
+
| `"1r longhand"` | All corners `1r`, output as 4 individual `border-*-radius` properties |
|
|
335
|
+
| `"inherit"` | All corners inherit from parent (`border-radius: inherit`) |
|
|
336
|
+
| `"inherit right"` | Right corners inherit from parent (uses longhand properties) |
|
|
320
337
|
|
|
321
338
|
### `outline`
|
|
322
339
|
|
package/docs/tasty-static.md
CHANGED
|
@@ -157,6 +157,7 @@ module.exports = {
|
|
|
157
157
|
| Option | Type | Default | Description |
|
|
158
158
|
|--------|------|---------|-------------|
|
|
159
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)) |
|
|
160
161
|
| `configFile` | `string` | — | Absolute path to a TS/JS module that default-exports a `TastyZeroConfig` object. JSON-serializable alternative to `config` — required for Turbopack. |
|
|
161
162
|
| `config` | `TastyZeroConfig \| () => TastyZeroConfig` | `{}` | Inline config object or factory function. Takes precedence over `configFile`. |
|
|
162
163
|
| `configDeps` | `string[]` | `[]` | Absolute file paths that affect config (for cache invalidation) |
|
|
@@ -224,6 +225,7 @@ export default withTastyZero({
|
|
|
224
225
|
| Option | Type | Default | Description |
|
|
225
226
|
|--------|------|---------|-------------|
|
|
226
227
|
| `output` | `string` | `'public/tasty.css'` | Output path for CSS relative to project root |
|
|
228
|
+
| `mode` | `'file' \| 'inject'` | `'file'` | `'file'` writes CSS to disk; `'inject'` embeds CSS inline in JS |
|
|
227
229
|
| `enabled` | `boolean` | `true` | Enable/disable the plugin |
|
|
228
230
|
| `configFile` | `string` | — | Path to a TS/JS module that default-exports `TastyZeroConfig`. Recommended for Turbopack compatibility. |
|
|
229
231
|
| `config` | `TastyZeroConfig` | — | Inline config object. For static configs that don't change during dev. |
|
|
@@ -333,6 +335,91 @@ export const Button = () => <button className={button}>Click</button>;
|
|
|
333
335
|
|
|
334
336
|
---
|
|
335
337
|
|
|
338
|
+
## Inject Mode
|
|
339
|
+
|
|
340
|
+
By default the Babel plugin writes CSS to a file (`mode: 'file'`). **Inject mode** (`mode: 'inject'`) embeds CSS inline in your JavaScript and injects it at runtime via a tiny injector. No CSS file is produced.
|
|
341
|
+
|
|
342
|
+
This is ideal for **reusable components**, **extensions**, and **libraries** where consumers shouldn't need to manage an external CSS file.
|
|
343
|
+
|
|
344
|
+
### How It Works
|
|
345
|
+
|
|
346
|
+
1. The Babel plugin extracts CSS at build time (same pipeline as file mode).
|
|
347
|
+
2. Instead of writing to a `.css` file, the CSS is embedded as string literals in the JS output.
|
|
348
|
+
3. The `@tenphi/tasty/static` import is rewritten to `@tenphi/tasty/static/inject`.
|
|
349
|
+
4. Each `tastyStatic` call becomes a self-contained expression that injects its CSS and evaluates to a `StaticStyle` object.
|
|
350
|
+
|
|
351
|
+
### Configuration
|
|
352
|
+
|
|
353
|
+
```javascript
|
|
354
|
+
// babel.config.js
|
|
355
|
+
module.exports = {
|
|
356
|
+
plugins: [
|
|
357
|
+
['@tenphi/tasty/babel-plugin', {
|
|
358
|
+
mode: 'inject',
|
|
359
|
+
config: {
|
|
360
|
+
states: { '@mobile': '@media(w < 768px)' },
|
|
361
|
+
},
|
|
362
|
+
}]
|
|
363
|
+
]
|
|
364
|
+
};
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
With Next.js:
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
// next.config.ts
|
|
371
|
+
import { withTastyZero } from '@tenphi/tasty/next';
|
|
372
|
+
|
|
373
|
+
export default withTastyZero({
|
|
374
|
+
mode: 'inject',
|
|
375
|
+
configFile: './app/tasty-zero.config.ts',
|
|
376
|
+
})({
|
|
377
|
+
reactStrictMode: true,
|
|
378
|
+
});
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
When `mode` is `'inject'`, the `output` and `injectImport` options are ignored.
|
|
382
|
+
|
|
383
|
+
### Build Transformation (inject mode)
|
|
384
|
+
|
|
385
|
+
**Before:**
|
|
386
|
+
|
|
387
|
+
```tsx
|
|
388
|
+
import { tastyStatic } from '@tenphi/tasty/static';
|
|
389
|
+
|
|
390
|
+
const button = tastyStatic({
|
|
391
|
+
padding: '2x 4x',
|
|
392
|
+
fill: '#purple',
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
tastyStatic('.heading', { preset: 'h1' });
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
**After:**
|
|
399
|
+
|
|
400
|
+
```tsx
|
|
401
|
+
import { injectCSS as _$i } from '@tenphi/tasty/static/inject';
|
|
402
|
+
|
|
403
|
+
const button = (_$i("ts3f2a1b ts8c4d2e", ".ts3f2a1b.ts3f2a1b{padding:16px 32px}\n.ts8c4d2e.ts8c4d2e{background:#9370db}"), {
|
|
404
|
+
className: 'ts3f2a1b ts8c4d2e',
|
|
405
|
+
styles: { padding: '2x 4x', fill: '#purple' },
|
|
406
|
+
toString() { return this.className; }
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
_$i(".heading", ".heading{font-size:2.5rem;font-weight:700;line-height:1.2}");
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### Dev Mode / HMR
|
|
413
|
+
|
|
414
|
+
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.
|
|
415
|
+
|
|
416
|
+
### Limitations (inject mode)
|
|
417
|
+
|
|
418
|
+
- **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()`.
|
|
419
|
+
- **Larger JS bundle** — CSS is embedded in JavaScript, increasing bundle size. Best suited for components and extensions, not full-app styling.
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
336
423
|
## Style Extension
|
|
337
424
|
|
|
338
425
|
```tsx
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tenphi/tasty",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.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",
|
|
@@ -17,6 +17,11 @@
|
|
|
17
17
|
"import": "./dist/static/index.js",
|
|
18
18
|
"default": "./dist/static/index.js"
|
|
19
19
|
},
|
|
20
|
+
"./static/inject": {
|
|
21
|
+
"types": "./dist/static/inject.d.ts",
|
|
22
|
+
"import": "./dist/static/inject.js",
|
|
23
|
+
"default": "./dist/static/inject.js"
|
|
24
|
+
},
|
|
20
25
|
"./babel-plugin": {
|
|
21
26
|
"types": "./dist/zero/babel.d.ts",
|
|
22
27
|
"import": "./dist/zero/babel.js",
|
|
@@ -117,7 +122,7 @@
|
|
|
117
122
|
},
|
|
118
123
|
"devDependencies": {
|
|
119
124
|
"@babel/core": "^7.24.0",
|
|
120
|
-
"@changesets/changelog-github": "^0.
|
|
125
|
+
"@changesets/changelog-github": "^0.6.0",
|
|
121
126
|
"@changesets/cli": "^2.29.8",
|
|
122
127
|
"@eslint/js": "^10.0.1",
|
|
123
128
|
"@size-limit/esbuild": "^12.0.0",
|
|
@@ -131,11 +136,11 @@
|
|
|
131
136
|
"@vitest/runner": "^4.0.18",
|
|
132
137
|
"eslint": "^10.0.0",
|
|
133
138
|
"eslint-config-prettier": "^10.1.8",
|
|
134
|
-
"jsdom": "^
|
|
139
|
+
"jsdom": "^29.0.1",
|
|
135
140
|
"prettier": "^3.8.1",
|
|
136
141
|
"react": "^19.0.0",
|
|
137
142
|
"size-limit": "^12.0.0",
|
|
138
|
-
"tsdown": "^0.
|
|
143
|
+
"tsdown": "^0.21.7",
|
|
139
144
|
"typescript": "^5.9.3",
|
|
140
145
|
"typescript-eslint": "^8.56.0",
|
|
141
146
|
"vitest": "^4.0.18"
|
|
@@ -145,7 +150,7 @@
|
|
|
145
150
|
"name": "main (import *)",
|
|
146
151
|
"path": "dist/index.js",
|
|
147
152
|
"import": "*",
|
|
148
|
-
"limit": "
|
|
153
|
+
"limit": "48 kB"
|
|
149
154
|
},
|
|
150
155
|
{
|
|
151
156
|
"name": "core (import *)",
|
|
@@ -168,7 +173,7 @@
|
|
|
168
173
|
"path",
|
|
169
174
|
"crypto"
|
|
170
175
|
],
|
|
171
|
-
"limit": "28 kB"
|
|
176
|
+
"limit": "28.5 kB"
|
|
172
177
|
},
|
|
173
178
|
{
|
|
174
179
|
"name": "babel-plugin",
|
|
@@ -179,7 +184,7 @@
|
|
|
179
184
|
"path",
|
|
180
185
|
"crypto"
|
|
181
186
|
],
|
|
182
|
-
"limit": "
|
|
187
|
+
"limit": "41 kB"
|
|
183
188
|
}
|
|
184
189
|
],
|
|
185
190
|
"scripts": {
|
package/dist/styles/align.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
//#region src/styles/align.d.ts
|
|
2
|
-
declare function alignStyle({
|
|
3
|
-
align
|
|
4
|
-
}: {
|
|
5
|
-
align?: string;
|
|
6
|
-
}): {
|
|
7
|
-
'align-items': string;
|
|
8
|
-
'align-content': string;
|
|
9
|
-
} | undefined;
|
|
10
|
-
declare namespace alignStyle {
|
|
11
|
-
var __lookupStyles: string[];
|
|
12
|
-
}
|
|
13
|
-
//#endregion
|
|
14
|
-
export { alignStyle };
|
|
15
|
-
//# sourceMappingURL=align.d.ts.map
|
package/dist/styles/align.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
//#region src/styles/align.ts
|
|
2
|
-
function alignStyle({ align }) {
|
|
3
|
-
if (typeof align !== "string") return;
|
|
4
|
-
if (!align) return;
|
|
5
|
-
return {
|
|
6
|
-
"align-items": align,
|
|
7
|
-
"align-content": align
|
|
8
|
-
};
|
|
9
|
-
}
|
|
10
|
-
alignStyle.__lookupStyles = ["align"];
|
|
11
|
-
|
|
12
|
-
//#endregion
|
|
13
|
-
export { alignStyle };
|
|
14
|
-
//# sourceMappingURL=align.js.map
|
package/dist/styles/align.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"align.js","names":[],"sources":["../../src/styles/align.ts"],"sourcesContent":["export function alignStyle({ align }: { align?: string }) {\n if (typeof align !== 'string') return;\n\n if (!align) return;\n\n return {\n 'align-items': align,\n 'align-content': align,\n };\n}\n\nalignStyle.__lookupStyles = ['align'];\n"],"mappings":";AAAA,SAAgB,WAAW,EAAE,SAA6B;AACxD,KAAI,OAAO,UAAU,SAAU;AAE/B,KAAI,CAAC,MAAO;AAEZ,QAAO;EACL,eAAe;EACf,iBAAiB;EAClB;;AAGH,WAAW,iBAAiB,CAAC,QAAQ"}
|
package/dist/styles/justify.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
//#region src/styles/justify.d.ts
|
|
2
|
-
declare function justifyStyle({
|
|
3
|
-
justify
|
|
4
|
-
}: {
|
|
5
|
-
justify?: string;
|
|
6
|
-
}): {
|
|
7
|
-
'justify-items': string;
|
|
8
|
-
'justify-content': string;
|
|
9
|
-
} | undefined;
|
|
10
|
-
declare namespace justifyStyle {
|
|
11
|
-
var __lookupStyles: string[];
|
|
12
|
-
}
|
|
13
|
-
//#endregion
|
|
14
|
-
export { justifyStyle };
|
|
15
|
-
//# sourceMappingURL=justify.d.ts.map
|
package/dist/styles/justify.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
//#region src/styles/justify.ts
|
|
2
|
-
function justifyStyle({ justify }) {
|
|
3
|
-
if (typeof justify !== "string") return;
|
|
4
|
-
if (!justify) return;
|
|
5
|
-
return {
|
|
6
|
-
"justify-items": justify,
|
|
7
|
-
"justify-content": justify
|
|
8
|
-
};
|
|
9
|
-
}
|
|
10
|
-
justifyStyle.__lookupStyles = ["justify"];
|
|
11
|
-
|
|
12
|
-
//#endregion
|
|
13
|
-
export { justifyStyle };
|
|
14
|
-
//# sourceMappingURL=justify.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"justify.js","names":[],"sources":["../../src/styles/justify.ts"],"sourcesContent":["export function justifyStyle({ justify }: { justify?: string }) {\n if (typeof justify !== 'string') return;\n\n if (!justify) return;\n\n return {\n 'justify-items': justify,\n 'justify-content': justify,\n };\n}\n\njustifyStyle.__lookupStyles = ['justify'];\n"],"mappings":";AAAA,SAAgB,aAAa,EAAE,WAAiC;AAC9D,KAAI,OAAO,YAAY,SAAU;AAEjC,KAAI,CAAC,QAAS;AAEd,QAAO;EACL,iBAAiB;EACjB,mBAAmB;EACpB;;AAGH,aAAa,iBAAiB,CAAC,UAAU"}
|