@tenphi/tasty 0.0.0-snapshot.d2dcdeb → 0.0.0-snapshot.d4ccea8
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 +181 -131
- package/dist/{ssr/async-storage.js → async-storage-DKK-wTD4.js} +2 -2
- package/dist/async-storage-DKK-wTD4.js.map +1 -0
- package/dist/babel-eMs_zZjM.d.ts +83 -0
- package/dist/{ssr/collector.js → collector-BjkQEtbh.js} +111 -38
- package/dist/collector-BjkQEtbh.js.map +1 -0
- package/dist/{ssr/collector.d.ts → collector-DzeNxHOq.d.ts} +58 -7
- package/dist/config-DMslHK_e.d.ts +1298 -0
- package/dist/config-DX126v_w.js +12318 -0
- package/dist/config-DX126v_w.js.map +1 -0
- package/dist/{ssr/context.js → context-CA8YKeMn.js} +11 -3
- package/dist/context-CA8YKeMn.js.map +1 -0
- package/dist/core/index.d.ts +5 -34
- package/dist/core/index.js +6 -27
- package/dist/core-CMn-Tj2X.js +1573 -0
- package/dist/core-CMn-Tj2X.js.map +1 -0
- package/dist/{zero/extractor.js → css-writer-C8ShXK5R.js} +148 -24
- package/dist/css-writer-C8ShXK5R.js.map +1 -0
- package/dist/{ssr/format-global-rules.js → format-global-rules-DklyaXv-.js} +2 -2
- package/dist/format-global-rules-DklyaXv-.js.map +1 -0
- package/dist/{ssr/format-rules.js → format-rules-BkstDvlN.js} +59 -2
- package/dist/format-rules-BkstDvlN.js.map +1 -0
- package/dist/{ssr/hydrate.js → hydrate-DXXWZgm0.js} +4 -12
- package/dist/hydrate-DXXWZgm0.js.map +1 -0
- package/dist/index-BcsaHo8x.d.ts +1568 -0
- package/dist/index-CC0wykKn.d.ts +1909 -0
- package/dist/index.d.ts +5 -51
- package/dist/index.js +922 -36
- package/dist/index.js.map +1 -0
- package/dist/keyframes-Z0YLVOWq.js +493 -0
- package/dist/keyframes-Z0YLVOWq.js.map +1 -0
- package/dist/{utils/merge-styles.d.ts → merge-styles-BTFAkYSD.d.ts} +3 -3
- package/dist/{utils/merge-styles.js → merge-styles-Dpueyn82.js} +3 -4
- package/dist/merge-styles-Dpueyn82.js.map +1 -0
- package/dist/{utils/resolve-recipes.js → resolve-recipes-B2Vjr7ET.js} +4 -6
- package/dist/resolve-recipes-B2Vjr7ET.js.map +1 -0
- package/dist/ssr/astro-client.js +1 -1
- package/dist/ssr/astro-client.js.map +1 -1
- package/dist/ssr/astro-middleware-static.d.ts +16 -0
- package/dist/ssr/astro-middleware-static.js +18 -0
- package/dist/ssr/astro-middleware-static.js.map +1 -0
- package/dist/ssr/astro-middleware.d.ts +8 -6
- package/dist/ssr/astro-middleware.js +10 -10
- package/dist/ssr/astro-middleware.js.map +1 -1
- package/dist/ssr/astro.d.ts +2 -17
- package/dist/ssr/astro.js +29 -21
- package/dist/ssr/astro.js.map +1 -1
- package/dist/ssr/index.d.ts +37 -4
- package/dist/ssr/index.js +5 -5
- package/dist/ssr/index.js.map +1 -1
- package/dist/ssr/next.d.ts +2 -3
- package/dist/ssr/next.js +7 -8
- package/dist/ssr/next.js.map +1 -1
- package/dist/static/index.d.ts +91 -5
- package/dist/static/index.js +49 -3
- package/dist/static/index.js.map +1 -0
- package/dist/static/inject.js.map +1 -1
- package/dist/zero/babel.d.ts +2 -81
- package/dist/zero/babel.js +45 -29
- package/dist/zero/babel.js.map +1 -1
- package/dist/zero/index.d.ts +81 -3
- package/dist/zero/index.js +2 -3
- package/dist/zero/next.d.ts +1 -1
- package/dist/zero/next.js +1 -1
- package/dist/zero/next.js.map +1 -1
- package/docs/README.md +18 -10
- package/docs/adoption.md +56 -31
- package/docs/ai-agents.md +224 -0
- package/docs/comparison.md +69 -40
- package/docs/configuration.md +501 -72
- package/docs/debug.md +22 -18
- package/docs/design-system.md +43 -24
- package/docs/dsl.md +512 -179
- package/docs/getting-started.md +60 -39
- package/docs/injector.md +110 -32
- package/docs/methodology.md +68 -36
- package/docs/migration-v3.md +285 -0
- package/docs/pipeline.md +741 -0
- package/docs/plugins.md +353 -0
- package/docs/react-api.md +164 -40
- package/docs/runtime-benchmarks.md +216 -0
- package/docs/ssr.md +92 -81
- package/docs/styles.md +65 -16
- package/docs/tasty-static.md +149 -99
- package/package.json +49 -21
- package/tasty.config.ts +6 -0
- package/dist/_virtual/_rolldown/runtime.js +0 -7
- package/dist/chunks/cacheKey.d.ts +0 -1
- package/dist/chunks/cacheKey.js +0 -77
- package/dist/chunks/cacheKey.js.map +0 -1
- package/dist/chunks/definitions.d.ts +0 -37
- package/dist/chunks/definitions.js +0 -258
- package/dist/chunks/definitions.js.map +0 -1
- package/dist/chunks/index.d.ts +0 -1
- package/dist/chunks/renderChunk.d.ts +0 -1
- package/dist/chunks/renderChunk.js +0 -59
- package/dist/chunks/renderChunk.js.map +0 -1
- package/dist/compute-styles.d.ts +0 -31
- package/dist/compute-styles.js +0 -335
- package/dist/compute-styles.js.map +0 -1
- package/dist/config.d.ts +0 -407
- package/dist/config.js +0 -587
- package/dist/config.js.map +0 -1
- package/dist/counter-style/index.js +0 -51
- package/dist/counter-style/index.js.map +0 -1
- package/dist/debug.d.ts +0 -89
- package/dist/debug.js +0 -453
- package/dist/debug.js.map +0 -1
- package/dist/font-face/index.js +0 -63
- package/dist/font-face/index.js.map +0 -1
- package/dist/hooks/index.d.ts +0 -7
- package/dist/hooks/useCounterStyle.d.ts +0 -36
- package/dist/hooks/useCounterStyle.js +0 -65
- package/dist/hooks/useCounterStyle.js.map +0 -1
- package/dist/hooks/useFontFace.d.ts +0 -45
- package/dist/hooks/useFontFace.js +0 -66
- package/dist/hooks/useFontFace.js.map +0 -1
- package/dist/hooks/useGlobalStyles.d.ts +0 -46
- package/dist/hooks/useGlobalStyles.js +0 -88
- package/dist/hooks/useGlobalStyles.js.map +0 -1
- package/dist/hooks/useKeyframes.d.ts +0 -58
- package/dist/hooks/useKeyframes.js +0 -55
- package/dist/hooks/useKeyframes.js.map +0 -1
- package/dist/hooks/useProperty.d.ts +0 -81
- package/dist/hooks/useProperty.js +0 -96
- package/dist/hooks/useProperty.js.map +0 -1
- package/dist/hooks/useRawCSS.d.ts +0 -22
- package/dist/hooks/useRawCSS.js +0 -103
- package/dist/hooks/useRawCSS.js.map +0 -1
- package/dist/hooks/useStyles.d.ts +0 -40
- package/dist/hooks/useStyles.js +0 -31
- package/dist/hooks/useStyles.js.map +0 -1
- package/dist/injector/index.d.ts +0 -182
- package/dist/injector/index.js +0 -185
- package/dist/injector/index.js.map +0 -1
- package/dist/injector/injector.d.ts +0 -198
- package/dist/injector/injector.js +0 -651
- package/dist/injector/injector.js.map +0 -1
- package/dist/injector/sheet-manager.d.ts +0 -132
- package/dist/injector/sheet-manager.js +0 -699
- package/dist/injector/sheet-manager.js.map +0 -1
- package/dist/injector/types.d.ts +0 -235
- package/dist/keyframes/index.js +0 -206
- package/dist/keyframes/index.js.map +0 -1
- package/dist/parser/classify.js +0 -319
- package/dist/parser/classify.js.map +0 -1
- package/dist/parser/const.js +0 -60
- package/dist/parser/const.js.map +0 -1
- package/dist/parser/lru.js +0 -109
- package/dist/parser/lru.js.map +0 -1
- package/dist/parser/parser.d.ts +0 -25
- package/dist/parser/parser.js +0 -115
- package/dist/parser/parser.js.map +0 -1
- package/dist/parser/tokenizer.js +0 -69
- package/dist/parser/tokenizer.js.map +0 -1
- package/dist/parser/types.d.ts +0 -51
- package/dist/parser/types.js +0 -46
- package/dist/parser/types.js.map +0 -1
- package/dist/pipeline/conditions.d.ts +0 -134
- package/dist/pipeline/conditions.js +0 -406
- package/dist/pipeline/conditions.js.map +0 -1
- package/dist/pipeline/exclusive.js +0 -230
- package/dist/pipeline/exclusive.js.map +0 -1
- package/dist/pipeline/index.d.ts +0 -55
- package/dist/pipeline/index.js +0 -708
- package/dist/pipeline/index.js.map +0 -1
- package/dist/pipeline/materialize.js +0 -1103
- package/dist/pipeline/materialize.js.map +0 -1
- package/dist/pipeline/parseStateKey.d.ts +0 -15
- package/dist/pipeline/parseStateKey.js +0 -446
- package/dist/pipeline/parseStateKey.js.map +0 -1
- package/dist/pipeline/simplify.js +0 -515
- package/dist/pipeline/simplify.js.map +0 -1
- package/dist/pipeline/warnings.js +0 -18
- package/dist/pipeline/warnings.js.map +0 -1
- package/dist/plugins/index.d.ts +0 -2
- package/dist/plugins/okhsl-plugin.d.ts +0 -35
- package/dist/plugins/okhsl-plugin.js +0 -97
- package/dist/plugins/okhsl-plugin.js.map +0 -1
- package/dist/plugins/types.d.ts +0 -87
- package/dist/properties/index.js +0 -222
- package/dist/properties/index.js.map +0 -1
- package/dist/properties/property-type-resolver.d.ts +0 -24
- package/dist/properties/property-type-resolver.js +0 -90
- package/dist/properties/property-type-resolver.js.map +0 -1
- package/dist/rsc-cache.js +0 -79
- package/dist/rsc-cache.js.map +0 -1
- package/dist/ssr/async-storage.d.ts +0 -17
- package/dist/ssr/async-storage.js.map +0 -1
- package/dist/ssr/collect-auto-properties.js +0 -58
- package/dist/ssr/collect-auto-properties.js.map +0 -1
- package/dist/ssr/collector.js.map +0 -1
- package/dist/ssr/context.js.map +0 -1
- package/dist/ssr/format-global-rules.js.map +0 -1
- package/dist/ssr/format-keyframes.js +0 -69
- package/dist/ssr/format-keyframes.js.map +0 -1
- package/dist/ssr/format-property.js +0 -49
- package/dist/ssr/format-property.js.map +0 -1
- package/dist/ssr/format-rules.js.map +0 -1
- package/dist/ssr/hydrate.d.ts +0 -29
- package/dist/ssr/hydrate.js.map +0 -1
- package/dist/ssr/ssr-collector-ref.js +0 -29
- package/dist/ssr/ssr-collector-ref.js.map +0 -1
- package/dist/states/index.d.ts +0 -49
- package/dist/states/index.js +0 -170
- package/dist/states/index.js.map +0 -1
- package/dist/static/tastyStatic.d.ts +0 -46
- package/dist/static/tastyStatic.js +0 -30
- package/dist/static/tastyStatic.js.map +0 -1
- package/dist/static/types.d.ts +0 -49
- package/dist/static/types.js +0 -24
- package/dist/static/types.js.map +0 -1
- package/dist/styles/border.d.ts +0 -25
- package/dist/styles/border.js +0 -120
- package/dist/styles/border.js.map +0 -1
- package/dist/styles/color.d.ts +0 -14
- package/dist/styles/color.js +0 -26
- package/dist/styles/color.js.map +0 -1
- package/dist/styles/const.js +0 -17
- package/dist/styles/const.js.map +0 -1
- package/dist/styles/createStyle.js +0 -79
- package/dist/styles/createStyle.js.map +0 -1
- package/dist/styles/dimension.js +0 -109
- package/dist/styles/dimension.js.map +0 -1
- package/dist/styles/directional.js +0 -133
- package/dist/styles/directional.js.map +0 -1
- package/dist/styles/display.d.ts +0 -30
- package/dist/styles/display.js +0 -73
- package/dist/styles/display.js.map +0 -1
- package/dist/styles/fade.d.ts +0 -15
- package/dist/styles/fade.js +0 -62
- package/dist/styles/fade.js.map +0 -1
- package/dist/styles/fill.d.ts +0 -42
- package/dist/styles/fill.js +0 -51
- package/dist/styles/fill.js.map +0 -1
- package/dist/styles/flow.d.ts +0 -16
- package/dist/styles/flow.js +0 -12
- package/dist/styles/flow.js.map +0 -1
- package/dist/styles/gap.d.ts +0 -31
- package/dist/styles/gap.js +0 -38
- package/dist/styles/gap.js.map +0 -1
- package/dist/styles/height.d.ts +0 -17
- package/dist/styles/height.js +0 -19
- package/dist/styles/height.js.map +0 -1
- package/dist/styles/index.d.ts +0 -1
- package/dist/styles/index.js +0 -8
- package/dist/styles/index.js.map +0 -1
- package/dist/styles/inset.d.ts +0 -24
- package/dist/styles/inset.js +0 -34
- package/dist/styles/inset.js.map +0 -1
- package/dist/styles/list.d.ts +0 -16
- package/dist/styles/list.js +0 -100
- package/dist/styles/list.js.map +0 -1
- package/dist/styles/margin.d.ts +0 -24
- package/dist/styles/margin.js +0 -32
- package/dist/styles/margin.js.map +0 -1
- package/dist/styles/outline.d.ts +0 -29
- package/dist/styles/outline.js +0 -55
- package/dist/styles/outline.js.map +0 -1
- package/dist/styles/padding.d.ts +0 -24
- package/dist/styles/padding.js +0 -32
- package/dist/styles/padding.js.map +0 -1
- package/dist/styles/placement.d.ts +0 -37
- package/dist/styles/placement.js +0 -74
- package/dist/styles/placement.js.map +0 -1
- package/dist/styles/predefined.d.ts +0 -71
- package/dist/styles/predefined.js +0 -237
- package/dist/styles/predefined.js.map +0 -1
- package/dist/styles/preset.d.ts +0 -52
- package/dist/styles/preset.js +0 -127
- package/dist/styles/preset.js.map +0 -1
- package/dist/styles/radius.d.ts +0 -12
- package/dist/styles/radius.js +0 -83
- package/dist/styles/radius.js.map +0 -1
- package/dist/styles/scrollMargin.d.ts +0 -24
- package/dist/styles/scrollMargin.js +0 -32
- package/dist/styles/scrollMargin.js.map +0 -1
- package/dist/styles/scrollbar.d.ts +0 -25
- package/dist/styles/scrollbar.js +0 -51
- package/dist/styles/scrollbar.js.map +0 -1
- package/dist/styles/shadow.d.ts +0 -14
- package/dist/styles/shadow.js +0 -25
- package/dist/styles/shadow.js.map +0 -1
- package/dist/styles/shared.js +0 -17
- package/dist/styles/shared.js.map +0 -1
- package/dist/styles/transition.d.ts +0 -14
- package/dist/styles/transition.js +0 -159
- package/dist/styles/transition.js.map +0 -1
- package/dist/styles/types.d.ts +0 -564
- package/dist/styles/width.d.ts +0 -17
- package/dist/styles/width.js +0 -19
- package/dist/styles/width.js.map +0 -1
- package/dist/tasty.d.ts +0 -134
- package/dist/tasty.js +0 -248
- package/dist/tasty.js.map +0 -1
- package/dist/types.d.ts +0 -184
- package/dist/utils/cache-wrapper.js +0 -21
- package/dist/utils/cache-wrapper.js.map +0 -1
- package/dist/utils/case-converter.js +0 -8
- package/dist/utils/case-converter.js.map +0 -1
- package/dist/utils/color-math.d.ts +0 -46
- package/dist/utils/color-math.js +0 -749
- package/dist/utils/color-math.js.map +0 -1
- package/dist/utils/color-space.d.ts +0 -5
- package/dist/utils/color-space.js +0 -228
- package/dist/utils/color-space.js.map +0 -1
- package/dist/utils/colors.d.ts +0 -5
- package/dist/utils/colors.js +0 -10
- package/dist/utils/colors.js.map +0 -1
- package/dist/utils/css-types.d.ts +0 -7
- package/dist/utils/deps-equal.js +0 -15
- package/dist/utils/deps-equal.js.map +0 -1
- package/dist/utils/dotize.d.ts +0 -26
- package/dist/utils/dotize.js +0 -122
- package/dist/utils/dotize.js.map +0 -1
- package/dist/utils/filter-base-props.d.ts +0 -15
- package/dist/utils/filter-base-props.js +0 -45
- package/dist/utils/filter-base-props.js.map +0 -1
- package/dist/utils/get-display-name.d.ts +0 -7
- package/dist/utils/get-display-name.js +0 -10
- package/dist/utils/get-display-name.js.map +0 -1
- package/dist/utils/has-keys.js +0 -13
- package/dist/utils/has-keys.js.map +0 -1
- package/dist/utils/hash.js +0 -14
- package/dist/utils/hash.js.map +0 -1
- package/dist/utils/is-dev-env.js +0 -19
- package/dist/utils/is-dev-env.js.map +0 -1
- package/dist/utils/is-valid-element-type.js +0 -15
- package/dist/utils/is-valid-element-type.js.map +0 -1
- package/dist/utils/merge-styles.js.map +0 -1
- package/dist/utils/mod-attrs.d.ts +0 -6
- package/dist/utils/mod-attrs.js +0 -20
- package/dist/utils/mod-attrs.js.map +0 -1
- package/dist/utils/process-tokens.d.ts +0 -17
- package/dist/utils/process-tokens.js +0 -83
- package/dist/utils/process-tokens.js.map +0 -1
- package/dist/utils/resolve-recipes.d.ts +0 -17
- package/dist/utils/resolve-recipes.js.map +0 -1
- package/dist/utils/selector-transform.js +0 -32
- package/dist/utils/selector-transform.js.map +0 -1
- package/dist/utils/string.js +0 -8
- package/dist/utils/string.js.map +0 -1
- package/dist/utils/styles.d.ts +0 -99
- package/dist/utils/styles.js +0 -220
- package/dist/utils/styles.js.map +0 -1
- package/dist/utils/typography.d.ts +0 -58
- package/dist/utils/typography.js +0 -51
- package/dist/utils/typography.js.map +0 -1
- package/dist/utils/warnings.d.ts +0 -16
- package/dist/utils/warnings.js +0 -16
- package/dist/utils/warnings.js.map +0 -1
- package/dist/zero/css-writer.d.ts +0 -45
- package/dist/zero/css-writer.js +0 -73
- package/dist/zero/css-writer.js.map +0 -1
- package/dist/zero/extractor.d.ts +0 -24
- package/dist/zero/extractor.js.map +0 -1
- package/docs/PIPELINE.md +0 -519
package/docs/configuration.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Configuration
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Tasty works without upfront configuration. Use `configure()` when your app or design system is ready to define shared tokens, state aliases, units, recipes, presets, or extension points.
|
|
4
|
+
|
|
5
|
+
Call it **before any styles are generated** (before first render). For a guided setup, see [Getting Started](getting-started.md). For the higher-level docs map, see the [Docs Hub](README.md).
|
|
4
6
|
|
|
5
7
|
```jsx
|
|
6
8
|
import { configure } from '@tenphi/tasty';
|
|
@@ -26,12 +28,15 @@ configure({
|
|
|
26
28
|
custom: (n) => `${n * 10}px`, // Function-based unit
|
|
27
29
|
},
|
|
28
30
|
|
|
29
|
-
// Custom functions for
|
|
30
|
-
|
|
31
|
+
// Custom functions — a single map for both flavors, discriminated by value type:
|
|
32
|
+
functions: {
|
|
33
|
+
// Bare key + function value → parse-time function, called as `double(...)`
|
|
31
34
|
double: (groups) => {
|
|
32
35
|
const value = parseFloat(groups[0]?.output || '0');
|
|
33
36
|
return `${value * 2}px`;
|
|
34
37
|
},
|
|
38
|
+
// `$$name` key + object value → declarative CSS @function, called as `$$negative(...)`
|
|
39
|
+
$$negative: { args: ['$value'], result: '(-1 * $value)' },
|
|
35
40
|
},
|
|
36
41
|
});
|
|
37
42
|
```
|
|
@@ -42,45 +47,281 @@ These docs use `data-schema="dark"` in examples. If your app already standardize
|
|
|
42
47
|
|
|
43
48
|
## Options
|
|
44
49
|
|
|
45
|
-
| Option
|
|
46
|
-
|
|
47
|
-
| `nonce`
|
|
48
|
-
| `
|
|
49
|
-
| `
|
|
50
|
-
| `
|
|
51
|
-
| `
|
|
52
|
-
| `
|
|
53
|
-
| `
|
|
54
|
-
| `
|
|
55
|
-
| `
|
|
56
|
-
| `
|
|
57
|
-
| `
|
|
58
|
-
| `
|
|
59
|
-
| `
|
|
60
|
-
| `
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
63
|
-
| `
|
|
50
|
+
| Option | Type | Default | Description |
|
|
51
|
+
| -------------------- | ------------------------------------------------ | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
52
|
+
| `nonce` | `string` | - | CSP nonce for style elements |
|
|
53
|
+
| `maxRulesPerSheet` | `number` | `8192` | Maximum rules per injected stylesheet |
|
|
54
|
+
| `forceTextInjection` | `boolean` | auto (`true` in test envs) | Force text-node CSS injection instead of constructable stylesheets |
|
|
55
|
+
| `devMode` | `boolean` | auto | Enable development-mode features: performance metrics and debug info |
|
|
56
|
+
| `states` | `Record<string, string>` | - | Global state aliases for advanced state mapping |
|
|
57
|
+
| `parserCacheSize` | `number` | `1000` | Parser LRU cache size |
|
|
58
|
+
| `units` | `Record<string, string \| UnitHandler>` | Built-in | Custom units (merged with built-in). See [built-in units](dsl.md#built-in-units) |
|
|
59
|
+
| `functions` | `Record<string, FunctionDefinition \| Function>` | - | Custom functions (merged). Bare keys → parse functions; `$$name` keys → declarative CSS `@function` definitions |
|
|
60
|
+
| `handlers` | `Record<string, StyleHandlerDefinition>` | Built-in | Custom style handlers (replace built-in). See [Custom Style Handlers](#custom-style-handlers) |
|
|
61
|
+
| `propHandlers` | `Record<string, PropHandlerDefinition>` | - | Props middleware for every component — props in, props out. See [Props Middleware](#props-middleware) |
|
|
62
|
+
| `baseStyleProps` | `readonly string[]` | - | Style names exposed as props on **every** component. See [Base Style Props](#base-style-props) |
|
|
63
|
+
| `tokens` | `Record<string, value \| stateMap>` | - | Design tokens injected as `:root` CSS custom properties |
|
|
64
|
+
| `replaceTokens` | `Record<string, string \| number \| boolean>` | - | Parse-time token substitution (inline replacement). `boolean` is allowed for `#` color tokens |
|
|
65
|
+
| `keyframes` | `Record<string, KeyframesSteps>` | - | Global keyframes for animations |
|
|
66
|
+
| `properties` | `Record<string, PropertyDefinition>` | - | Global CSS @property definitions |
|
|
67
|
+
| `fontFaces` | `Record<string, FontFaceInput>` | - | Global @font-face definitions |
|
|
68
|
+
| `counterStyles` | `Record<string, CounterStyleDescriptors>` | - | Global @counter-style definitions |
|
|
69
|
+
| `polyfills` | `{ functions?: boolean }` | `{}` | Opt-in polyfills for not-yet-baseline features. `functions: true` inlines `@function` calls into plain CSS at parse time |
|
|
70
|
+
| `autoPropertyTypes` | `boolean` | `true` | Auto-infer and register `@property` types from values |
|
|
71
|
+
| `recipes` | `Record<string, RecipeStyles>` | - | Predefined style recipes (named style bundles) |
|
|
72
|
+
| `presets` | `Record<string, TypographyPreset>` | - | Typography presets — shorthand for `generateTypographyTokens()` |
|
|
73
|
+
| `globalStyles` | `Record<string, Styles>` | - | Global Tasty styles keyed by CSS selector |
|
|
74
|
+
| `plugins` | `TastyPlugin[]` | - | Plugins that bundle any of the above (processed in order; later override earlier, and direct config wins over all). See [Plugins](plugins.md) |
|
|
75
|
+
| `gc` | `GCConfig` | - | Garbage-collection tuning for unused styles (`{ touchInterval, capacity }`) |
|
|
76
|
+
| `batchInjection` | `boolean \| 'always'` | `false` | Defer stylesheet writes and apply them in one batch. See [Batched injection](#batched-injection) |
|
|
77
|
+
| `colorSpace` | `'rgb' \| 'hsl' \| 'oklch'` | - | **Deprecated** — no longer has any effect. See [Color space](#color-space) |
|
|
78
|
+
| `namePrefix` | `string` | `'t'` (`tasty`) / `'ts'` (build-time) | Prefix prepended to every generated identifier (class, keyframe, counter-style names). Must match `^[a-zA-Z_][a-zA-Z0-9_-]{0,31}$`. See [Name prefix](#name-prefix). |
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Batched Injection
|
|
83
|
+
|
|
84
|
+
Every `insertRule()` on a live stylesheet invalidates style for that sheet's
|
|
85
|
+
scope. Components inject during React's render phase, and if anything else reads
|
|
86
|
+
layout in the same pass, the two interleave:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
inject -> read (forced style recalc) -> inject -> read (forced recalc) -> ...
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
A UI kit that measures during render — popovers, autosizing inputs, virtualized
|
|
93
|
+
lists — can turn one mount into a dozen full-tree recalculations. `batchInjection`
|
|
94
|
+
queues the writes instead and applies them together, so the tree is invalidated
|
|
95
|
+
once per flush.
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
import { configure, TastyBatchProvider } from '@tenphi/tasty';
|
|
99
|
+
|
|
100
|
+
configure({ batchInjection: true });
|
|
101
|
+
|
|
102
|
+
createRoot(el).render(
|
|
103
|
+
<TastyBatchProvider>
|
|
104
|
+
<App />
|
|
105
|
+
</TastyBatchProvider>,
|
|
106
|
+
);
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Why the provider is required
|
|
110
|
+
|
|
111
|
+
Deferring a write past React's layout phase would let a `useLayoutEffect`
|
|
112
|
+
measure an element whose rules are not in the sheet yet and read its _unstyled_
|
|
113
|
+
box — a wrong number, not a stale one, so it never self-corrects.
|
|
114
|
+
|
|
115
|
+
`TastyBatchProvider` closes that hole. It opens a _batch window_ during its
|
|
116
|
+
render and closes it — flushing — in `useInsertionEffect`, which React runs in
|
|
117
|
+
the mutation phase, before any layout effect:
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
provider renders -> window OPEN
|
|
121
|
+
children render -> injections queued
|
|
122
|
+
provider insertionEffect -> FLUSH, window CLOSED
|
|
123
|
+
layout effects run -> rules are in the sheet
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
With `batchInjection: true` a write is only ever queued inside such a window, so
|
|
127
|
+
turning the flag on can make injection cheaper but can never make a measurement
|
|
128
|
+
wrong. Injections outside a window are written straight through, exactly as with
|
|
129
|
+
batching off:
|
|
130
|
+
|
|
131
|
+
- a deep update that did not re-render the provider
|
|
132
|
+
- an injection from a layout effect, an event handler or an async callback
|
|
133
|
+
- SSR and RSC, where there is no live sheet at all
|
|
134
|
+
|
|
135
|
+
Mount the provider as high in the tree as you can: batching applies to the
|
|
136
|
+
commits it takes part in.
|
|
137
|
+
|
|
138
|
+
### Modes
|
|
139
|
+
|
|
140
|
+
| Value | Behaviour |
|
|
141
|
+
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
142
|
+
| `false` (default) | One `insertRule()` per component, synchronously. |
|
|
143
|
+
| `true` | Batch inside a provider window only. Cannot affect measurement. Without the provider nothing is batched, and dev mode says so once. |
|
|
144
|
+
| `'always'` | Batch every injection, flushing on a microtask when no window is open. Covers more commits; a `useLayoutEffect` measuring a freshly mounted element can read its unstyled box. Paint is unaffected — microtasks always drain before the browser paints. |
|
|
145
|
+
|
|
146
|
+
### Ordering
|
|
147
|
+
|
|
148
|
+
All writes share one FIFO queue — component rules, global rules, raw CSS,
|
|
149
|
+
`@property`, `@keyframes`, `@font-face`, `@counter-style` and `@function`.
|
|
150
|
+
Draining it in insertion order keeps the sheet byte-identical to unbatched
|
|
151
|
+
output, which matters because equal-specificity rules resolve by document order.
|
|
152
|
+
|
|
153
|
+
### Client rendering, server rendering, and build-time extraction
|
|
154
|
+
|
|
155
|
+
**Server render — nothing to batch, safe to leave enabled.** SSR and RSC collect
|
|
156
|
+
CSS as text through `ServerStyleCollector`; the runtime injector never runs
|
|
157
|
+
there, so `batchInjection` changes nothing either way. `<TastyBatchProvider>`
|
|
158
|
+
renders its children and does nothing else: the window it opens is a no-op
|
|
159
|
+
without a `document`, and the `useInsertionEffect` that would close it never
|
|
160
|
+
fires on the server. Configure the flag once in shared code — no environment
|
|
161
|
+
branching needed.
|
|
162
|
+
|
|
163
|
+
**Hydration — the better your SSR coverage, the less there is to batch.**
|
|
164
|
+
`hydrateTastyClasses()` (wired up for you by `@tenphi/tasty/ssr/next` and the
|
|
165
|
+
Astro integration) pre-populates the injector from `window.__TASTY__`, so
|
|
166
|
+
hydrating a server-rendered class is a cache hit that produces no sheet write at
|
|
167
|
+
all. Batching pays off on what SSR could not cover: client-only routes, styles
|
|
168
|
+
that first appear after hydration (a `styles` prop that changes on interaction, a
|
|
169
|
+
modal or popover mounting), and dynamic tokens.
|
|
170
|
+
|
|
171
|
+
**Astro islands — one provider per island, or `'always'`.** Every island is its
|
|
172
|
+
own React root, so a provider inside one island does not open a window for
|
|
173
|
+
another. With `batchInjection: true`, wrap each island root that renders enough
|
|
174
|
+
tasty components to be worth it. `'always'` needs no provider and covers every
|
|
175
|
+
island, at the cost of the measurement hazard above. Either way, an island that
|
|
176
|
+
only re-hydrates server-rendered classes has nothing to batch.
|
|
177
|
+
|
|
178
|
+
**Build-time extraction (`tastyStatic`) — unaffected.** The Babel plugin never
|
|
179
|
+
touches the injector: the babel plugin emits either a CSS file import or an
|
|
180
|
+
`injectCSS()` call from `@tenphi/tasty/static/inject`, which appends text to a
|
|
181
|
+
single `<style data-tasty-static>` element. `batchInjection` only defers CSSOM
|
|
182
|
+
writes made by the browser injector, so extracted styles are unchanged. In an app
|
|
183
|
+
that mixes both, it still applies to the `tasty()` half.
|
|
184
|
+
|
|
185
|
+
### `flushStyles()`
|
|
186
|
+
|
|
187
|
+
Applies every pending write immediately. Every injector read API calls it for
|
|
188
|
+
you — `getCSSText`, `getCSSTextForClasses`, `getCSSTextForNode`,
|
|
189
|
+
`getRawCSSText`, `isPropertyDefined`, `getMetrics`, `cleanup`, `gc` and
|
|
190
|
+
`destroy` — so a read never observes a partial sheet.
|
|
191
|
+
Call it directly before measuring in code that runs outside a batch window under
|
|
192
|
+
`'always'`:
|
|
193
|
+
|
|
194
|
+
```tsx
|
|
195
|
+
import { flushStyles } from '@tenphi/tasty';
|
|
196
|
+
|
|
197
|
+
useLayoutEffect(() => {
|
|
198
|
+
flushStyles();
|
|
199
|
+
const { width } = ref.current.getBoundingClientRect();
|
|
200
|
+
}, []);
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
`hasPendingStyleWrites()` reports whether anything is queued, and
|
|
204
|
+
`resetStyleBatch()` drops the queue without applying it (tests only).
|
|
64
205
|
|
|
65
206
|
---
|
|
66
207
|
|
|
67
208
|
## Color Space
|
|
68
209
|
|
|
69
|
-
|
|
210
|
+
> **Deprecated.** `configure({ colorSpace })` no longer has any effect and will
|
|
211
|
+
> be removed in the next major. Setting it warns in development.
|
|
212
|
+
|
|
213
|
+
A `#name` token's value used to be rewritten into a configured color space, so
|
|
214
|
+
`#brand: '#ff8800'` declared `--brand-color: oklch(0.75 0.16 55)`. That existed
|
|
215
|
+
to serve the opacity suffix, which needed numeric channels to write an alpha
|
|
216
|
+
into. Opacity now uses CSS relative color syntax —
|
|
217
|
+
`oklch(from var(--brand-color) l c h / .5)` — which has the browser read the
|
|
218
|
+
channels, so there is nothing left for the setting to decide.
|
|
219
|
+
|
|
220
|
+
A color is emitted exactly as authored:
|
|
221
|
+
|
|
222
|
+
```jsx
|
|
223
|
+
configure({ tokens: { '#brand': '#ff8800' } });
|
|
224
|
+
// → --brand-color: #ff8800
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
That holds for every form: a hex literal, a native color function, a bare CSS
|
|
228
|
+
color name, a `color-mix()`, a `light-dark()`, a fallback chain. A `#token`
|
|
229
|
+
reference still resolves to its `var()` chain, and a plugin color function such
|
|
230
|
+
as `okhsl()` is still resolved by the parser to the color it denotes.
|
|
231
|
+
|
|
232
|
+
To address a token's channels, use relative color syntax against the token:
|
|
233
|
+
|
|
234
|
+
```css
|
|
235
|
+
background: oklch(from var(--brand-color) calc(l * 1.2) c h);
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
This works on any `<color>`, including the ones no build-time conversion could
|
|
239
|
+
have evaluated — and it is what the [opacity suffix](dsl.md#color-tokens--opacity)
|
|
240
|
+
itself uses. Its space is always `oklch`, which is unbounded, so a wide-gamut
|
|
241
|
+
color survives a round trip a narrower space would clamp.
|
|
242
|
+
|
|
243
|
+
### Migrating off `colorSpace`
|
|
244
|
+
|
|
245
|
+
Drop the option. If you relied on the uniform output format, nothing in Tasty
|
|
246
|
+
needs it — author your tokens in the space you want them emitted in, since the
|
|
247
|
+
value now passes through untouched:
|
|
248
|
+
|
|
249
|
+
```jsx
|
|
250
|
+
// before: any input, normalized to the configured space on the way out
|
|
251
|
+
configure({ colorSpace: 'oklch', tokens: { '#brand': '#ff8800' } });
|
|
252
|
+
|
|
253
|
+
// after: author it in the space you want
|
|
254
|
+
configure({ tokens: { '#brand': 'oklch(0.75 0.16 55)' } });
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Migrating off the channel companions
|
|
258
|
+
|
|
259
|
+
A `#name` token used to declare a second variable beside `--name-color` holding
|
|
260
|
+
its channels decomposed, suffixed with the configured space —
|
|
261
|
+
`--brand-color-oklch: 0.75 0.16 55` — and a `color` style emitted
|
|
262
|
+
`--current-color-{space}` beside `--current-color`. Both are gone, for the same
|
|
263
|
+
reason: they existed so an opacity suffix had channels to write an alpha into.
|
|
264
|
+
|
|
265
|
+
These were never part of the public API, but they were visible in the emitted
|
|
266
|
+
CSS, so hand-authored CSS may reference one. Address the token itself instead:
|
|
267
|
+
|
|
268
|
+
```css
|
|
269
|
+
/* before */
|
|
270
|
+
color: oklch(var(--brand-color-oklch) / 0.5);
|
|
271
|
+
background: oklch(var(--brand-color-oklch));
|
|
272
|
+
|
|
273
|
+
/* after */
|
|
274
|
+
color: oklch(from var(--brand-color) l c h / 0.5);
|
|
275
|
+
background: var(--brand-color);
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
The replacement is strictly more capable — the companion could only be
|
|
279
|
+
decomposed for a color the engine could evaluate at build time, while relative
|
|
280
|
+
color syntax works on every `<color>`, including a `color-mix()`, a
|
|
281
|
+
`light-dark()`, and a variable Tasty never defined.
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## Name Prefix
|
|
286
|
+
|
|
287
|
+
Every identifier Tasty generates — class names, keyframe names, counter-style names — starts with a configurable prefix. The `tasty()` client, SSR, and RSC paths default to `'t'`; the build-time extraction path (`tastyStatic` via the Babel plugin) defaults to `'ts'` so extracted classes can never collide with `tasty()` classes when both are loaded on the same page.
|
|
70
288
|
|
|
71
289
|
```jsx
|
|
72
290
|
configure({
|
|
73
|
-
|
|
291
|
+
namePrefix: 'mb',
|
|
74
292
|
});
|
|
75
293
|
```
|
|
76
294
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
|
80
|
-
|
|
|
81
|
-
| `
|
|
295
|
+
The prefix is prepended verbatim to the hash, so include any separator inside the prefix string itself:
|
|
296
|
+
|
|
297
|
+
| Setting | Class | Keyframe | Counter-style |
|
|
298
|
+
| --------------------------- | ------------- | -------------- | -------------- |
|
|
299
|
+
| `'t'` (`tasty` default) | `t1a2b3` | `tk1a2b3` | `tc1a2b3` |
|
|
300
|
+
| `'ts'` (build-time default) | `ts1a2b3` | `tsk1a2b3` | `tsc1a2b3` |
|
|
301
|
+
| `'mb'` | `mb1a2b3` | `mbk1a2b3` | `mbc1a2b3` |
|
|
302
|
+
| `'myapp-'` | `myapp-1a2b3` | `myapp-k1a2b3` | `myapp-c1a2b3` |
|
|
303
|
+
|
|
304
|
+
The single-letter discriminators (`k` for keyframes, `c` for counter-styles) keep the three kinds visually distinct in devtools — they are not required for correctness because CSS keeps these in separate namespaces.
|
|
305
|
+
|
|
306
|
+
### Rules
|
|
307
|
+
|
|
308
|
+
- Must match `^[a-zA-Z_][a-zA-Z0-9_-]{0,31}$`. Examples that pass: `'t'`, `'ts'`, `'app'`, `'myapp-'`, `'_foo'`. Examples that fail: `''`, `'1foo'`, `'my app'`.
|
|
309
|
+
- Validated at `configure()` time; an invalid prefix throws synchronously rather than silently producing broken hydration.
|
|
310
|
+
- Locked once styles have been generated, like all other config.
|
|
82
311
|
|
|
83
|
-
|
|
312
|
+
### Coexistence with build-time extraction
|
|
313
|
+
|
|
314
|
+
The `tasty()` and `tastyStatic()` paths **must use different prefixes** when both are loaded on the same page. Defaults already guarantee this; if you customize one, customize the other accordingly:
|
|
315
|
+
|
|
316
|
+
```jsx
|
|
317
|
+
// app config (client / SSR / RSC)
|
|
318
|
+
configure({ namePrefix: 'mb' });
|
|
319
|
+
|
|
320
|
+
// tasty-zero.config.ts (Babel plugin)
|
|
321
|
+
export default { namePrefix: 'mbs' };
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
If you only use one of the two builds, you only need to set `namePrefix` on that path.
|
|
84
325
|
|
|
85
326
|
---
|
|
86
327
|
|
|
@@ -93,8 +334,8 @@ Tokens support state maps for responsive or theme-aware values:
|
|
|
93
334
|
```jsx
|
|
94
335
|
configure({
|
|
95
336
|
tokens: {
|
|
96
|
-
|
|
97
|
-
|
|
337
|
+
$gap: '4px',
|
|
338
|
+
$radius: '6px',
|
|
98
339
|
'#primary': {
|
|
99
340
|
'': '#purple',
|
|
100
341
|
'@dark': '#light-purple',
|
|
@@ -108,9 +349,10 @@ configure({
|
|
|
108
349
|
```
|
|
109
350
|
|
|
110
351
|
- `$name` keys become `--name` CSS custom properties
|
|
111
|
-
- `#name` keys become `--name-color`
|
|
352
|
+
- `#name` keys become `--name-color` custom properties
|
|
353
|
+
- Names keep their case, since CSS custom properties are case-sensitive: `$myVar` is `--myVar` and is referenced as `$myVar`. A leading capital is not supported and folds — `$Foo` and `#Purple` become `--foo` and `--purple-color` — so start names lowercase. Kebab-case (`$my-var`) remains the convention.
|
|
112
354
|
|
|
113
|
-
Tokens are automatically emitted in all
|
|
355
|
+
Tokens are automatically emitted in all delivery modes: client rendering, server rendering, and build-time extraction with the Babel plugin.
|
|
114
356
|
|
|
115
357
|
---
|
|
116
358
|
|
|
@@ -143,7 +385,7 @@ Register custom fonts globally so every component can reference them by family n
|
|
|
143
385
|
|
|
144
386
|
```ts
|
|
145
387
|
configure({
|
|
146
|
-
|
|
388
|
+
fontFaces: {
|
|
147
389
|
'Brand Sans': [
|
|
148
390
|
{
|
|
149
391
|
src: 'url("/fonts/brand-regular.woff2") format("woff2")',
|
|
@@ -164,9 +406,9 @@ configure({
|
|
|
164
406
|
});
|
|
165
407
|
```
|
|
166
408
|
|
|
167
|
-
Now any component can use `
|
|
409
|
+
Now any component can use `font: '"Brand Sans", sans-serif'` and the browser will already have the `@font-face` rules in the stylesheet.
|
|
168
410
|
|
|
169
|
-
See [Font Face (`@
|
|
411
|
+
See [Font Face (`@font-face`)](dsl.md#font-face-font-face) for inline usage inside component styles and the full list of supported descriptors.
|
|
170
412
|
|
|
171
413
|
---
|
|
172
414
|
|
|
@@ -176,7 +418,7 @@ Define custom list-marker algorithms globally. Rules are injected eagerly when s
|
|
|
176
418
|
|
|
177
419
|
```ts
|
|
178
420
|
configure({
|
|
179
|
-
|
|
421
|
+
counterStyles: {
|
|
180
422
|
thumbs: {
|
|
181
423
|
system: 'cyclic',
|
|
182
424
|
symbols: '"👍"',
|
|
@@ -192,7 +434,98 @@ configure({
|
|
|
192
434
|
|
|
193
435
|
Components can then reference `listStyleType: 'thumbs'` directly.
|
|
194
436
|
|
|
195
|
-
See [Counter Style (`@
|
|
437
|
+
See [Counter Style (`@counter-style`)](dsl.md#counter-style-counter-style) for inline usage inside component styles and the full list of supported descriptors.
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
## Functions
|
|
442
|
+
|
|
443
|
+
The single `functions` map holds both kinds of custom functions, discriminated by the value type:
|
|
444
|
+
|
|
445
|
+
- **Parse functions** — a **bare key** with a **function value** `(groups) => string`. Runs at parse time and is called as `name(...)`. Use these for computed/derived CSS that JavaScript produces (e.g. color-space conversions).
|
|
446
|
+
- **CSS `@function` definitions** — a **`$$name` key** with an **object value** (a [`FunctionDefinition`](https://developer.mozilla.org/en-US/docs/Web/CSS/@function)). Injected eagerly as a native `@function` rule and called as `$$name(...)` (→ `--name(...)`).
|
|
447
|
+
|
|
448
|
+
```ts
|
|
449
|
+
configure({
|
|
450
|
+
functions: {
|
|
451
|
+
// Parse function — bare key, function value
|
|
452
|
+
double: (groups) => `calc(2 * ${groups[0]?.output ?? '0'})`,
|
|
453
|
+
|
|
454
|
+
// CSS @function — `$$` key, object value
|
|
455
|
+
$$negative: { args: ['$value'], result: '(-1 * $value)' },
|
|
456
|
+
$$shadow: {
|
|
457
|
+
args: { '$shadow-color': { syntax: '<color>', default: 'inherit' } },
|
|
458
|
+
returns: '<color>',
|
|
459
|
+
$offset: '2px',
|
|
460
|
+
result: '$offset $offset ($shadow-color, black)',
|
|
461
|
+
},
|
|
462
|
+
},
|
|
463
|
+
});
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
Components then invoke parse functions as `double(...)` and CSS functions with the `$$name(...)` sugar, e.g. `margin: '$$negative(10px) top'`.
|
|
467
|
+
|
|
468
|
+
> A key whose prefix doesn't match its value type (an object under a bare key, or a function under a `$$` key) is **ignored with a dev-mode warning**.
|
|
469
|
+
|
|
470
|
+
See [Functions (`@function`)](dsl.md#functions-function) for inline usage inside component styles, the full descriptor shape, and token conventions. `@function` is an experimental CSS feature — unsupported browsers safely ignore the native rule (see the polyfill below).
|
|
471
|
+
|
|
472
|
+
### Custom color functions
|
|
473
|
+
|
|
474
|
+
A parse function whose output is an already-supported color (`rgb`, `hsl`, `#…`, `oklch`, …) is treated as a **color function**: it works everywhere a color is accepted — style values, `#token.alpha` opacity injection, and `parseColor` — with no extra registration. This is the same mechanism the built-in `okhsl`/`okhst` plugins use; they are ordinary plugins registered by default.
|
|
475
|
+
|
|
476
|
+
```ts
|
|
477
|
+
import { configure, createColorFunc } from '@tenphi/tasty';
|
|
478
|
+
|
|
479
|
+
// A custom color space is just a `functions` entry.
|
|
480
|
+
const myColorPlugin = () => ({
|
|
481
|
+
name: 'mycolor',
|
|
482
|
+
functions: {
|
|
483
|
+
// Hand-written parse function:
|
|
484
|
+
mycolor: (groups) => {
|
|
485
|
+
const [r, g, b] = groups[0].all;
|
|
486
|
+
return `rgb(${r} ${g} ${b})`;
|
|
487
|
+
},
|
|
488
|
+
},
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
configure({ plugins: [myColorPlugin()] });
|
|
492
|
+
|
|
493
|
+
// Now `mycolor(...)` is a color in every context:
|
|
494
|
+
// fill: 'mycolor(255 0 0)'
|
|
495
|
+
// fill: '#brand.5' (with replaceTokens: { '#brand': 'mycolor(255 0 0)' })
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
For HSL-style color spaces (a hue angle plus two percentages), the exported `createColorFunc(name, convert, label?)` helper handles angle/percentage parsing, clamping, alpha, and caching. `convert` returns sRGB `[r, g, b]` in 0-1; `label` is an optional string used only in dev warnings (e.g. `'H S L'`) and has no effect on output. This is exactly how `okhslPlugin`/`okhstPlugin` are implemented.
|
|
499
|
+
|
|
500
|
+
---
|
|
501
|
+
|
|
502
|
+
## Polyfills
|
|
503
|
+
|
|
504
|
+
`@function` only ships natively in Chromium 139+. To use CSS functions in browsers that don't support the at-rule yet (Firefox, Safari), enable the **functions polyfill**, which expands every `$$name(...)` call into plain CSS (`calc()`/`var()`/`color-mix()`) at parse time instead of emitting the native `@function` rule:
|
|
505
|
+
|
|
506
|
+
```ts
|
|
507
|
+
configure({
|
|
508
|
+
polyfills: {
|
|
509
|
+
functions: true, // default: false
|
|
510
|
+
},
|
|
511
|
+
functions: {
|
|
512
|
+
$$negative: { args: ['$value'], result: '(-1 * $value)' },
|
|
513
|
+
},
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
// Now `margin: '$$negative(10px) top'` renders `margin: calc(-1 * 10px) 0 0 0`
|
|
517
|
+
// — no native @function rule is emitted.
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
This works across all rendering modes (client, SSR/RSC, and `tastyStatic`) because expansion happens in the parser. Note `polyfills.functions` (the feature toggle) is distinct from the top-level `functions` (the definitions map).
|
|
521
|
+
|
|
522
|
+
**Decided limitations when the polyfill is on:**
|
|
523
|
+
|
|
524
|
+
- **No native fallback** — functions are always inlined; we do exactly what is configured.
|
|
525
|
+
- **Conditional results** (`@media`/`@supports`/`if()` inside `result`) are inlined verbatim; the conditional nuance is not resolved per-element.
|
|
526
|
+
- **Typed params / `returns`** are dropped (inlining is purely lexical substitution).
|
|
527
|
+
- **Param name collisions** are avoided by fully inlining argument values (no function-internal custom properties are emitted) and by namespacing the function's own variables.
|
|
528
|
+
- **Recursion** — self/mutually-recursive functions are not expanded; the cycle guard bails and leaves the call untouched.
|
|
196
529
|
|
|
197
530
|
---
|
|
198
531
|
|
|
@@ -216,7 +549,7 @@ configure({
|
|
|
216
549
|
});
|
|
217
550
|
```
|
|
218
551
|
|
|
219
|
-
Recipe values are flat tasty styles (no sub-element keys). They may contain base styles, tokens, local states, `@keyframes`, and `@
|
|
552
|
+
Recipe values are flat tasty styles (no sub-element keys). They may contain base styles, tokens, local states, `@keyframes`, and `@property`. Recipes cannot reference other recipes.
|
|
220
553
|
|
|
221
554
|
For how to apply, compose, and override recipes in components, see [Recipes](dsl.md#recipes) in the Style DSL reference.
|
|
222
555
|
|
|
@@ -273,7 +606,7 @@ configure({
|
|
|
273
606
|
color: '#text',
|
|
274
607
|
preset: 't2',
|
|
275
608
|
margin: 0,
|
|
276
|
-
|
|
609
|
+
font: 'system-ui, sans-serif',
|
|
277
610
|
},
|
|
278
611
|
html: {
|
|
279
612
|
overflow: 'hidden',
|
|
@@ -284,7 +617,7 @@ configure({
|
|
|
284
617
|
|
|
285
618
|
Each key is a CSS selector, and each value is a Tasty `Styles` object supporting the full style syntax including style properties, tokens, state maps, and selector-based sub-styling (e.g. `$: '> .app'` for elements outside React scope). Global styles are injected alongside `:root` tokens when the first style is rendered.
|
|
286
619
|
|
|
287
|
-
Global styles are automatically emitted in all
|
|
620
|
+
Global styles are automatically emitted in all delivery modes: client rendering, server rendering, and build-time extraction with the Babel plugin. Plugins can also provide `globalStyles`; they are merged per selector with config global styles (config wins on conflict).
|
|
288
621
|
|
|
289
622
|
---
|
|
290
623
|
|
|
@@ -292,25 +625,25 @@ Global styles are automatically emitted in all rendering modes: runtime (client)
|
|
|
292
625
|
|
|
293
626
|
CSS cannot transition or animate custom properties unless their type is declared via [`@property`](https://developer.mozilla.org/en-US/docs/Web/CSS/@property). Tasty handles this automatically — when a custom property is assigned a concrete value (e.g. `'$scale': 1`, `'$gap': '10px'`, `'#accent': 'purple'`), the type is inferred and a `@property` rule is registered.
|
|
294
627
|
|
|
295
|
-
This works across all declaration contexts: component styles, `@keyframes`, global config, and the
|
|
628
|
+
This works across all declaration contexts: component styles, `@keyframes`, global config, and build-time extraction with the Babel plugin. It also resolves `var()` chains — if `$a` references `var(--b)`, the type propagates once `--b` is resolved.
|
|
296
629
|
|
|
297
630
|
Supported types:
|
|
298
631
|
|
|
299
|
-
| Detection
|
|
300
|
-
|
|
301
|
-
| `1`, `0.5`, `-3` (bare numbers)
|
|
302
|
-
| `10px`, `2rem`, `100vw` (length units) | `<length>`
|
|
303
|
-
| `50%`
|
|
304
|
-
| `45deg`, `0.5turn` (angle units)
|
|
305
|
-
| `300ms`, `1s` (time units)
|
|
306
|
-
| `#name` tokens (by naming convention)
|
|
632
|
+
| Detection | Inferred syntax |
|
|
633
|
+
| -------------------------------------- | --------------- |
|
|
634
|
+
| `1`, `0.5`, `-3` (bare numbers) | `<number>` |
|
|
635
|
+
| `10px`, `2rem`, `100vw` (length units) | `<length>` |
|
|
636
|
+
| `50%` | `<percentage>` |
|
|
637
|
+
| `45deg`, `0.5turn` (angle units) | `<angle>` |
|
|
638
|
+
| `300ms`, `1s` (time units) | `<time>` |
|
|
639
|
+
| `#name` tokens (by naming convention) | `<color>` |
|
|
307
640
|
|
|
308
|
-
Auto-inferred properties use `inherits: true` (the CSS default). Use explicit `@
|
|
641
|
+
Auto-inferred properties use `inherits: true` (the CSS default). Use explicit `@property` when you need different settings:
|
|
309
642
|
|
|
310
643
|
```jsx
|
|
311
644
|
// In component styles
|
|
312
645
|
styles: {
|
|
313
|
-
'@
|
|
646
|
+
'@property': {
|
|
314
647
|
'$scale': { syntax: '<number>', inherits: false, initialValue: 1 },
|
|
315
648
|
},
|
|
316
649
|
}
|
|
@@ -323,7 +656,7 @@ configure({
|
|
|
323
656
|
});
|
|
324
657
|
```
|
|
325
658
|
|
|
326
|
-
To disable auto-inference entirely (only explicit `@
|
|
659
|
+
To disable auto-inference entirely (only explicit `@property` will be used):
|
|
327
660
|
|
|
328
661
|
```jsx
|
|
329
662
|
configure({ autoPropertyTypes: false });
|
|
@@ -335,27 +668,19 @@ configure({ autoPropertyTypes: false });
|
|
|
335
668
|
|
|
336
669
|
Override or extend the built-in style property handlers. A handler definition can take three forms:
|
|
337
670
|
|
|
338
|
-
| Form
|
|
339
|
-
|
|
340
|
-
| Function only | `handler`
|
|
341
|
-
| Single dep
|
|
342
|
-
| Multi dep
|
|
671
|
+
| Form | Syntax | Description |
|
|
672
|
+
| ------------- | ---------------------------------- | --------------------------------------------------------------- |
|
|
673
|
+
| Function only | `handler` | Triggered by its key name; receives only that property |
|
|
674
|
+
| Single dep | `['styleName', handler]` | Triggered by the specified style property |
|
|
675
|
+
| Multi dep | `[['dep1', 'dep2', ...], handler]` | Triggered by any of the listed properties; receives all of them |
|
|
343
676
|
|
|
344
|
-
The multi-dep form is useful when output depends on several style properties together (e.g., `gap` needs to know `display` and `flow` to decide the CSS strategy).
|
|
677
|
+
The multi-dep form is useful when output depends on several style properties together (e.g., `gap` needs to know `display` and `flow` to decide the CSS strategy). Use `defineHandler` for it and the dependency types are inferred from the dependency list, so a typo in the destructure is a type error instead of a silent `undefined`.
|
|
345
678
|
|
|
346
679
|
```jsx
|
|
347
|
-
import { configure, styleHandlers } from '@tenphi/tasty';
|
|
680
|
+
import { configure, defineHandler, styleHandlers } from '@tenphi/tasty';
|
|
348
681
|
|
|
349
682
|
configure({
|
|
350
683
|
handlers: {
|
|
351
|
-
// Function only — overrides built-in fill handler
|
|
352
|
-
fill: ({ fill }) => {
|
|
353
|
-
if (fill?.startsWith('gradient:')) {
|
|
354
|
-
return { background: fill.slice(9) };
|
|
355
|
-
}
|
|
356
|
-
return styleHandlers.fill({ fill });
|
|
357
|
-
},
|
|
358
|
-
|
|
359
684
|
// Function only — new single-prop handler
|
|
360
685
|
elevation: ({ elevation }) => {
|
|
361
686
|
const level = parseInt(elevation) || 1;
|
|
@@ -365,16 +690,120 @@ configure({
|
|
|
365
690
|
};
|
|
366
691
|
},
|
|
367
692
|
|
|
693
|
+
// Overriding a built-in: declare every style the built-in handled and pass
|
|
694
|
+
// them all through, or the ones you leave out stop working (see below).
|
|
695
|
+
fill: defineHandler(
|
|
696
|
+
[
|
|
697
|
+
'fill',
|
|
698
|
+
'backgroundColor',
|
|
699
|
+
'image',
|
|
700
|
+
'backgroundImage',
|
|
701
|
+
'backgroundPosition',
|
|
702
|
+
'backgroundSize',
|
|
703
|
+
'backgroundRepeat',
|
|
704
|
+
],
|
|
705
|
+
(props) => {
|
|
706
|
+
if (
|
|
707
|
+
typeof props.fill === 'string' &&
|
|
708
|
+
props.fill.startsWith('gradient:')
|
|
709
|
+
) {
|
|
710
|
+
return { background: props.fill.slice(9) };
|
|
711
|
+
}
|
|
712
|
+
return styleHandlers.fill(props);
|
|
713
|
+
},
|
|
714
|
+
),
|
|
715
|
+
|
|
368
716
|
// Multi dep — handler reads multiple style properties
|
|
369
|
-
gap: [
|
|
717
|
+
gap: defineHandler(['display', 'flow', 'gap'], ({ display, flow, gap }) => {
|
|
370
718
|
if (!gap) return;
|
|
371
|
-
const isGrid = display
|
|
719
|
+
const isGrid = String(display ?? '').includes('grid');
|
|
372
720
|
return { gap: isGrid ? gap : `/* custom logic for ${flow} */` };
|
|
373
|
-
}
|
|
721
|
+
}),
|
|
722
|
+
},
|
|
723
|
+
});
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
### Return shape
|
|
727
|
+
|
|
728
|
+
A handler returns a `CSSMap`, an array of them, or nothing:
|
|
729
|
+
|
|
730
|
+
- Keys are **kebab-case** CSS property names (`'background-color'`, not `backgroundColor`) or `--custom-property` names. A camelCase key warns in development and is emitted verbatim, which the browser ignores.
|
|
731
|
+
- Values are stringified, so numbers are fine (`{ '-webkit-line-clamp': 3 }`).
|
|
732
|
+
- The reserved `$` key is a **selector suffix**: `{ $: '& > *:not(:last-child)', 'margin-right': gap }` applies the declarations to a nested selector. It accepts an array to fan out over several.
|
|
733
|
+
- Return an array of maps to emit several declaration sets, each with its own `$`.
|
|
734
|
+
|
|
735
|
+
Values arrive **state-resolved but unparsed** — the raw authored DSL string (`'2x'`, `'#purple.5'`, `true`, `4`). Call the exported `parseStyle()` / `parseColor()` yourself; nothing parses them for you.
|
|
736
|
+
|
|
737
|
+
### Replacing a built-in handler
|
|
738
|
+
|
|
739
|
+
Built-in handlers are shared across several style names. Registering a handler for one of those names unregisters the built-in from **all** of them, and the displaced names then fall back to auto-generated CSS aliases — so `hide: true` starts emitting a literal `hide: true` declaration. A development-mode warning lists exactly what was displaced.
|
|
740
|
+
|
|
741
|
+
The shared groups worth knowing:
|
|
742
|
+
|
|
743
|
+
| Registering a handler for | Also takes over |
|
|
744
|
+
| ------------------------------ | --------------------------------------------------------------------------------------------------------- |
|
|
745
|
+
| `fill` | `backgroundColor`, `image`, `backgroundImage`, `backgroundPosition`, `backgroundSize`, `backgroundRepeat` |
|
|
746
|
+
| `display` | `hide`, `overflow`, `whiteSpace`, `textOverflow`, `flow`, `gap` |
|
|
747
|
+
| `preset` | `font`, `fontSize`, `fontWeight`, `fontStyle`, `lineHeight`, `letterSpacing`, `textTransform` |
|
|
748
|
+
| `padding` / `margin` / `inset` | their `*Top`/`*Right`/`*Bottom`/`*Left`/`*Block`/`*Inline` longhands |
|
|
749
|
+
|
|
750
|
+
Either declare the whole group and delegate the rest to `styleHandlers.*`, or pick a name that isn't shared.
|
|
751
|
+
|
|
752
|
+
### Chunk membership
|
|
753
|
+
|
|
754
|
+
Tasty renders and caches CSS in independent chunks, and a chunk's cache key covers only its own style values. All of a handler's dependencies must therefore live in one chunk. Custom style names are pulled into their handler's chunk automatically at registration; a handler whose dependencies span two _built-in_ chunks (say `fill` and `padding`) warns, because it would be invoked once per chunk with a subset of its inputs.
|
|
755
|
+
|
|
756
|
+
`configure({ handlers })` must run before the first render, like every other config option.
|
|
757
|
+
|
|
758
|
+
---
|
|
759
|
+
|
|
760
|
+
## Props Middleware
|
|
761
|
+
|
|
762
|
+
`propHandlers` are middleware over a component's props — props in, props out. Where a style _handler_ turns a style property into CSS declarations, a prop handler turns a **component prop** into other props, including `styles`. It is the extension point for props whose value isn't a style value.
|
|
763
|
+
|
|
764
|
+
```jsx
|
|
765
|
+
import { configure, mergeStyles } from '@tenphi/tasty';
|
|
766
|
+
|
|
767
|
+
configure({
|
|
768
|
+
propHandlers: {
|
|
769
|
+
glaze: (props) => {
|
|
770
|
+
const { glaze, ...rest } = props;
|
|
771
|
+
if (!glaze) return rest;
|
|
772
|
+
|
|
773
|
+
return { ...rest, styles: mergeStyles(glazeStyles(glaze), rest.styles) };
|
|
774
|
+
},
|
|
374
775
|
},
|
|
375
776
|
});
|
|
777
|
+
|
|
778
|
+
<Element glaze="purple" />;
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
The map key is the handler's name and, by default, the prop that triggers it, so an absent prop costs one property check rather than a call. A tuple overrides that: `['glaze', fn]`, `[['glaze', 'tint'], fn]`, or `['*', fn]` for unconditional.
|
|
782
|
+
|
|
783
|
+
Handlers run at the very top of every component's render, before any prop is destructured, so one can rewrite `styles`, `mods`, `tokens`, `variant`, `as`, `element`, and `qa`. They run in registration order — plugins first, then direct config — each receiving the previous one's output.
|
|
784
|
+
|
|
785
|
+
Handlers must be **pure** and must not mutate their input, and should memoize the styles they build per input value: style values are cached by object identity, so mutating one in place yields stale CSS, and a reference-stable object avoids re-serializing on every render.
|
|
786
|
+
|
|
787
|
+
Injected styles occupy the `styles` slot, so they beat a component's own default styles and lose to a style prop at the call site. Prop handlers do not apply to sub-elements or to build-time `tastyStatic()`, which has no props.
|
|
788
|
+
|
|
789
|
+
See [Plugins → Props middleware](plugins.md#props-middleware) for the full contract and a worked example.
|
|
790
|
+
|
|
791
|
+
---
|
|
792
|
+
|
|
793
|
+
## Base Style Props
|
|
794
|
+
|
|
795
|
+
`baseStyleProps` exposes style properties as props on **every** `tasty()` component, on top of the built-in base styles (`display`, `font`, `preset`, `hide`, `whiteSpace`, `opacity`, `transition`):
|
|
796
|
+
|
|
797
|
+
```jsx
|
|
798
|
+
configure({ baseStyleProps: ['radius', 'shadow'] });
|
|
799
|
+
|
|
800
|
+
<Card radius="1r" shadow />;
|
|
376
801
|
```
|
|
377
802
|
|
|
803
|
+
`configure()` may run after your components are defined — each factory resolves its prop list lazily.
|
|
804
|
+
|
|
805
|
+
Each name costs one property check per render of every component, and the effect is app-global, so keep the list short and use a factory's own `styleProps` for anything narrower. Avoid names that collide with real DOM or component props (`width`, `size`, `color`), since every component will then swallow them as styles.
|
|
806
|
+
|
|
378
807
|
---
|
|
379
808
|
|
|
380
809
|
## Extending Style Types (TypeScript)
|