@tenphi/tasty 0.0.0-snapshot.bbd10c7 → 0.0.0-snapshot.bbfcd75
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 +196 -140
- package/dist/babel-Zs1Q03qt.d.ts +83 -0
- package/dist/chunks/async-storage-CG5HnUT5.js +20 -0
- package/dist/chunks/async-storage-CG5HnUT5.js.map +1 -0
- package/dist/chunks/build-config-CUQVwdWF.js +42 -0
- package/dist/chunks/build-config-CUQVwdWF.js.map +1 -0
- package/dist/chunks/collector-B0LTHw6Z.js +208 -0
- package/dist/chunks/collector-B0LTHw6Z.js.map +1 -0
- package/dist/chunks/config-engine-C3pF9EK9.js +267 -0
- package/dist/chunks/config-engine-C3pF9EK9.js.map +1 -0
- package/dist/chunks/css-definitions-D_it4XZH.js +875 -0
- package/dist/chunks/css-definitions-D_it4XZH.js.map +1 -0
- package/dist/chunks/css-resources-BN8apgxt.js +142 -0
- package/dist/chunks/css-resources-BN8apgxt.js.map +1 -0
- package/dist/{debug.js → chunks/debug-C9LeNVoJ.js} +187 -139
- package/dist/chunks/debug-C9LeNVoJ.js.map +1 -0
- package/dist/chunks/dsl-C5yuAn41.js +1373 -0
- package/dist/chunks/dsl-C5yuAn41.js.map +1 -0
- package/dist/chunks/hydration-TRcz5vnk.js +41 -0
- package/dist/chunks/hydration-TRcz5vnk.js.map +1 -0
- package/dist/chunks/react-runtime-O-OUrKLH.js +557 -0
- package/dist/chunks/react-runtime-O-OUrKLH.js.map +1 -0
- package/dist/chunks/runtime-engine-DvnE2g-E.js +2437 -0
- package/dist/chunks/runtime-engine-DvnE2g-E.js.map +1 -0
- package/dist/chunks/runtime-state-BTDqSYBk.js +127 -0
- package/dist/chunks/runtime-state-BTDqSYBk.js.map +1 -0
- package/dist/{utils/merge-styles.js → chunks/shared-utils-DWA4LjIa.js} +55 -55
- package/dist/chunks/shared-utils-DWA4LjIa.js.map +1 -0
- package/dist/chunks/style-engine-C1ECBZxR.js +5131 -0
- package/dist/chunks/style-engine-C1ECBZxR.js.map +1 -0
- package/dist/{zero/extractor.js → chunks/zero-engine-B6EnYcXY.js} +89 -82
- package/dist/chunks/zero-engine-B6EnYcXY.js.map +1 -0
- package/dist/collector-hl5KNH7k.d.ts +165 -0
- package/dist/config-DWL1ZYue.d.ts +1358 -0
- package/dist/core/index.d.ts +5 -34
- package/dist/core/index.js +9 -27
- package/dist/index-D9N9GV24.d.ts +1947 -0
- package/dist/index.d.ts +5 -51
- package/dist/index.js +11 -36
- package/dist/{utils/merge-styles.d.ts → merge-styles-C_oMoJVd.d.ts} +3 -3
- package/dist/ssr/astro-client.js +6 -0
- package/dist/ssr/astro-client.js.map +1 -0
- package/dist/ssr/astro-middleware-extract-static.d.ts +11 -0
- package/dist/ssr/astro-middleware-extract-static.js +9 -0
- package/dist/ssr/astro-middleware-extract-static.js.map +1 -0
- package/dist/ssr/astro-middleware-extract.d.ts +11 -0
- package/dist/ssr/astro-middleware-extract.js +9 -0
- package/dist/ssr/astro-middleware-extract.js.map +1 -0
- package/dist/ssr/astro-middleware-static.d.ts +18 -0
- package/dist/ssr/astro-middleware-static.js +7 -0
- package/dist/ssr/astro-middleware-static.js.map +1 -0
- package/dist/ssr/astro-middleware.d.ts +19 -0
- package/dist/ssr/astro-middleware.js +7 -0
- package/dist/ssr/astro-middleware.js.map +1 -0
- package/dist/ssr/astro.d.ts +117 -11
- package/dist/ssr/astro.js +204 -48
- package/dist/ssr/astro.js.map +1 -1
- package/dist/ssr/index.d.ts +37 -4
- package/dist/ssr/index.js +6 -6
- package/dist/ssr/index.js.map +1 -1
- package/dist/ssr/next-config.d.ts +66 -0
- package/dist/ssr/next-config.js +124 -0
- package/dist/ssr/next-config.js.map +1 -0
- package/dist/ssr/next.d.ts +14 -5
- package/dist/ssr/next.js +31 -46
- package/dist/ssr/next.js.map +1 -1
- package/dist/static/index.d.ts +91 -5
- package/dist/static/index.js +31 -3
- package/dist/static/index.js.map +1 -0
- package/dist/static/inject.js.map +1 -1
- package/dist/types-jKZNpias.d.ts +1616 -0
- package/dist/zero/babel.d.ts +2 -182
- package/dist/zero/babel.js +82 -113
- 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 +0 -31
- package/dist/zero/next.js.map +1 -1
- package/docs/README.md +20 -12
- package/docs/adoption.md +59 -32
- package/docs/ai-agents.md +229 -0
- package/docs/comparison.md +76 -48
- package/docs/configuration.md +566 -69
- package/docs/debug.md +37 -23
- package/docs/design-system.md +63 -32
- package/docs/dsl.md +527 -179
- package/docs/getting-started.md +62 -41
- package/docs/injector.md +157 -51
- package/docs/methodology.md +69 -37
- package/docs/migration-v3.md +285 -0
- package/docs/pipeline.md +744 -0
- package/docs/plugins.md +353 -0
- package/docs/{runtime.md → react-api.md} +176 -67
- package/docs/runtime-benchmarks.md +377 -0
- package/docs/ssr.md +350 -94
- package/docs/styles.md +108 -27
- package/docs/tasty-static.md +149 -87
- package/package.json +121 -23
- package/tasty.config.ts +6 -0
- package/dist/_virtual/_rolldown/runtime.js +0 -7
- 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 -356
- package/dist/compute-styles.js.map +0 -1
- package/dist/config.d.ts +0 -372
- package/dist/config.js +0 -507
- 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.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 -50
- package/dist/hooks/useCounterStyle.js +0 -45
- package/dist/hooks/useCounterStyle.js.map +0 -1
- package/dist/hooks/useFontFace.d.ts +0 -43
- package/dist/hooks/useFontFace.js +0 -69
- package/dist/hooks/useFontFace.js.map +0 -1
- package/dist/hooks/useGlobalStyles.d.ts +0 -30
- package/dist/hooks/useGlobalStyles.js +0 -77
- package/dist/hooks/useGlobalStyles.js.map +0 -1
- package/dist/hooks/useKeyframes.d.ts +0 -56
- package/dist/hooks/useKeyframes.js +0 -63
- package/dist/hooks/useKeyframes.js.map +0 -1
- package/dist/hooks/useProperty.d.ts +0 -79
- package/dist/hooks/useProperty.js +0 -108
- package/dist/hooks/useProperty.js.map +0 -1
- package/dist/hooks/useRawCSS.d.ts +0 -53
- package/dist/hooks/useRawCSS.js +0 -34
- package/dist/hooks/useRawCSS.js.map +0 -1
- package/dist/hooks/useStyles.d.ts +0 -40
- package/dist/hooks/useStyles.js +0 -29
- package/dist/hooks/useStyles.js.map +0 -1
- package/dist/injector/index.d.ts +0 -166
- package/dist/injector/index.js +0 -166
- package/dist/injector/index.js.map +0 -1
- package/dist/injector/injector.d.ts +0 -166
- package/dist/injector/injector.js +0 -464
- package/dist/injector/injector.js.map +0 -1
- package/dist/injector/sheet-manager.d.ts +0 -136
- package/dist/injector/sheet-manager.js +0 -733
- package/dist/injector/sheet-manager.js.map +0 -1
- package/dist/injector/types.d.ts +0 -204
- 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 -76
- 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/ssr/async-storage.d.ts +0 -17
- package/dist/ssr/async-storage.js +0 -34
- package/dist/ssr/async-storage.js.map +0 -1
- package/dist/ssr/collect-auto-properties.js +0 -39
- package/dist/ssr/collect-auto-properties.js.map +0 -1
- package/dist/ssr/collector.d.ts +0 -102
- package/dist/ssr/collector.js +0 -219
- package/dist/ssr/collector.js.map +0 -1
- package/dist/ssr/format-global-rules.js +0 -22
- 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 +0 -73
- package/dist/ssr/format-rules.js.map +0 -1
- package/dist/ssr/hydrate.d.ts +0 -22
- package/dist/ssr/hydrate.js +0 -49
- package/dist/ssr/hydrate.js.map +0 -1
- package/dist/ssr/ssr-collector-ref.js +0 -12
- 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 -240
- 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/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/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 +0 -146
- 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 -47
- 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/dist/{chunks/cacheKey.d.ts → ssr/astro-client.d.ts} +0 -0
package/docs/getting-started.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Getting Started
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Build one stateful component first. You will see how Tasty expresses priority as a state map, why overlapping states do not compete, and where shared design-system configuration fits afterward.
|
|
4
|
+
|
|
5
|
+
This is the right starting point when you are ready to try Tasty in code. If you are still evaluating it, read the [Introduction](../README.md), [Comparison](comparison.md), and [Adoption Guide](adoption.md).
|
|
4
6
|
|
|
5
7
|
---
|
|
6
8
|
|
|
@@ -10,7 +12,7 @@ This guide walks you from zero to a working Tasty component, then through the op
|
|
|
10
12
|
- **React** >= 18 (peer dependency)
|
|
11
13
|
- **Package manager**: pnpm, npm, or yarn
|
|
12
14
|
|
|
13
|
-
Tasty
|
|
15
|
+
No Tasty configuration is required for the first component.
|
|
14
16
|
|
|
15
17
|
---
|
|
16
18
|
|
|
@@ -22,36 +24,57 @@ pnpm add @tenphi/tasty
|
|
|
22
24
|
|
|
23
25
|
---
|
|
24
26
|
|
|
25
|
-
##
|
|
27
|
+
## Build a button whose states don’t fight
|
|
26
28
|
|
|
27
29
|
```tsx
|
|
28
30
|
import { tasty } from '@tenphi/tasty';
|
|
29
31
|
|
|
30
|
-
const
|
|
31
|
-
as: '
|
|
32
|
+
const Button = tasty({
|
|
33
|
+
as: 'button',
|
|
32
34
|
styles: {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
padding: '12px 20px',
|
|
36
|
+
radius: '8px',
|
|
37
|
+
border: '0',
|
|
38
|
+
fill: {
|
|
39
|
+
'': 'royalblue',
|
|
40
|
+
':hover': 'blue',
|
|
41
|
+
':active': 'navy',
|
|
42
|
+
disabled: 'lightgray',
|
|
43
|
+
},
|
|
44
|
+
color: {
|
|
45
|
+
'': 'white',
|
|
46
|
+
disabled: 'dimgray',
|
|
47
|
+
},
|
|
48
|
+
cursor: {
|
|
49
|
+
'': 'pointer',
|
|
50
|
+
disabled: 'not-allowed',
|
|
51
|
+
},
|
|
40
52
|
},
|
|
41
53
|
});
|
|
42
54
|
|
|
43
55
|
export default function App() {
|
|
44
|
-
return
|
|
56
|
+
return (
|
|
57
|
+
<>
|
|
58
|
+
<Button>Save changes</Button>
|
|
59
|
+
<Button disabled>Disabled</Button>
|
|
60
|
+
</>
|
|
61
|
+
);
|
|
45
62
|
}
|
|
46
63
|
```
|
|
47
64
|
|
|
48
|
-
`tasty()` returns a normal React component.
|
|
65
|
+
`tasty()` returns a normal React component. The keys in each state map define priority: later branches win over earlier ones. Because `disabled` is last, a disabled button keeps its disabled styles even while the pointer is over it.
|
|
66
|
+
|
|
67
|
+
`disabled` and `checked` are built-in automatic states. Tasty activates them from the corresponding native prop or attribute, so `disabled` is the concise Tasty form of `[disabled]`, and `checked` is the concise form of `[checked]`.
|
|
68
|
+
|
|
69
|
+
Tasty compiles that priority into selectors that exclude one another. The hover, active, and disabled background rules cannot all match and ask the CSS cascade to decide the winner.
|
|
70
|
+
|
|
71
|
+
The example uses ordinary CSS values so it works without setup. A design system will usually replace them with shared tokens, units, and state aliases in the next step. See the [Style DSL](dsl.md) for the complete state-map syntax and [Style Properties](styles.md) for Tasty’s enhanced CSS properties.
|
|
49
72
|
|
|
50
73
|
---
|
|
51
74
|
|
|
52
|
-
##
|
|
75
|
+
## Add your design system’s language
|
|
53
76
|
|
|
54
|
-
Use `configure()` once, before your app renders, when your app or design system
|
|
77
|
+
The first component needs no configuration. Use `configure()` once, before your app renders, when your app or design system is ready to share tokens, state aliases, recipes, units, or parser extensions:
|
|
55
78
|
|
|
56
79
|
```tsx
|
|
57
80
|
// src/tasty-config.ts
|
|
@@ -91,8 +114,8 @@ configure({
|
|
|
91
114
|
'#primary': 'oklch(55% 0.25 265)',
|
|
92
115
|
'#surface': '#fff',
|
|
93
116
|
'#text': '#111',
|
|
94
|
-
|
|
95
|
-
|
|
117
|
+
$gap: '8px',
|
|
118
|
+
$radius: '4px',
|
|
96
119
|
'$border-width': '1px',
|
|
97
120
|
'$outline-width': '2px',
|
|
98
121
|
},
|
|
@@ -148,23 +171,21 @@ export default [
|
|
|
148
171
|
|
|
149
172
|
The `recommended` config enables 18 of the plugin's 27 total rules. It covers the most common issues without turning on the stricter governance rules:
|
|
150
173
|
|
|
151
|
-
| Category
|
|
152
|
-
|
|
153
|
-
| Property validation | `known-property`, `valid-boolean-property`, `valid-sub-element`
|
|
154
|
-
| Value validation
|
|
155
|
-
| State validation
|
|
156
|
-
| Structure
|
|
157
|
-
| Static mode
|
|
158
|
-
| Style properties
|
|
174
|
+
| Category | Rules | Examples |
|
|
175
|
+
| ------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------ |
|
|
176
|
+
| Property validation | `known-property`, `valid-boolean-property`, `valid-sub-element` | Flags typos like `pading` or invalid boolean usage |
|
|
177
|
+
| Value validation | `valid-value`, `valid-color-token`, `valid-custom-unit` | Catches `#nonexistent` tokens, bad unit syntax |
|
|
178
|
+
| State validation | `valid-state-key`, `no-nested-state-map`, `require-default-state` | Validates state key syntax, ensures `''` default exists |
|
|
179
|
+
| Structure | `valid-styles-structure`, `no-important`, `no-nested-selector` | Prevents `!important`, invalid nesting |
|
|
180
|
+
| Static mode | `static-no-dynamic-values`, `static-valid-selector` | Enforces build-time constraints in `tastyStatic()` |
|
|
181
|
+
| Style properties | `valid-preset`, `valid-recipe`, `valid-transition`, `valid-directional-modifier`, `valid-radius-shape` | Validates preset names, recipe references, transition syntax |
|
|
159
182
|
|
|
160
183
|
### Strict config
|
|
161
184
|
|
|
162
185
|
For stricter governance, use `tasty.configs.strict`. It adds rules that enforce best practices like preferring shorthand properties, consistent token usage, and flagging direct `styles` prop usage:
|
|
163
186
|
|
|
164
187
|
```js
|
|
165
|
-
export default [
|
|
166
|
-
tasty.configs.strict,
|
|
167
|
-
];
|
|
188
|
+
export default [tasty.configs.strict];
|
|
168
189
|
```
|
|
169
190
|
|
|
170
191
|
---
|
|
@@ -179,19 +200,19 @@ export default [
|
|
|
179
200
|
|
|
180
201
|
## Choosing a rendering mode
|
|
181
202
|
|
|
182
|
-
|
|
203
|
+
`tasty()` is the default for all React apps. All `tasty()` components and style functions are hook-free and work as React Server Components without `'use client'`. Zero-runtime delivery is not tied to one API: both server-only `tasty()` and build-time `tastyStatic()` can ship no Tasty styling runtime to the browser.
|
|
183
204
|
|
|
184
|
-
| Approach |
|
|
185
|
-
|
|
186
|
-
| **
|
|
187
|
-
| **
|
|
205
|
+
| Approach | Authoring API | CSS is generated | Tasty runtime in the browser | Best for |
|
|
206
|
+
| ------------------------- | ------------------------------------------- | ----------------------------------- | ---------------------------- | ---------------------------------------------------------------- |
|
|
207
|
+
| **Server-only React** | `tasty()`; optional `@tenphi/tasty/ssr/*` | During server or static rendering | None | Astro without islands, server-only RSC, SSG |
|
|
208
|
+
| **Hydrated React** | `tasty()` plus `@tenphi/tasty/ssr/*` | During SSR, then on demand in React | Only in hydrated components | Interactive React apps, Next.js client components, Astro islands |
|
|
209
|
+
| **Build-time extraction** | `tastyStatic()` from `@tenphi/tasty/static` | During the build | None in file mode | Non-React frameworks or extraction before rendering |
|
|
188
210
|
|
|
189
|
-
Both share the same DSL, tokens, units, and state mappings.
|
|
211
|
+
Both `tasty()` and `tastyStatic()` share the same DSL, tokens, units, and state mappings.
|
|
190
212
|
|
|
191
|
-
-
|
|
192
|
-
-
|
|
193
|
-
-
|
|
194
|
-
- SSR works with existing `tasty()` components — wrap your app with a registry, middleware, or collector. See [Server-Side Rendering](ssr.md).
|
|
213
|
+
- **Server-only `tasty()`** keeps the full feature set (`styleProps`, sub-elements, variants) while generating CSS during server rendering. `tasty()` plus `tastyIntegration({ islands: false })` in Astro is the verified concrete setup and ships no client JavaScript. The same server-only architecture applies to Next.js RSC; verify the generated output for your deployment.
|
|
214
|
+
- **Hydrated `tasty()`** uses the SSR integrations to batch and deduplicate server CSS, prevent FOUC, and hydrate the client cache. The browser styling pipeline is available only where React components hydrate. See [Server-Side Rendering](ssr.md).
|
|
215
|
+
- **Build-time extraction** uses the Babel plugin and additional peer dependencies. Choose `tastyStatic()` when CSS must be generated before rendering or when the consumer is not React. File mode ships no styling runtime; inject mode intentionally includes a tiny CSS injector. See [Build-Time Extraction (`tastyStatic`)](tasty-static.md).
|
|
195
216
|
|
|
196
217
|
---
|
|
197
218
|
|
|
@@ -200,7 +221,7 @@ Both share the same DSL, tokens, units, and state mappings.
|
|
|
200
221
|
- **[Docs Hub](README.md)** — Pick the next guide by role, styling approach, or task
|
|
201
222
|
- **[Methodology](methodology.md)** — The recommended patterns for structuring Tasty components: sub-elements, styleProps, tokens, extension
|
|
202
223
|
- **[Style DSL](dsl.md)** — State maps, tokens, units, extending semantics, keyframes, @property
|
|
203
|
-
- **[
|
|
224
|
+
- **[React API](react-api.md)** — `tasty()` factory, component props, variants, sub-elements, style functions
|
|
204
225
|
- **[Building a Design System](design-system.md)** — Practical guide to building a DS layer with Tasty: tokens, recipes, primitives, compound components
|
|
205
226
|
- **[Adoption Guide](adoption.md)** — Roll out Tasty inside an existing design system or platform team
|
|
206
227
|
- **[Comparison](comparison.md)** — Evaluate Tasty against other styling systems
|
|
@@ -214,4 +235,4 @@ Both share the same DSL, tokens, units, and state mappings.
|
|
|
214
235
|
|
|
215
236
|
- Styles are missing on first render: make sure the file that calls `configure()` is imported before any `tasty()` component renders.
|
|
216
237
|
- Token or unit values are not what you expect: check your `configure({ tokens, units })` setup, then inspect the generated CSS variables with [Debug Utilities](debug.md).
|
|
217
|
-
- You need
|
|
238
|
+
- You need zero-runtime delivery: use server-only [`tasty()` with server rendering](ssr.md) to keep the full React API, or use [`tastyStatic()` build-time extraction](tasty-static.md) when styles must be compiled before rendering.
|
package/docs/injector.md
CHANGED
|
@@ -9,9 +9,9 @@ A high-performance CSS-in-JS solution that powers the Tasty design system with e
|
|
|
9
9
|
The Style Injector is the core engine behind Tasty's styling system, providing:
|
|
10
10
|
|
|
11
11
|
- **Hash-based deduplication** - Identical CSS gets the same className
|
|
12
|
-
- **
|
|
12
|
+
- **DOM-driven lifetime** - Styles are collected once nothing renders them
|
|
13
13
|
- **CSS nesting flattening** - Handles `&`, `.Class`, `SubElement` patterns
|
|
14
|
-
- **
|
|
14
|
+
- **At-rule injection** - First-class `@keyframes`, `@property`, `@font-face`, `@counter-style`, and `@function` support
|
|
15
15
|
- **Smart cleanup** - CSS rules batched cleanup, keyframes disposed immediately
|
|
16
16
|
- **SSR support** - Deterministic class names and CSS extraction
|
|
17
17
|
- **Multiple roots** - Works with Document and ShadowRoot
|
|
@@ -63,10 +63,16 @@ const result = inject([{
|
|
|
63
63
|
|
|
64
64
|
console.log(result.className); // 't-abc123'
|
|
65
65
|
|
|
66
|
-
//
|
|
66
|
+
// Release the pin; the class becomes collectible once nothing renders it
|
|
67
67
|
result.dispose();
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
+
`inject()` **pins** the class it returns, and `gc()` never evicts a pinned class
|
|
71
|
+
— that is what `dispose()` releases. Pass `{ pin: false }` when the caller keeps
|
|
72
|
+
no handle and the DOM is the only record that the class is in use; `dispose()`
|
|
73
|
+
is then a no-op. The render path (`tasty()` / `computeStyles()`) injects this
|
|
74
|
+
way, because a hook-free render has no unmount signal to dispose on.
|
|
75
|
+
|
|
70
76
|
### `injectGlobal(rules, options?): { dispose: () => void }`
|
|
71
77
|
|
|
72
78
|
Injects global styles that don't reserve tasty class names.
|
|
@@ -115,11 +121,13 @@ dispose();
|
|
|
115
121
|
|
|
116
122
|
### `useRawCSS(css, options?)` or `useRawCSS(factory, deps, options?)`
|
|
117
123
|
|
|
118
|
-
|
|
124
|
+
Inject raw CSS without parsing. Hook-free — works in client components, SSR, and React Server Components.
|
|
119
125
|
|
|
120
126
|
Supports two overloads:
|
|
121
|
-
- **Static CSS**: `useRawCSS(cssString, options?)`
|
|
122
|
-
- **Factory function**: `useRawCSS(() => cssString, deps, options?)`
|
|
127
|
+
- **Static CSS**: `useRawCSS(cssString, options?)` — content-based deduplication
|
|
128
|
+
- **Factory function**: `useRawCSS(() => cssString, deps, options?)` — factory called on every invocation, dedup handled internally
|
|
129
|
+
|
|
130
|
+
Use the `id` option for update tracking — when the CSS changes for the same id, the previous injection is replaced:
|
|
123
131
|
|
|
124
132
|
```tsx
|
|
125
133
|
import { useRawCSS } from '@tenphi/tasty';
|
|
@@ -132,7 +140,7 @@ function GlobalReset() {
|
|
|
132
140
|
return null;
|
|
133
141
|
}
|
|
134
142
|
|
|
135
|
-
// Dynamic CSS with factory function
|
|
143
|
+
// Dynamic CSS with factory function and update tracking
|
|
136
144
|
function ThemeStyles({ theme }: { theme: 'dark' | 'light' }) {
|
|
137
145
|
useRawCSS(() => `
|
|
138
146
|
body {
|
|
@@ -140,7 +148,7 @@ function ThemeStyles({ theme }: { theme: 'dark' | 'light' }) {
|
|
|
140
148
|
background: ${theme === 'dark' ? '#000' : '#fff'};
|
|
141
149
|
color: ${theme === 'dark' ? '#fff' : '#000'};
|
|
142
150
|
}
|
|
143
|
-
`, [theme]);
|
|
151
|
+
`, [theme], { id: 'theme-body' });
|
|
144
152
|
|
|
145
153
|
return null;
|
|
146
154
|
}
|
|
@@ -207,13 +215,12 @@ import { configure } from '@tenphi/tasty';
|
|
|
207
215
|
configure({
|
|
208
216
|
devMode: true, // Enable development features (auto-detected)
|
|
209
217
|
maxRulesPerSheet: 8192, // Cap rules per stylesheet (default: 8192)
|
|
210
|
-
unusedStylesThreshold: 500, // Trigger cleanup threshold (CSS rules only)
|
|
211
|
-
bulkCleanupDelay: 5000, // Cleanup delay (ms) - ignored if idleCleanup is true
|
|
212
|
-
idleCleanup: true, // Use requestIdleCallback for cleanup
|
|
213
|
-
bulkCleanupBatchRatio: 0.5, // Clean up oldest 50% per batch
|
|
214
|
-
unusedStylesMinAgeMs: 10000, // Minimum age before cleanup (ms)
|
|
215
218
|
forceTextInjection: false, // Force textContent insertion (auto-detected for tests)
|
|
216
219
|
nonce: 'csp-nonce', // CSP nonce for security
|
|
220
|
+
gc: { // Garbage collection for unused styles
|
|
221
|
+
touchInterval: 1000, // Touch events between GC cycles (default: 1000)
|
|
222
|
+
capacity: 1000, // Max unused styles to retain (default: 1000)
|
|
223
|
+
},
|
|
217
224
|
states: { // Global predefined states for advanced state mapping
|
|
218
225
|
'@mobile': '@media(w < 768px)',
|
|
219
226
|
'@dark': '@root(schema=dark)',
|
|
@@ -223,13 +230,75 @@ configure({
|
|
|
223
230
|
|
|
224
231
|
**Auto-Detection Features:**
|
|
225
232
|
- `devMode`: Automatically enabled in development environments (detected via `isDevEnv()`)
|
|
226
|
-
- `forceTextInjection`: Automatically enabled in test environments (Jest, Vitest, Mocha, jsdom)
|
|
233
|
+
- `forceTextInjection`: Automatically enabled in test environments (Jest, Vitest, Mocha, happy-dom, jsdom)
|
|
234
|
+
|
|
235
|
+
**Injection Modes:**
|
|
236
|
+
|
|
237
|
+
Each sheet picks its write mode once, when it is created, and keeps it for its
|
|
238
|
+
lifetime:
|
|
239
|
+
|
|
240
|
+
| Mode | How rules are written | How rules are removed |
|
|
241
|
+
|---|---|---|
|
|
242
|
+
| CSSOM (default) | `styleSheet.insertRule(rule, index)` | `styleSheet.deleteRule(index)` |
|
|
243
|
+
| Text (`forceTextInjection`, or when `styleElement.sheet` is unavailable) | appended to `<style>.textContent` | rule texts are tracked per sheet and the element's text is rebuilt without them |
|
|
244
|
+
| Adopted (ShadowRoot with constructable sheets) | `insertRule` on the constructable sheet | `deleteRule` on the constructable sheet |
|
|
245
|
+
|
|
246
|
+
Text mode cannot edit a single rule in place the way CSSOM can, so the sheet
|
|
247
|
+
keeps the inserted rule texts in index order and rewrites the element on delete.
|
|
248
|
+
Dispose, ref-counted cleanup and GC therefore behave identically in every mode.
|
|
227
249
|
|
|
228
250
|
**Configuration Notes:**
|
|
229
251
|
- Most options have sensible defaults and auto-detection
|
|
230
252
|
- `configure()` is optional - the injector works with defaults
|
|
231
253
|
- **Configuration is locked after styles are generated** - calling `configure()` after first render will emit a warning and be ignored
|
|
232
|
-
- `
|
|
254
|
+
- `gc.touchInterval`: Number of renders between sweeps. When the counter reaches this value, a sweep is scheduled via `requestIdleCallback`; without idle callbacks nothing is collected automatically.
|
|
255
|
+
- `gc.grace`: How long a class is left alone after collection first notices nothing is carrying it, in milliseconds (default `10000`).
|
|
256
|
+
|
|
257
|
+
**What a sweep does.** Everything the injector holds falls into one of five bands, and only the last is ever deleted:
|
|
258
|
+
|
|
259
|
+
| Band | | Deleted |
|
|
260
|
+
|---|---|---|
|
|
261
|
+
| 1 | **Rendered** — some element carries the class right now | never |
|
|
262
|
+
| 2 | **Not ours** — queued for a batched write, pre-allocated, or server-rendered | never |
|
|
263
|
+
| 3 | **Hot** — nothing carries it, but that was noticed less than `grace` ago | never |
|
|
264
|
+
| 4 | **Cached** — cold, but within `capacity` when ordered by when it went cold | never |
|
|
265
|
+
| 5 | Everything else | on every sweep |
|
|
266
|
+
|
|
267
|
+
`gc({ force: true })` and `cleanup()` take bands 4 and 5 together, ignoring capacity. Band 3 is spared even then: an explicit cleanup is still no reason to take rules from a render that has not committed yet.
|
|
268
|
+
|
|
269
|
+
> **Why band 3 exists.** Rendering is not commit-aware: a render can resolve a class and commit it a little later, and in between nothing on the page carries it. From outside React that is indistinguishable from a class that is finished, so collection does not try to tell them apart — it leaves alone anything only just noticed to be cold. A render would have to stay pending for the whole window to lose its rules, and it gets them back on its next render.
|
|
270
|
+
>
|
|
271
|
+
> The clock starts when a sweep *notices*, not when the element actually left — nothing observes that moment, so starting it at the sighting is what gives every class the same full window however long ago it went.
|
|
272
|
+
>
|
|
273
|
+
> This is also why collection costs nothing to run: the timestamps are written by the sweep's own DOM scan, so rendering tracks nothing per class.
|
|
274
|
+
- `gc.capacity`: Maximum number of unused styles (not in the DOM, not pinned) to retain. When exceeded, the least recently used are evicted first. Rendered and pinned styles don't count against this limit.
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
### At-rule injection
|
|
279
|
+
|
|
280
|
+
Beyond ordinary rules, the injector has a dedicated entry point per at-rule. All of them deduplicate by name and are **permanent** — there is no ref-counting or dispose, because a rule that other CSS references by name cannot be safely removed while any of it might still apply.
|
|
281
|
+
|
|
282
|
+
| Function | Injects | Notes |
|
|
283
|
+
|---|---|---|
|
|
284
|
+
| `keyframes(steps, nameOrOptions?)` | `@keyframes` | The exception: ref-counted and disposed immediately when unused. Returns the injected name, which may differ from the requested one. |
|
|
285
|
+
| `property(name, options?)` | `@property` | `name` accepts `$name`, `#name`, or `--name`. Usually unnecessary — types are inferred automatically unless `configure({ autoPropertyTypes: false })`. |
|
|
286
|
+
| `fontFace(family, descriptors, options?)` | `@font-face` | Deduplicated by family **and** descriptor content, so several faces of one family coexist. |
|
|
287
|
+
| `counterStyle(name, descriptors, options?)` | `@counter-style` | |
|
|
288
|
+
| `func(name, definition, options?)` | `@function` | `name` accepts `$$name`, `$name`, or `--name`. Abbreviated because `function` is a reserved word. |
|
|
289
|
+
|
|
290
|
+
Every one of these takes an optional `root` (a `Document` or `ShadowRoot`).
|
|
291
|
+
|
|
292
|
+
`func()` also accepts `weak: true`, which registers the rule **without** overriding an existing one of the same name. `configure()` uses it for global `@function` definitions so a component-local definition always wins over the global default, regardless of which is injected first.
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
import { counterStyle, fontFace, func, property } from '@tenphi/tasty';
|
|
296
|
+
|
|
297
|
+
property('$card-elevation', { syntax: '<number>', initialValue: '0' });
|
|
298
|
+
fontFace('Inter', { src: 'url(/inter.woff2)', fontWeight: '400 700' });
|
|
299
|
+
counterStyle('dashes', { system: 'cyclic', symbols: '"—"', suffix: ' ' });
|
|
300
|
+
func('$$negative', { args: ['$value'], result: '(-1 * $value)' });
|
|
301
|
+
```
|
|
233
302
|
|
|
234
303
|
---
|
|
235
304
|
|
|
@@ -256,6 +325,17 @@ const styleRule: StyleResult = {
|
|
|
256
325
|
};
|
|
257
326
|
```
|
|
258
327
|
|
|
328
|
+
### Batched Injection
|
|
329
|
+
|
|
330
|
+
`configure({ batchInjection: true })` queues every sheet write — component
|
|
331
|
+
rules, global rules, raw CSS and at-rules — into one FIFO and applies them
|
|
332
|
+
together, so the document is style-invalidated once per flush instead of once per
|
|
333
|
+
component. `<TastyBatchProvider>` flushes in `useInsertionEffect`, before any
|
|
334
|
+
layout effect, so a queued write can never be observed by a measurement. See
|
|
335
|
+
[Batched injection](configuration.md#batched-injection) for the modes and the
|
|
336
|
+
ordering guarantee. `flushStyles()` applies pending writes on demand; every read
|
|
337
|
+
API here calls it for you.
|
|
338
|
+
|
|
259
339
|
### Deduplication & Performance
|
|
260
340
|
|
|
261
341
|
```typescript
|
|
@@ -274,40 +354,65 @@ const button2 = inject([{
|
|
|
274
354
|
console.log(button1.className === button2.className); // true
|
|
275
355
|
```
|
|
276
356
|
|
|
277
|
-
###
|
|
357
|
+
### Pinning
|
|
278
358
|
|
|
279
359
|
```typescript
|
|
280
|
-
// Multiple
|
|
360
|
+
// Multiple callers using the same styles
|
|
281
361
|
const comp1 = inject([commonStyle]);
|
|
282
362
|
const comp2 = inject([commonStyle]);
|
|
283
363
|
const comp3 = inject([commonStyle]);
|
|
284
364
|
|
|
285
|
-
// Style is
|
|
286
|
-
comp1.dispose(); //
|
|
287
|
-
comp2.dispose(); //
|
|
288
|
-
comp3.dispose(); //
|
|
365
|
+
// Style is pinned while any caller holds a handle
|
|
366
|
+
comp1.dispose(); // pins: 3 → 2
|
|
367
|
+
comp2.dispose(); // pins: 2 → 1
|
|
368
|
+
comp3.dispose(); // pins: 1 → 0, now up to the DOM and gc()
|
|
289
369
|
|
|
290
|
-
//
|
|
291
|
-
//
|
|
370
|
+
// Unpinned does not mean deleted: the rule stays cached and is reused instantly
|
|
371
|
+
// by the next inject(). gc() decides when it actually goes.
|
|
292
372
|
```
|
|
293
373
|
|
|
294
|
-
|
|
374
|
+
Pinning covers callers that hold a handle. Styles that come from rendering are
|
|
375
|
+
never pinned — see `{ pin: false }` under
|
|
376
|
+
[`inject()`](#injectrules-options-injectresult) — so what keeps them alive is
|
|
377
|
+
being in the DOM, and nothing else.
|
|
378
|
+
|
|
379
|
+
### Garbage Collection
|
|
295
380
|
|
|
296
381
|
```typescript
|
|
297
|
-
import { configure } from '@tenphi/tasty';
|
|
382
|
+
import { configure, gc } from '@tenphi/tasty';
|
|
298
383
|
|
|
299
|
-
// CSS rules: Not immediately deleted, marked for bulk cleanup (refCount = 0)
|
|
300
384
|
// Keyframes: Disposed immediately when refCount = 0 (safer for global scope)
|
|
385
|
+
// CSS rules: Tracked by touch count and cleaned up via gc()
|
|
386
|
+
//
|
|
387
|
+
// A CSS rule is collectible when no element carries its class AND nobody
|
|
388
|
+
// pinned it with inject().
|
|
301
389
|
|
|
302
390
|
configure({
|
|
303
|
-
|
|
304
|
-
|
|
391
|
+
gc: {
|
|
392
|
+
touchInterval: 1000, // Schedule GC every 1000 touches
|
|
393
|
+
capacity: 1000, // Max unused styles to retain
|
|
394
|
+
},
|
|
305
395
|
});
|
|
306
396
|
|
|
397
|
+
// Manual GC (synchronous, returns number of swept styles):
|
|
398
|
+
gc();
|
|
399
|
+
|
|
400
|
+
// Force-remove ALL unused styles (e.g. on route change or test teardown):
|
|
401
|
+
gc({ force: true });
|
|
402
|
+
|
|
403
|
+
// cleanup() is the same thing:
|
|
404
|
+
cleanup();
|
|
405
|
+
|
|
406
|
+
// Every `touchInterval` renders, a sweep is scheduled in idle time. It scans
|
|
407
|
+
// the DOM for the classes actually on the page and sorts everything the
|
|
408
|
+
// injector holds into five bands — only the last one is deleted. See below.
|
|
409
|
+
|
|
307
410
|
// Benefits:
|
|
308
|
-
// -
|
|
411
|
+
// - Activity-proportional: busy apps trigger GC more often
|
|
412
|
+
// - DOM-safe: styles currently in the DOM are never evicted
|
|
413
|
+
// - Oldest-first: least recently used styles are evicted first
|
|
309
414
|
// - Keyframes: Immediate cleanup prevents global namespace pollution
|
|
310
|
-
// - Unused styles
|
|
415
|
+
// - Unused styles stay cached until evicted, so re-rendering them is a cache hit
|
|
311
416
|
```
|
|
312
417
|
|
|
313
418
|
### Shadow DOM Support
|
|
@@ -335,14 +440,14 @@ const shadowAnimation = keyframes({
|
|
|
335
440
|
### Server-Side Rendering
|
|
336
441
|
|
|
337
442
|
```typescript
|
|
338
|
-
import {
|
|
443
|
+
import { getCSSText, getCSSTextForNode } from '@tenphi/tasty';
|
|
339
444
|
|
|
340
445
|
// Extract all CSS for SSR
|
|
341
|
-
const cssText =
|
|
446
|
+
const cssText = getCSSText();
|
|
342
447
|
|
|
343
448
|
// Extract CSS for specific DOM subtree (like jest-styled-components)
|
|
344
449
|
const container = render(<MyComponent />);
|
|
345
|
-
const componentCSS =
|
|
450
|
+
const componentCSS = getCSSTextForNode(container);
|
|
346
451
|
```
|
|
347
452
|
|
|
348
453
|
### Test Environment Detection
|
|
@@ -351,7 +456,7 @@ const componentCSS = getCssTextForNode(container);
|
|
|
351
456
|
// Automatically detected test environments:
|
|
352
457
|
// - NODE_ENV === 'test'
|
|
353
458
|
// - Jest globals (jest, describe, it, expect)
|
|
354
|
-
// - jsdom user agent
|
|
459
|
+
// - jsdom / HappyDOM user agent
|
|
355
460
|
// - Vitest globals (vitest)
|
|
356
461
|
// - Mocha globals (mocha)
|
|
357
462
|
|
|
@@ -402,11 +507,11 @@ const metrics = injector.instance.getMetrics();
|
|
|
402
507
|
console.log({
|
|
403
508
|
cacheHits: metrics.hits, // Successful cache hits
|
|
404
509
|
cacheMisses: metrics.misses, // New styles injected
|
|
405
|
-
unusedHits: metrics.unusedHits, //
|
|
510
|
+
unusedHits: metrics.unusedHits, // Styles currently eligible for eviction (scans the DOM)
|
|
406
511
|
bulkCleanups: metrics.bulkCleanups, // Number of bulk cleanup operations
|
|
407
512
|
stylesCleanedUp: metrics.stylesCleanedUp, // Total styles removed in bulk cleanups
|
|
408
513
|
totalInsertions: metrics.totalInsertions, // Lifetime insertions
|
|
409
|
-
totalUnused: metrics.totalUnused, //
|
|
514
|
+
totalUnused: metrics.totalUnused, // Times a pinned style lost its last pin
|
|
410
515
|
startTime: metrics.startTime, // Metrics collection start timestamp
|
|
411
516
|
cleanupHistory: metrics.cleanupHistory, // Detailed cleanup operation history
|
|
412
517
|
});
|
|
@@ -450,10 +555,10 @@ metrics.cleanupHistory.forEach(cleanup => {
|
|
|
450
555
|
|
|
451
556
|
```typescript
|
|
452
557
|
// ✅ Reuse styles - identical CSS gets deduplicated
|
|
453
|
-
const buttonBase =
|
|
558
|
+
const buttonBase = 'padding: 8px 16px; border-radius: 4px;';
|
|
454
559
|
|
|
455
560
|
// ✅ Avoid frequent disposal and re-injection
|
|
456
|
-
// Let the
|
|
561
|
+
// Let the injector handle cleanup
|
|
457
562
|
|
|
458
563
|
// ✅ Use bulk operations for global styles
|
|
459
564
|
injectGlobal([
|
|
@@ -462,13 +567,14 @@ injectGlobal([
|
|
|
462
567
|
{ selector: '.container', declarations: 'max-width: 1200px;' }
|
|
463
568
|
]);
|
|
464
569
|
|
|
465
|
-
// ✅ Configure
|
|
570
|
+
// ✅ Configure GC for your app (BEFORE first render!)
|
|
466
571
|
import { configure } from '@tenphi/tasty';
|
|
467
572
|
|
|
468
573
|
configure({
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
574
|
+
gc: {
|
|
575
|
+
touchInterval: 1000, // Schedule GC every 1000 style touches
|
|
576
|
+
capacity: 1000, // Max unused styles to retain
|
|
577
|
+
},
|
|
472
578
|
});
|
|
473
579
|
```
|
|
474
580
|
|
|
@@ -478,13 +584,13 @@ configure({
|
|
|
478
584
|
// The injector automatically manages memory through:
|
|
479
585
|
|
|
480
586
|
// 1. Hash-based deduplication - same CSS = same className
|
|
481
|
-
// 2.
|
|
482
|
-
// 3.
|
|
483
|
-
// 4.
|
|
484
|
-
// 5.
|
|
587
|
+
// 2. DOM-driven lifetime - a rendered class is never evicted
|
|
588
|
+
// 3. Pinning - inject() callers hold their classes until they dispose
|
|
589
|
+
// 4. Immediate keyframes cleanup - disposed instantly when refCount = 0
|
|
590
|
+
// 5. Touch-count GC - unused CSS rules are evicted oldest-first when over capacity
|
|
485
591
|
|
|
486
592
|
// Manual cleanup is rarely needed but available:
|
|
487
|
-
cleanup(); //
|
|
593
|
+
cleanup(); // Remove every rule that is neither rendered nor pinned
|
|
488
594
|
destroy(); // Nuclear option: remove all stylesheets and reset
|
|
489
595
|
```
|
|
490
596
|
|
|
@@ -506,12 +612,12 @@ const StyledButton = tasty({
|
|
|
506
612
|
|
|
507
613
|
// Internally uses the injector:
|
|
508
614
|
// 1. Styles are parsed into StyleResult objects
|
|
509
|
-
// 2. inject() is called with the parsed results
|
|
615
|
+
// 2. inject() is called with the parsed results, unpinned
|
|
510
616
|
// 3. Component gets the returned className
|
|
511
|
-
// 4.
|
|
617
|
+
// 4. gc() reclaims the class once no element carries it
|
|
512
618
|
```
|
|
513
619
|
|
|
514
|
-
For most development, you'll use the [
|
|
620
|
+
For most development, you'll use the [React API](./react-api.md) rather than the injector directly. The injector provides the high-performance foundation that makes Tasty's declarative styling possible.
|
|
515
621
|
|
|
516
622
|
---
|
|
517
623
|
|
|
@@ -525,4 +631,4 @@ Direct injector usage is recommended for:
|
|
|
525
631
|
- **Performance-critical scenarios** where you need direct control
|
|
526
632
|
- **Testing utilities** that need to inject or extract CSS
|
|
527
633
|
|
|
528
|
-
For regular component styling, prefer the [`tasty()` API](./
|
|
634
|
+
For regular component styling, prefer the [`tasty()` API](./react-api.md) which provides a more developer-friendly interface.
|