@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.
Files changed (357) hide show
  1. package/README.md +181 -131
  2. package/dist/{ssr/async-storage.js → async-storage-DKK-wTD4.js} +2 -2
  3. package/dist/async-storage-DKK-wTD4.js.map +1 -0
  4. package/dist/babel-eMs_zZjM.d.ts +83 -0
  5. package/dist/{ssr/collector.js → collector-BjkQEtbh.js} +111 -38
  6. package/dist/collector-BjkQEtbh.js.map +1 -0
  7. package/dist/{ssr/collector.d.ts → collector-DzeNxHOq.d.ts} +58 -7
  8. package/dist/config-DMslHK_e.d.ts +1298 -0
  9. package/dist/config-DX126v_w.js +12318 -0
  10. package/dist/config-DX126v_w.js.map +1 -0
  11. package/dist/{ssr/context.js → context-CA8YKeMn.js} +11 -3
  12. package/dist/context-CA8YKeMn.js.map +1 -0
  13. package/dist/core/index.d.ts +5 -34
  14. package/dist/core/index.js +6 -27
  15. package/dist/core-CMn-Tj2X.js +1573 -0
  16. package/dist/core-CMn-Tj2X.js.map +1 -0
  17. package/dist/{zero/extractor.js → css-writer-C8ShXK5R.js} +148 -24
  18. package/dist/css-writer-C8ShXK5R.js.map +1 -0
  19. package/dist/{ssr/format-global-rules.js → format-global-rules-DklyaXv-.js} +2 -2
  20. package/dist/format-global-rules-DklyaXv-.js.map +1 -0
  21. package/dist/{ssr/format-rules.js → format-rules-BkstDvlN.js} +59 -2
  22. package/dist/format-rules-BkstDvlN.js.map +1 -0
  23. package/dist/{ssr/hydrate.js → hydrate-DXXWZgm0.js} +4 -12
  24. package/dist/hydrate-DXXWZgm0.js.map +1 -0
  25. package/dist/index-BcsaHo8x.d.ts +1568 -0
  26. package/dist/index-CC0wykKn.d.ts +1909 -0
  27. package/dist/index.d.ts +5 -51
  28. package/dist/index.js +922 -36
  29. package/dist/index.js.map +1 -0
  30. package/dist/keyframes-Z0YLVOWq.js +493 -0
  31. package/dist/keyframes-Z0YLVOWq.js.map +1 -0
  32. package/dist/{utils/merge-styles.d.ts → merge-styles-BTFAkYSD.d.ts} +3 -3
  33. package/dist/{utils/merge-styles.js → merge-styles-Dpueyn82.js} +3 -4
  34. package/dist/merge-styles-Dpueyn82.js.map +1 -0
  35. package/dist/{utils/resolve-recipes.js → resolve-recipes-B2Vjr7ET.js} +4 -6
  36. package/dist/resolve-recipes-B2Vjr7ET.js.map +1 -0
  37. package/dist/ssr/astro-client.js +1 -1
  38. package/dist/ssr/astro-client.js.map +1 -1
  39. package/dist/ssr/astro-middleware-static.d.ts +16 -0
  40. package/dist/ssr/astro-middleware-static.js +18 -0
  41. package/dist/ssr/astro-middleware-static.js.map +1 -0
  42. package/dist/ssr/astro-middleware.d.ts +8 -6
  43. package/dist/ssr/astro-middleware.js +10 -10
  44. package/dist/ssr/astro-middleware.js.map +1 -1
  45. package/dist/ssr/astro.d.ts +2 -17
  46. package/dist/ssr/astro.js +29 -21
  47. package/dist/ssr/astro.js.map +1 -1
  48. package/dist/ssr/index.d.ts +37 -4
  49. package/dist/ssr/index.js +5 -5
  50. package/dist/ssr/index.js.map +1 -1
  51. package/dist/ssr/next.d.ts +2 -3
  52. package/dist/ssr/next.js +7 -8
  53. package/dist/ssr/next.js.map +1 -1
  54. package/dist/static/index.d.ts +91 -5
  55. package/dist/static/index.js +49 -3
  56. package/dist/static/index.js.map +1 -0
  57. package/dist/static/inject.js.map +1 -1
  58. package/dist/zero/babel.d.ts +2 -81
  59. package/dist/zero/babel.js +45 -29
  60. package/dist/zero/babel.js.map +1 -1
  61. package/dist/zero/index.d.ts +81 -3
  62. package/dist/zero/index.js +2 -3
  63. package/dist/zero/next.d.ts +1 -1
  64. package/dist/zero/next.js +1 -1
  65. package/dist/zero/next.js.map +1 -1
  66. package/docs/README.md +18 -10
  67. package/docs/adoption.md +56 -31
  68. package/docs/ai-agents.md +224 -0
  69. package/docs/comparison.md +69 -40
  70. package/docs/configuration.md +501 -72
  71. package/docs/debug.md +22 -18
  72. package/docs/design-system.md +43 -24
  73. package/docs/dsl.md +512 -179
  74. package/docs/getting-started.md +60 -39
  75. package/docs/injector.md +110 -32
  76. package/docs/methodology.md +68 -36
  77. package/docs/migration-v3.md +285 -0
  78. package/docs/pipeline.md +741 -0
  79. package/docs/plugins.md +353 -0
  80. package/docs/react-api.md +164 -40
  81. package/docs/runtime-benchmarks.md +216 -0
  82. package/docs/ssr.md +92 -81
  83. package/docs/styles.md +65 -16
  84. package/docs/tasty-static.md +149 -99
  85. package/package.json +49 -21
  86. package/tasty.config.ts +6 -0
  87. package/dist/_virtual/_rolldown/runtime.js +0 -7
  88. package/dist/chunks/cacheKey.d.ts +0 -1
  89. package/dist/chunks/cacheKey.js +0 -77
  90. package/dist/chunks/cacheKey.js.map +0 -1
  91. package/dist/chunks/definitions.d.ts +0 -37
  92. package/dist/chunks/definitions.js +0 -258
  93. package/dist/chunks/definitions.js.map +0 -1
  94. package/dist/chunks/index.d.ts +0 -1
  95. package/dist/chunks/renderChunk.d.ts +0 -1
  96. package/dist/chunks/renderChunk.js +0 -59
  97. package/dist/chunks/renderChunk.js.map +0 -1
  98. package/dist/compute-styles.d.ts +0 -31
  99. package/dist/compute-styles.js +0 -335
  100. package/dist/compute-styles.js.map +0 -1
  101. package/dist/config.d.ts +0 -407
  102. package/dist/config.js +0 -587
  103. package/dist/config.js.map +0 -1
  104. package/dist/counter-style/index.js +0 -51
  105. package/dist/counter-style/index.js.map +0 -1
  106. package/dist/debug.d.ts +0 -89
  107. package/dist/debug.js +0 -453
  108. package/dist/debug.js.map +0 -1
  109. package/dist/font-face/index.js +0 -63
  110. package/dist/font-face/index.js.map +0 -1
  111. package/dist/hooks/index.d.ts +0 -7
  112. package/dist/hooks/useCounterStyle.d.ts +0 -36
  113. package/dist/hooks/useCounterStyle.js +0 -65
  114. package/dist/hooks/useCounterStyle.js.map +0 -1
  115. package/dist/hooks/useFontFace.d.ts +0 -45
  116. package/dist/hooks/useFontFace.js +0 -66
  117. package/dist/hooks/useFontFace.js.map +0 -1
  118. package/dist/hooks/useGlobalStyles.d.ts +0 -46
  119. package/dist/hooks/useGlobalStyles.js +0 -88
  120. package/dist/hooks/useGlobalStyles.js.map +0 -1
  121. package/dist/hooks/useKeyframes.d.ts +0 -58
  122. package/dist/hooks/useKeyframes.js +0 -55
  123. package/dist/hooks/useKeyframes.js.map +0 -1
  124. package/dist/hooks/useProperty.d.ts +0 -81
  125. package/dist/hooks/useProperty.js +0 -96
  126. package/dist/hooks/useProperty.js.map +0 -1
  127. package/dist/hooks/useRawCSS.d.ts +0 -22
  128. package/dist/hooks/useRawCSS.js +0 -103
  129. package/dist/hooks/useRawCSS.js.map +0 -1
  130. package/dist/hooks/useStyles.d.ts +0 -40
  131. package/dist/hooks/useStyles.js +0 -31
  132. package/dist/hooks/useStyles.js.map +0 -1
  133. package/dist/injector/index.d.ts +0 -182
  134. package/dist/injector/index.js +0 -185
  135. package/dist/injector/index.js.map +0 -1
  136. package/dist/injector/injector.d.ts +0 -198
  137. package/dist/injector/injector.js +0 -651
  138. package/dist/injector/injector.js.map +0 -1
  139. package/dist/injector/sheet-manager.d.ts +0 -132
  140. package/dist/injector/sheet-manager.js +0 -699
  141. package/dist/injector/sheet-manager.js.map +0 -1
  142. package/dist/injector/types.d.ts +0 -235
  143. package/dist/keyframes/index.js +0 -206
  144. package/dist/keyframes/index.js.map +0 -1
  145. package/dist/parser/classify.js +0 -319
  146. package/dist/parser/classify.js.map +0 -1
  147. package/dist/parser/const.js +0 -60
  148. package/dist/parser/const.js.map +0 -1
  149. package/dist/parser/lru.js +0 -109
  150. package/dist/parser/lru.js.map +0 -1
  151. package/dist/parser/parser.d.ts +0 -25
  152. package/dist/parser/parser.js +0 -115
  153. package/dist/parser/parser.js.map +0 -1
  154. package/dist/parser/tokenizer.js +0 -69
  155. package/dist/parser/tokenizer.js.map +0 -1
  156. package/dist/parser/types.d.ts +0 -51
  157. package/dist/parser/types.js +0 -46
  158. package/dist/parser/types.js.map +0 -1
  159. package/dist/pipeline/conditions.d.ts +0 -134
  160. package/dist/pipeline/conditions.js +0 -406
  161. package/dist/pipeline/conditions.js.map +0 -1
  162. package/dist/pipeline/exclusive.js +0 -230
  163. package/dist/pipeline/exclusive.js.map +0 -1
  164. package/dist/pipeline/index.d.ts +0 -55
  165. package/dist/pipeline/index.js +0 -708
  166. package/dist/pipeline/index.js.map +0 -1
  167. package/dist/pipeline/materialize.js +0 -1103
  168. package/dist/pipeline/materialize.js.map +0 -1
  169. package/dist/pipeline/parseStateKey.d.ts +0 -15
  170. package/dist/pipeline/parseStateKey.js +0 -446
  171. package/dist/pipeline/parseStateKey.js.map +0 -1
  172. package/dist/pipeline/simplify.js +0 -515
  173. package/dist/pipeline/simplify.js.map +0 -1
  174. package/dist/pipeline/warnings.js +0 -18
  175. package/dist/pipeline/warnings.js.map +0 -1
  176. package/dist/plugins/index.d.ts +0 -2
  177. package/dist/plugins/okhsl-plugin.d.ts +0 -35
  178. package/dist/plugins/okhsl-plugin.js +0 -97
  179. package/dist/plugins/okhsl-plugin.js.map +0 -1
  180. package/dist/plugins/types.d.ts +0 -87
  181. package/dist/properties/index.js +0 -222
  182. package/dist/properties/index.js.map +0 -1
  183. package/dist/properties/property-type-resolver.d.ts +0 -24
  184. package/dist/properties/property-type-resolver.js +0 -90
  185. package/dist/properties/property-type-resolver.js.map +0 -1
  186. package/dist/rsc-cache.js +0 -79
  187. package/dist/rsc-cache.js.map +0 -1
  188. package/dist/ssr/async-storage.d.ts +0 -17
  189. package/dist/ssr/async-storage.js.map +0 -1
  190. package/dist/ssr/collect-auto-properties.js +0 -58
  191. package/dist/ssr/collect-auto-properties.js.map +0 -1
  192. package/dist/ssr/collector.js.map +0 -1
  193. package/dist/ssr/context.js.map +0 -1
  194. package/dist/ssr/format-global-rules.js.map +0 -1
  195. package/dist/ssr/format-keyframes.js +0 -69
  196. package/dist/ssr/format-keyframes.js.map +0 -1
  197. package/dist/ssr/format-property.js +0 -49
  198. package/dist/ssr/format-property.js.map +0 -1
  199. package/dist/ssr/format-rules.js.map +0 -1
  200. package/dist/ssr/hydrate.d.ts +0 -29
  201. package/dist/ssr/hydrate.js.map +0 -1
  202. package/dist/ssr/ssr-collector-ref.js +0 -29
  203. package/dist/ssr/ssr-collector-ref.js.map +0 -1
  204. package/dist/states/index.d.ts +0 -49
  205. package/dist/states/index.js +0 -170
  206. package/dist/states/index.js.map +0 -1
  207. package/dist/static/tastyStatic.d.ts +0 -46
  208. package/dist/static/tastyStatic.js +0 -30
  209. package/dist/static/tastyStatic.js.map +0 -1
  210. package/dist/static/types.d.ts +0 -49
  211. package/dist/static/types.js +0 -24
  212. package/dist/static/types.js.map +0 -1
  213. package/dist/styles/border.d.ts +0 -25
  214. package/dist/styles/border.js +0 -120
  215. package/dist/styles/border.js.map +0 -1
  216. package/dist/styles/color.d.ts +0 -14
  217. package/dist/styles/color.js +0 -26
  218. package/dist/styles/color.js.map +0 -1
  219. package/dist/styles/const.js +0 -17
  220. package/dist/styles/const.js.map +0 -1
  221. package/dist/styles/createStyle.js +0 -79
  222. package/dist/styles/createStyle.js.map +0 -1
  223. package/dist/styles/dimension.js +0 -109
  224. package/dist/styles/dimension.js.map +0 -1
  225. package/dist/styles/directional.js +0 -133
  226. package/dist/styles/directional.js.map +0 -1
  227. package/dist/styles/display.d.ts +0 -30
  228. package/dist/styles/display.js +0 -73
  229. package/dist/styles/display.js.map +0 -1
  230. package/dist/styles/fade.d.ts +0 -15
  231. package/dist/styles/fade.js +0 -62
  232. package/dist/styles/fade.js.map +0 -1
  233. package/dist/styles/fill.d.ts +0 -42
  234. package/dist/styles/fill.js +0 -51
  235. package/dist/styles/fill.js.map +0 -1
  236. package/dist/styles/flow.d.ts +0 -16
  237. package/dist/styles/flow.js +0 -12
  238. package/dist/styles/flow.js.map +0 -1
  239. package/dist/styles/gap.d.ts +0 -31
  240. package/dist/styles/gap.js +0 -38
  241. package/dist/styles/gap.js.map +0 -1
  242. package/dist/styles/height.d.ts +0 -17
  243. package/dist/styles/height.js +0 -19
  244. package/dist/styles/height.js.map +0 -1
  245. package/dist/styles/index.d.ts +0 -1
  246. package/dist/styles/index.js +0 -8
  247. package/dist/styles/index.js.map +0 -1
  248. package/dist/styles/inset.d.ts +0 -24
  249. package/dist/styles/inset.js +0 -34
  250. package/dist/styles/inset.js.map +0 -1
  251. package/dist/styles/list.d.ts +0 -16
  252. package/dist/styles/list.js +0 -100
  253. package/dist/styles/list.js.map +0 -1
  254. package/dist/styles/margin.d.ts +0 -24
  255. package/dist/styles/margin.js +0 -32
  256. package/dist/styles/margin.js.map +0 -1
  257. package/dist/styles/outline.d.ts +0 -29
  258. package/dist/styles/outline.js +0 -55
  259. package/dist/styles/outline.js.map +0 -1
  260. package/dist/styles/padding.d.ts +0 -24
  261. package/dist/styles/padding.js +0 -32
  262. package/dist/styles/padding.js.map +0 -1
  263. package/dist/styles/placement.d.ts +0 -37
  264. package/dist/styles/placement.js +0 -74
  265. package/dist/styles/placement.js.map +0 -1
  266. package/dist/styles/predefined.d.ts +0 -71
  267. package/dist/styles/predefined.js +0 -237
  268. package/dist/styles/predefined.js.map +0 -1
  269. package/dist/styles/preset.d.ts +0 -52
  270. package/dist/styles/preset.js +0 -127
  271. package/dist/styles/preset.js.map +0 -1
  272. package/dist/styles/radius.d.ts +0 -12
  273. package/dist/styles/radius.js +0 -83
  274. package/dist/styles/radius.js.map +0 -1
  275. package/dist/styles/scrollMargin.d.ts +0 -24
  276. package/dist/styles/scrollMargin.js +0 -32
  277. package/dist/styles/scrollMargin.js.map +0 -1
  278. package/dist/styles/scrollbar.d.ts +0 -25
  279. package/dist/styles/scrollbar.js +0 -51
  280. package/dist/styles/scrollbar.js.map +0 -1
  281. package/dist/styles/shadow.d.ts +0 -14
  282. package/dist/styles/shadow.js +0 -25
  283. package/dist/styles/shadow.js.map +0 -1
  284. package/dist/styles/shared.js +0 -17
  285. package/dist/styles/shared.js.map +0 -1
  286. package/dist/styles/transition.d.ts +0 -14
  287. package/dist/styles/transition.js +0 -159
  288. package/dist/styles/transition.js.map +0 -1
  289. package/dist/styles/types.d.ts +0 -564
  290. package/dist/styles/width.d.ts +0 -17
  291. package/dist/styles/width.js +0 -19
  292. package/dist/styles/width.js.map +0 -1
  293. package/dist/tasty.d.ts +0 -134
  294. package/dist/tasty.js +0 -248
  295. package/dist/tasty.js.map +0 -1
  296. package/dist/types.d.ts +0 -184
  297. package/dist/utils/cache-wrapper.js +0 -21
  298. package/dist/utils/cache-wrapper.js.map +0 -1
  299. package/dist/utils/case-converter.js +0 -8
  300. package/dist/utils/case-converter.js.map +0 -1
  301. package/dist/utils/color-math.d.ts +0 -46
  302. package/dist/utils/color-math.js +0 -749
  303. package/dist/utils/color-math.js.map +0 -1
  304. package/dist/utils/color-space.d.ts +0 -5
  305. package/dist/utils/color-space.js +0 -228
  306. package/dist/utils/color-space.js.map +0 -1
  307. package/dist/utils/colors.d.ts +0 -5
  308. package/dist/utils/colors.js +0 -10
  309. package/dist/utils/colors.js.map +0 -1
  310. package/dist/utils/css-types.d.ts +0 -7
  311. package/dist/utils/deps-equal.js +0 -15
  312. package/dist/utils/deps-equal.js.map +0 -1
  313. package/dist/utils/dotize.d.ts +0 -26
  314. package/dist/utils/dotize.js +0 -122
  315. package/dist/utils/dotize.js.map +0 -1
  316. package/dist/utils/filter-base-props.d.ts +0 -15
  317. package/dist/utils/filter-base-props.js +0 -45
  318. package/dist/utils/filter-base-props.js.map +0 -1
  319. package/dist/utils/get-display-name.d.ts +0 -7
  320. package/dist/utils/get-display-name.js +0 -10
  321. package/dist/utils/get-display-name.js.map +0 -1
  322. package/dist/utils/has-keys.js +0 -13
  323. package/dist/utils/has-keys.js.map +0 -1
  324. package/dist/utils/hash.js +0 -14
  325. package/dist/utils/hash.js.map +0 -1
  326. package/dist/utils/is-dev-env.js +0 -19
  327. package/dist/utils/is-dev-env.js.map +0 -1
  328. package/dist/utils/is-valid-element-type.js +0 -15
  329. package/dist/utils/is-valid-element-type.js.map +0 -1
  330. package/dist/utils/merge-styles.js.map +0 -1
  331. package/dist/utils/mod-attrs.d.ts +0 -6
  332. package/dist/utils/mod-attrs.js +0 -20
  333. package/dist/utils/mod-attrs.js.map +0 -1
  334. package/dist/utils/process-tokens.d.ts +0 -17
  335. package/dist/utils/process-tokens.js +0 -83
  336. package/dist/utils/process-tokens.js.map +0 -1
  337. package/dist/utils/resolve-recipes.d.ts +0 -17
  338. package/dist/utils/resolve-recipes.js.map +0 -1
  339. package/dist/utils/selector-transform.js +0 -32
  340. package/dist/utils/selector-transform.js.map +0 -1
  341. package/dist/utils/string.js +0 -8
  342. package/dist/utils/string.js.map +0 -1
  343. package/dist/utils/styles.d.ts +0 -99
  344. package/dist/utils/styles.js +0 -220
  345. package/dist/utils/styles.js.map +0 -1
  346. package/dist/utils/typography.d.ts +0 -58
  347. package/dist/utils/typography.js +0 -51
  348. package/dist/utils/typography.js.map +0 -1
  349. package/dist/utils/warnings.d.ts +0 -16
  350. package/dist/utils/warnings.js +0 -16
  351. package/dist/utils/warnings.js.map +0 -1
  352. package/dist/zero/css-writer.d.ts +0 -45
  353. package/dist/zero/css-writer.js +0 -73
  354. package/dist/zero/css-writer.js.map +0 -1
  355. package/dist/zero/extractor.d.ts +0 -24
  356. package/dist/zero/extractor.js.map +0 -1
  357. package/docs/PIPELINE.md +0 -519
@@ -1,6 +1,8 @@
1
1
  # Getting Started
2
2
 
3
- This guide walks you from zero to a working Tasty component, then through the optional shared configuration and tooling layers. It is the right starting point when you already want to try Tasty in code. If you are still deciding whether Tasty fits your team, start with [Comparison](comparison.md) and [Adoption Guide](adoption.md) first. For a feature overview, see the [README](../README.md). For the full style language reference, see the [Style DSL](dsl.md). For the React API, see the [React API](react-api.md). For the rest of the docs by role or task, see the [Docs Hub](README.md).
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 can be used immediately in a React app, but it is most compelling for teams building reusable components with intersecting states, variants, tokens, and design-system conventions.
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
- ## Your first component
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 Card = tasty({
31
- as: 'div',
32
+ const Button = tasty({
33
+ as: 'button',
32
34
  styles: {
33
- display: 'flex',
34
- flow: 'column',
35
- padding: '4x',
36
- gap: '2x',
37
- fill: '#white',
38
- border: true,
39
- radius: true,
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 <Card>Hello, Tasty!</Card>;
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. Values like `4x`, `#white`, `true`, and `1r` are Tasty DSL — they map to CSS custom properties, shorthand expansions, and design tokens. See [Style Properties](styles.md) for the full reference.
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
- ## Optional: add shared configuration
75
+ ## Add your design system’s language
53
76
 
54
- Use `configure()` once, before your app renders, when your app or design system needs shared state aliases, tokens, recipes, or parser extensions:
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
- '$gap': '8px',
95
- '$radius': '4px',
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 | Rules | Examples |
152
- |----------|-------|---------|
153
- | Property validation | `known-property`, `valid-boolean-property`, `valid-sub-element` | Flags typos like `pading` or invalid boolean usage |
154
- | Value validation | `valid-value`, `valid-color-token`, `valid-custom-unit` | Catches `#nonexistent` tokens, bad unit syntax |
155
- | State validation | `valid-state-key`, `no-nested-state-map`, `require-default-state` | Validates state key syntax, ensures `''` default exists |
156
- | Structure | `valid-styles-structure`, `no-important`, `no-nested-selector` | Prevents `!important`, invalid nesting |
157
- | Static mode | `static-no-dynamic-values`, `static-valid-selector` | Enforces build-time constraints in `tastyStatic()` |
158
- | Style properties | `valid-preset`, `valid-recipe`, `valid-transition`, `valid-directional-modifier`, `valid-radius-shape` | Validates preset names, recipe references, transition syntax |
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
- `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'`. In server-only contexts, they produce zero client JavaScript with the full feature set.
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 | Entry point | Best for | Trade-off |
185
- |------|-------------|----------|-----------|
186
- | **Runtime (default)** | `tasty()` from `@tenphi/tasty` | All React apps — server-rendered by default, zero client JS until you need interactivity | Full feature set (styleProps, sub-elements, variants); CSS computed during rendering |
187
- | **Runtime + SSR integration** | Add `@tenphi/tasty/ssr/*` | Apps with client-side hydration (Next.js client components, Astro islands) | Adds CSS batching, deduplication, FOUC prevention, and client cache hydration |
188
- | **Zero-runtime** | `tastyStatic()` from `@tenphi/tasty/static` | Non-React frameworks or build-time extraction without React | Requires Babel plugin; no `styleProps` or runtime-only APIs |
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 |
189
210
 
190
211
  Both `tasty()` and `tastyStatic()` share the same DSL, tokens, units, and state mappings.
191
212
 
192
- - **Runtime** is the default and requires no extra setup beyond `@tenphi/tasty`. In server-only contexts (Next.js RSC, Astro without `client:*` directives, SSG), `tasty()` produces static HTML + CSS with zero client JavaScript — the same end result as `tastyStatic()` but with the full feature set. For example, `tasty()` + `tastyIntegration()` in Astro without islands gives you the complete API with zero JS shipped.
193
- - **SSR integration** is only needed when your app also has client-side rendering. Add `@tenphi/tasty/ssr/next`, `@tenphi/tasty/ssr/astro`, or the core SSR API to get CSS deduplication and cache hydration. See [Server-Side Rendering](ssr.md).
194
- - **Zero-runtime** requires the Babel plugin and additional peer dependencies. Use it when you need build-time extraction without a React runtime. See [Zero Runtime (tastyStatic)](tasty-static.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
 
@@ -214,4 +235,4 @@ Both `tasty()` and `tastyStatic()` share the same DSL, tokens, units, and state
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 build-time extraction or server-rendered CSS delivery: use [Zero Runtime (tastyStatic)](tasty-static.md) for extraction, or add [Server-Side Rendering](ssr.md) on top of runtime `tasty()` when your framework renders on the server.
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
- - **Reference counting** - Automatic cleanup when components unmount (refCount = 0)
12
+ - **DOM-driven lifetime** - Styles are collected once nothing renders them
13
13
  - **CSS nesting flattening** - Handles `&`, `.Class`, `SubElement` patterns
14
- - **Keyframes injection** - First-class `@keyframes` support with immediate disposal
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
- // Cleanup when component unmounts (refCount decremented)
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.
@@ -224,14 +230,62 @@ configure({
224
230
 
225
231
  **Auto-Detection Features:**
226
232
  - `devMode`: Automatically enabled in development environments (detected via `isDevEnv()`)
227
- - `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.
228
249
 
229
250
  **Configuration Notes:**
230
251
  - Most options have sensible defaults and auto-detection
231
252
  - `configure()` is optional - the injector works with defaults
232
253
  - **Configuration is locked after styles are generated** - calling `configure()` after first render will emit a warning and be ignored
233
- - `gc.touchInterval`: Number of touch events between GC cycles. Each style render counts as a touch. When the counter reaches this value, GC is scheduled via `requestIdleCallback`.
234
- - `gc.capacity`: Maximum number of unused styles (refCount = 0, not in DOM) to retain. When exceeded, the oldest are evicted first. Actively referenced styles don't count against this limit.
254
+ - `gc.unsafeAutoCollect`: Sweep automatically as rendering goes, rather than only when you call `gc()` or `cleanup()` (default `false`). See the caveat below.
255
+ - `gc.touchInterval`: Number of touch events between automatic GC cycles, used only with `unsafeAutoCollect`. Each style render counts as a touch. When the counter reaches this value, GC is scheduled via `requestIdleCallback`.
256
+ - `gc.timeoutFallback`: With `unsafeAutoCollect`, schedule the sweep with a timeout where `requestIdleCallback` is unavailable (default `false`). Collection is a background chore, so it otherwise runs only in idle time; opting in trades that for automatic collection on engines that cannot offer any.
257
+
258
+ > **Why `unsafeAutoCollect` is named that.** A sweep decides what is finished by scanning the DOM, and rendering is not commit-aware: a concurrent render can yield between the `inject()` that creates a class and the commit that attaches it. A sweep landing in that window sees no element carrying the class and deletes rules the pending render is about to use, so the element commits unstyled until something re-renders it. A pending render is indistinguishable from one that already committed and unmounted — React reports neither commit nor discard for a class name handed out during render — so no heuristic closes this; only a real commit signal would.
259
+ >
260
+ > Prefer `gc()` or `cleanup()` at a moment of your choosing: a route change, an idle callback of your own, test teardown.
261
+ - `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.
262
+
263
+ ---
264
+
265
+ ### At-rule injection
266
+
267
+ 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.
268
+
269
+ | Function | Injects | Notes |
270
+ |---|---|---|
271
+ | `keyframes(steps, nameOrOptions?)` | `@keyframes` | The exception: ref-counted and disposed immediately when unused. Returns the injected name, which may differ from the requested one. |
272
+ | `property(name, options?)` | `@property` | `name` accepts `$name`, `#name`, or `--name`. Usually unnecessary — types are inferred automatically unless `configure({ autoPropertyTypes: false })`. |
273
+ | `fontFace(family, descriptors, options?)` | `@font-face` | Deduplicated by family **and** descriptor content, so several faces of one family coexist. |
274
+ | `counterStyle(name, descriptors, options?)` | `@counter-style` | |
275
+ | `func(name, definition, options?)` | `@function` | `name` accepts `$$name`, `$name`, or `--name`. Abbreviated because `function` is a reserved word. |
276
+
277
+ Every one of these takes an optional `root` (a `Document` or `ShadowRoot`).
278
+
279
+ `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.
280
+
281
+ ```ts
282
+ import { counterStyle, fontFace, func, property } from '@tenphi/tasty';
283
+
284
+ property('$card-elevation', { syntax: '<number>', initialValue: '0' });
285
+ fontFace('Inter', { src: 'url(/inter.woff2)', fontWeight: '400 700' });
286
+ counterStyle('dashes', { system: 'cyclic', symbols: '"—"', suffix: ' ' });
287
+ func('$$negative', { args: ['$value'], result: '(-1 * $value)' });
288
+ ```
235
289
 
236
290
  ---
237
291
 
@@ -258,6 +312,17 @@ const styleRule: StyleResult = {
258
312
  };
259
313
  ```
260
314
 
315
+ ### Batched Injection
316
+
317
+ `configure({ batchInjection: true })` queues every sheet write — component
318
+ rules, global rules, raw CSS and at-rules — into one FIFO and applies them
319
+ together, so the document is style-invalidated once per flush instead of once per
320
+ component. `<TastyBatchProvider>` flushes in `useInsertionEffect`, before any
321
+ layout effect, so a queued write can never be observed by a measurement. See
322
+ [Batched injection](configuration.md#batched-injection) for the modes and the
323
+ ordering guarantee. `flushStyles()` applies pending writes on demand; every read
324
+ API here calls it for you.
325
+
261
326
  ### Deduplication & Performance
262
327
 
263
328
  ```typescript
@@ -276,23 +341,28 @@ const button2 = inject([{
276
341
  console.log(button1.className === button2.className); // true
277
342
  ```
278
343
 
279
- ### Reference Counting
344
+ ### Pinning
280
345
 
281
346
  ```typescript
282
- // Multiple components using the same styles
347
+ // Multiple callers using the same styles
283
348
  const comp1 = inject([commonStyle]);
284
349
  const comp2 = inject([commonStyle]);
285
350
  const comp3 = inject([commonStyle]);
286
351
 
287
- // Style is kept alive while any component uses it
288
- comp1.dispose(); // refCount: 3 → 2
289
- comp2.dispose(); // refCount: 2 → 1
290
- comp3.dispose(); // refCount: 1 → 0, eligible for bulk cleanup
352
+ // Style is pinned while any caller holds a handle
353
+ comp1.dispose(); // pins: 3 → 2
354
+ comp2.dispose(); // pins: 2 → 1
355
+ comp3.dispose(); // pins: 1 → 0, now up to the DOM and gc()
291
356
 
292
- // Rule exists but refCount = 0 means unused
293
- // Next inject() with same styles will increment refCount and reuse immediately
357
+ // Unpinned does not mean deleted: the rule stays cached and is reused instantly
358
+ // by the next inject(). gc() decides when it actually goes.
294
359
  ```
295
360
 
361
+ Pinning covers callers that hold a handle. Styles that come from rendering are
362
+ never pinned — see `{ pin: false }` under
363
+ [`inject()`](#injectrules-options-injectresult) — so what keeps them alive is
364
+ being in the DOM, and nothing else.
365
+
296
366
  ### Garbage Collection
297
367
 
298
368
  ```typescript
@@ -300,6 +370,9 @@ import { configure, gc } from '@tenphi/tasty';
300
370
 
301
371
  // Keyframes: Disposed immediately when refCount = 0 (safer for global scope)
302
372
  // CSS rules: Tracked by touch count and cleaned up via gc()
373
+ //
374
+ // A CSS rule is collectible when no element carries its class AND nobody
375
+ // pinned it with inject().
303
376
 
304
377
  configure({
305
378
  gc: {
@@ -314,15 +387,20 @@ gc();
314
387
  // Force-remove ALL unused styles (e.g. on route change or test teardown):
315
388
  gc({ force: true });
316
389
 
317
- // GC is also triggered automatically by touch count during rendering.
318
- // Every `touchInterval` touches, GC is scheduled via requestIdleCallback.
390
+ // cleanup() is the same thing:
391
+ cleanup();
392
+
393
+ // Collection is on demand by default: gc() and cleanup() are the whole story,
394
+ // and nothing sweeps behind your back. `gc.unsafeAutoCollect` opts into a sweep
395
+ // every `touchInterval` touches, scheduled via requestIdleCallback — read the
396
+ // caveat below before turning it on.
319
397
 
320
398
  // Benefits:
321
399
  // - Activity-proportional: busy apps trigger GC more often
322
400
  // - DOM-safe: styles currently in the DOM are never evicted
323
401
  // - Oldest-first: least recently used styles are evicted first
324
402
  // - Keyframes: Immediate cleanup prevents global namespace pollution
325
- // - Unused styles can be instantly reactivated (just increment refCount)
403
+ // - Unused styles stay cached until evicted, so re-rendering them is a cache hit
326
404
  ```
327
405
 
328
406
  ### Shadow DOM Support
@@ -350,14 +428,14 @@ const shadowAnimation = keyframes({
350
428
  ### Server-Side Rendering
351
429
 
352
430
  ```typescript
353
- import { getCssText, getCssTextForNode } from '@tenphi/tasty';
431
+ import { getCSSText, getCSSTextForNode } from '@tenphi/tasty';
354
432
 
355
433
  // Extract all CSS for SSR
356
- const cssText = getCssText();
434
+ const cssText = getCSSText();
357
435
 
358
436
  // Extract CSS for specific DOM subtree (like jest-styled-components)
359
437
  const container = render(<MyComponent />);
360
- const componentCSS = getCssTextForNode(container);
438
+ const componentCSS = getCSSTextForNode(container);
361
439
  ```
362
440
 
363
441
  ### Test Environment Detection
@@ -366,7 +444,7 @@ const componentCSS = getCssTextForNode(container);
366
444
  // Automatically detected test environments:
367
445
  // - NODE_ENV === 'test'
368
446
  // - Jest globals (jest, describe, it, expect)
369
- // - jsdom user agent
447
+ // - jsdom / HappyDOM user agent
370
448
  // - Vitest globals (vitest)
371
449
  // - Mocha globals (mocha)
372
450
 
@@ -417,11 +495,11 @@ const metrics = injector.instance.getMetrics();
417
495
  console.log({
418
496
  cacheHits: metrics.hits, // Successful cache hits
419
497
  cacheMisses: metrics.misses, // New styles injected
420
- unusedHits: metrics.unusedHits, // Current unused styles (calculated on demand)
498
+ unusedHits: metrics.unusedHits, // Styles currently eligible for eviction (scans the DOM)
421
499
  bulkCleanups: metrics.bulkCleanups, // Number of bulk cleanup operations
422
500
  stylesCleanedUp: metrics.stylesCleanedUp, // Total styles removed in bulk cleanups
423
501
  totalInsertions: metrics.totalInsertions, // Lifetime insertions
424
- totalUnused: metrics.totalUnused, // Total styles marked as unused (refCount = 0)
502
+ totalUnused: metrics.totalUnused, // Times a pinned style lost its last pin
425
503
  startTime: metrics.startTime, // Metrics collection start timestamp
426
504
  cleanupHistory: metrics.cleanupHistory, // Detailed cleanup operation history
427
505
  });
@@ -465,10 +543,10 @@ metrics.cleanupHistory.forEach(cleanup => {
465
543
 
466
544
  ```typescript
467
545
  // ✅ Reuse styles - identical CSS gets deduplicated
468
- const buttonBase = { padding: '8px 16px', borderRadius: '4px' };
546
+ const buttonBase = 'padding: 8px 16px; border-radius: 4px;';
469
547
 
470
548
  // ✅ Avoid frequent disposal and re-injection
471
- // Let the reference counting system handle cleanup
549
+ // Let the injector handle cleanup
472
550
 
473
551
  // ✅ Use bulk operations for global styles
474
552
  injectGlobal([
@@ -494,13 +572,13 @@ configure({
494
572
  // The injector automatically manages memory through:
495
573
 
496
574
  // 1. Hash-based deduplication - same CSS = same className
497
- // 2. Reference counting - styles stay alive while in use (refCount > 0)
498
- // 3. Immediate keyframes cleanup - disposed instantly when refCount = 0
499
- // 4. Touch-count GC - unused CSS rules are evicted oldest-first when over capacity
500
- // 5. DOM safety guard - styles visible in the DOM are never evicted
575
+ // 2. DOM-driven lifetime - a rendered class is never evicted
576
+ // 3. Pinning - inject() callers hold their classes until they dispose
577
+ // 4. Immediate keyframes cleanup - disposed instantly when refCount = 0
578
+ // 5. Touch-count GC - unused CSS rules are evicted oldest-first when over capacity
501
579
 
502
580
  // Manual cleanup is rarely needed but available:
503
- cleanup(); // Force immediate cleanup of all unused CSS rules (refCount = 0)
581
+ cleanup(); // Remove every rule that is neither rendered nor pinned
504
582
  destroy(); // Nuclear option: remove all stylesheets and reset
505
583
  ```
506
584
 
@@ -522,9 +600,9 @@ const StyledButton = tasty({
522
600
 
523
601
  // Internally uses the injector:
524
602
  // 1. Styles are parsed into StyleResult objects
525
- // 2. inject() is called with the parsed results
603
+ // 2. inject() is called with the parsed results, unpinned
526
604
  // 3. Component gets the returned className
527
- // 4. dispose() is called when component unmounts
605
+ // 4. gc() reclaims the class once no element carries it
528
606
  ```
529
607
 
530
608
  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.