@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
@@ -0,0 +1,224 @@
1
+ # Tasty Style Rules for AI Agents
2
+
3
+ A compact ruleset for **writing correct `@tenphi/tasty` styles**. It is not an API tour — most rules here are machine-checked by [`@tenphi/eslint-plugin-tasty`](https://www.npmjs.com/package/@tenphi/eslint-plugin-tasty), so following it generally means clean lint output. The plugin ships separately and can lag a new DSL rule, so treat this page as the source of truth and the plugin as the enforcement that catches most of it. For explanations and the complete API, see [Style DSL](dsl.md), [Style Properties](styles.md), [React API](react-api.md).
4
+
5
+ Notation: ❌ wrong → ✅ correct.
6
+
7
+ ---
8
+
9
+ ## 0. Read the project config first
10
+
11
+ Color tokens (`#name`), custom properties (`$name`), `preset` names, `recipe` names, state aliases (`@mobile`) and extra units are **project-defined** in `tasty.config.ts` or a `configure({ … })` call. Look them up before writing styles. Never invent a name — reuse an existing one, or add it to the config.
12
+
13
+ ## 1. Where styles go
14
+
15
+ ```jsx
16
+ const Card = tasty({ as: 'div', styles: { … }, styleProps: ['padding'] }); // new component
17
+ const Hero = tasty(Card, { styles: { … } }); // extend a component
18
+ const cls = useStyles({ … }); // ad-hoc class name
19
+ tastyStatic('.card', { … }); // build-time extraction
20
+ ```
21
+
22
+ - Wrap, don't pass styles per instance: ❌ `<Box styles={{ padding: '2x' }} />` → ✅ `const Box = tasty({ styles: { padding: '2x' } })`.
23
+ - Style values are **static**. Route anything conditional through a state map plus `mods` (or `tokens` / `styleProps`):
24
+ ❌ `fill: isOpen ? '#primary' : '#surface'` → ✅ `fill: { '': '#surface', open: '#primary' }` with `mods={{ open: isOpen }}`.
25
+
26
+ ## 2. Property names
27
+
28
+ Keys are camelCase — a Tasty property or a real CSS property. No kebab-case, no invented names.
29
+
30
+ Prefer the Tasty shorthand over its CSS equivalents:
31
+
32
+ | Use | Instead of |
33
+ | ---------------------------------------- | ------------------------------------------------------------------------------------- |
34
+ | `fill` | `backgroundColor`, `background` |
35
+ | `image` | `backgroundImage` |
36
+ | `border` | `borderColor`, `borderWidth`, `borderStyle`, `borderTop`/`Right`/`Bottom`/`Left` |
37
+ | `radius` | `borderRadius` |
38
+ | `outline` | `outlineColor`, `outlineWidth`, `outlineStyle`, `outlineOffset` |
39
+ | `shadow` | `boxShadow` |
40
+ | `padding` | `paddingTop`/`Right`/`Bottom`/`Left` |
41
+ | `margin` | `marginTop`/`Right`/`Bottom`/`Left` |
42
+ | `inset` | `top`, `right`, `bottom`, `left` |
43
+ | `width` / `height` | `minWidth`/`maxWidth`, `minHeight`/`maxHeight` |
44
+ | `flow` | `flexDirection`, `flexWrap`, `flexFlow`, `gridAutoFlow` |
45
+ | `preset` | `fontSize`, `lineHeight`, `letterSpacing`, `fontWeight`, `fontStyle`, `textTransform` |
46
+ | `font` | `fontFamily` |
47
+ | `scrollbar` | `scrollbarWidth`, `scrollbarColor`, `scrollbarGutter` |
48
+ | `gridColumns` / `gridRows` / `gridAreas` | `gridTemplateColumns` / `Rows` / `Areas` |
49
+ | `textOverflow: 'ellipsis / 3'` | `lineClamp` |
50
+ | `hide: true` | `display: 'none'` |
51
+ | `flexGrow` / `flexShrink` / `flexBasis` | `flex` |
52
+
53
+ The last row goes the other way — longhands over the shorthand — because `flex` is lossy: it resets the components you omit to non-initial values (`flex: '0'` silently sets `flex-basis: 0%`) and cannot express `flexShrink: 0` at all. The longhands also carry separate state maps.
54
+
55
+ ## 3. Values
56
+
57
+ ### Tokens
58
+
59
+ | Syntax | Meaning |
60
+ | --------------------- | ------------------------------------------------------------------------- |
61
+ | `#name` | color token → `var(--name-color)` |
62
+ | `#name.50` | token at 50% opacity (`0`–`100`) |
63
+ | `#name.$opacity` | opacity from a custom property |
64
+ | `#clear` / `#current` | `transparent` / `currentcolor` |
65
+ | `$name` | custom property → `var(--name)` |
66
+ | `(#a, #b)` | fallback chain |
67
+ | `$$name` / `##name` | the custom property _name_ (not its value) — inside `transition` |
68
+ | `$$name(…)` | call a CSS `@function` declared via `@function`, `useFunction`, or config |
69
+
70
+ - ❌ `#f5f5f5`, `rgb(0 0 0)`, `oklch(…)`, `okhsl(…)`, `red` → ✅ `#surface` (add the token to the config if it doesn't exist).
71
+ - ❌ `var(--gap)` → ✅ `$gap` · ❌ `$accent-color` → ✅ `#accent` · ❌ `transparent` → ✅ `#clear` · ❌ `currentColor` → ✅ `#current`.
72
+ - A `$name` must be declared as a `'$name': value` key in the same styles object, or in the config.
73
+ - Names are case-sensitive and start lowercase — `$myVar` → `--myVar`; a leading capital folds (`$Foo` → `--foo`). Prefer kebab-case.
74
+ - A `$name` reference is untyped: it fills the first free slot in a shorthand (`border: '1bw $my-style'` → the line style). Write colors as `#name`; `$name-color` is a fallback for pointing at a raw CSS custom property that holds a color.
75
+ - `preset` and `transition` take token _names_, not values, so a `$name` cannot stand in for one (it warns in dev and is ignored).
76
+
77
+ ### Units
78
+
79
+ Tasty units: `x` (gap multiple), `r` (radius), `cr` (card radius), `bw` (border width), `ow` (outline width), `sf` (`minmax(0, Nfr)`). All CSS units work too, plus anything in `units` in the config. Any other unit is an error.
80
+
81
+ Prefer units over raw pixels: `8px` → `1x` … `64px` → `8x`; `radius: '6px'` → `'1r'`; `border: '1px …'` → `'1bw …'`.
82
+
83
+ ### Math
84
+
85
+ ❌ `calc(100% - 2x)` → ✅ `(100% - 2x)`. Parentheses are wrapped in `calc()` automatically — and must be balanced.
86
+
87
+ ### `true`
88
+
89
+ `true` means "the design-system default" and is accepted **only** by: `border`, `radius`, `outline`, `shadow`, `padding`, `margin`, `gap`, `inset`, `width`, `height`, `fill`, `color`, `fade`, `preset`, `font`, `scrollbar`, `hide`. Anywhere else it is an error.
90
+
91
+ ### `!important`
92
+
93
+ Never use it. Tasty owns specificity through doubled selectors and state ordering; `!important` breaks that. Express the exception as a state instead.
94
+
95
+ ### Modifiers
96
+
97
+ A value is `[values…] [modifiers…]`, and several groups can be comma-separated (later groups override earlier ones). A group that names **direction** modifiers takes a **single** value, applied to every direction it names — per-side values come from comma groups. A group naming no direction keeps plain CSS shorthand order (1–4 values). (`inset` + `dock` is the one exception: a second value insets the spanned sides.) Only the modifiers a property knows are valid:
98
+
99
+ | Property | Modifiers |
100
+ | ------------------------------------ | ---------------------------------------------------------------------------------------------------------- |
101
+ | `padding`, `margin`, `inset`, `fade` | `top` `right` `bottom` `left` |
102
+ | `border` | the four directions + `solid` `dashed` `dotted` `double` `groove` `ridge` `inset` `outset` `none` `hidden` |
103
+ | `outline` | the style keywords above |
104
+ | `radius` | `top` `right` `bottom` `left` + shapes `round` `ellipse` `leaf` `backleaf` |
105
+ | `width`, `height` | `min` `max` `fixed` |
106
+ | `flow` | `row` `column` `row-reverse` `column-reverse` `wrap` `nowrap` `dense` |
107
+ | `overflow` | `visible` `hidden` `scroll` `clip` `auto` `overlay` |
108
+ | `position` | `static` `relative` `absolute` `fixed` `sticky` |
109
+ | `shadow` | `inset` |
110
+ | `preset` | `name / strong` (or `bold`) `italic` `icon` `tight` |
111
+ | box properties | `longhand` — emit CSS longhands instead of the shorthand |
112
+
113
+ Directional modifiers beat placeholder zeros:
114
+
115
+ - ❌ `padding: '0 0 2x 0'` → ✅ `padding: '2x bottom'`
116
+ - ❌ `padding: '1x 1x 2x 1x'` → ✅ `padding: '1x, 2x bottom'`
117
+ - ❌ `border: '0 0 1bw 0'` — four tokens parse as _one_ border value, so this renders no border at all → ✅ `border: '1bw bottom'`
118
+ - ❌ `padding: '2x 4x top right'` — a directional group takes one value, so `4x` is dropped → ✅ `padding: '2x top, 4x right'`
119
+ - ❌ `fade: '3x 1x top bottom'` → ✅ `fade: '3x top, 1x bottom'`
120
+
121
+ Value-only properties reject both colors and modifiers: `gap`, `columnGap`, `rowGap`, `opacity`, `zIndex`, `order`, `flexGrow`, `flexShrink`, `flexBasis`, `aspectRatio`, `lineClamp`, `tabSize`, `paddingInline`, `paddingBlock`. `fill` and `color` take a color (plus `none` / `transparent`); `caretColor` and `accentColor` take a color only.
122
+
123
+ ### `transition`
124
+
125
+ Use semantic names, not CSS property names: `fade` `fill` `color` `theme` `border` `radius` `shadow` `outline` `preset` `text` `gap` `opacity` `translate` `rotate` `scale` `filter` `image` `background` `width` `height` `zIndex` `inset` `flow` `dimension`.
126
+
127
+ ❌ `transition: 'background-color 0.2s'` → ✅ `transition: 'fill 0.2s'`
128
+
129
+ ## 4. State maps
130
+
131
+ A property value can be an object of `state: value`. Key order is priority — later keys win.
132
+
133
+ | Key | Generated selector |
134
+ | ------------------------ | ----------------------------------------------- |
135
+ | `hovered` | `[data-hovered]` (boolean modifier from `mods`) |
136
+ | `theme=danger` | `[data-theme="danger"]` (value modifier) |
137
+ | `:hover` | pseudo-class |
138
+ | `.active` | class selector |
139
+ | `[aria-expanded="true"]` | attribute selector |
140
+ | `hovered & .active` | AND |
141
+ | `hovered \| focused` | OR (`,` also means OR) |
142
+ | `!disabled` | NOT |
143
+ | `hovered ^ focused` | XOR — exactly one (keep chains ≤ 4 operands) |
144
+
145
+ Precedence `!` > `^` > `|` > `&`; use parentheses to override.
146
+
147
+ Rules:
148
+
149
+ 1. **`''` comes first.** The bare default is the lowest-priority state; placing it later would override everything above it.
150
+ 2. **Every state map needs `''` or `_`** — except when extending (`tasty(Parent, …)`), where omitting `''` merges into the parent's states and including `''` replaces them wholesale.
151
+ 3. **`_` is standalone-only** and always first (with `''` right after it, if present). `_` is a never-negated fallback floor for cases where a higher-priority branch may be _unknown_ (`@supports`, container queries). If a map contains only `_` and `''`, drop the `''`.
152
+ 4. **No nested maps:** ❌ `{ hovered: { pressed: 'x' } }` → ✅ `{ 'hovered & pressed': 'x' }`
153
+ 5. **State keys never sit at the top level** of a styles object — `:hover`, `.active`, `[open]` belong inside a property value.
154
+ 6. **Prefer automatic native states:** use `disabled` and `checked` in Tasty component styles instead of `[disabled]` and `[checked]`. The concise states track the corresponding native prop or attribute automatically.
155
+
156
+ ```jsx
157
+ color: { '': '#text', hovered: '#accent', disabled: '#text.40' }
158
+ ```
159
+
160
+ Advanced states:
161
+
162
+ | Prefix | Use | Example |
163
+ | -------------- | -------------------------------------------------------------------------------- | --------------------------------------------------- |
164
+ | `@media(…)` | media query; dimensions `w` `h`; types `@media:print` `:screen` `:all` `:speech` | `@media(w < 768px)`, `@media(600px <= w < 1200px)` |
165
+ | `@(…)` | container query; dimensions `w` `h` `is` `bs` | `@(layout, w >= 600px)`, `@($variant=primary)` |
166
+ | `@supports(…)` | feature query; `$` first argument tests a selector | `@supports(display: grid)`, `@supports($, :has(*))` |
167
+ | `@root(…)` | condition on `:root` | `@root(schema=dark)` |
168
+ | `@parent(…)` | condition on an ancestor; `, >` for the direct parent | `@parent(hovered, >)` |
169
+ | `@own(…)` | a sub-element's own state — **only inside sub-element styles** | `@own(:hover)` |
170
+ | `@starting` | `@starting-style` entry animation | `@starting` |
171
+ | `@name` | project state alias | `@mobile` |
172
+
173
+ - At root level write the selector directly: ❌ `'@own(:hover)'` → ✅ `':hover'`.
174
+ - `@name` aliases must exist in `states` in the config or be declared locally as an `'@name': '<state expression>'` key; alias keys start with `@` and their value must be a valid state expression.
175
+ - `:is()` / `:has()` / `:not()` / `:where()` work in state keys but support at most 2 levels of nested parentheses, and `:has()` is expensive — prefer `@parent()`, `@own()` and modifiers.
176
+
177
+ When extending a parent's state map: `'@inherit'` reuses the parent's value for that state, `null` removes a state (or resets a property, letting recipes fill in), `false` is a tombstone that blocks it entirely.
178
+
179
+ ## 5. Sub-elements
180
+
181
+ A **capitalized** key targets `[data-element="Name"]`, and its value must be a style object.
182
+
183
+ ```jsx
184
+ styles: { Title: { preset: 'h3' }, Icon: { $: '>@:last-child', color: '#accent' } }
185
+ ```
186
+
187
+ ❌ nested-selector keys (`'& .title'`, `'&:hover'`) → ✅ sub-elements and state maps. Use the `$` affix property inside a sub-element to control how its selector attaches (`>` direct child, `@` placeholder for the element itself, `&::before` for a root pseudo-element).
188
+
189
+ ## 6. Special top-level keys
190
+
191
+ | Key | Shape |
192
+ | ---------------- | ----------------------------------------------------------------------------------------------------------- |
193
+ | `@keyframes` | `{ name: { '0%': styles, … } }` |
194
+ | `@property` | `{ '$name': { syntax, inherits, initialValue } }` |
195
+ | `@font-face` | `{ 'Family Name': descriptors \| descriptors[] }` |
196
+ | `@counter-style` | `{ name: descriptors }` |
197
+ | `@function` | `{ '$$name': { args, returns?, result, '$local'? } }` |
198
+ | `recipe` | a **string** of configured recipe names: `'card elevated'`, `'reset input / autofill'`, `'none / disabled'` |
199
+
200
+ At-rule keys match the real CSS at-rule names, so they are kebab-case, not camelCase. Inside `@function`, the callable is `$$name` but its parameters and local variables are `$name`:
201
+
202
+ ```jsx
203
+ styles: {
204
+ '@function': { $$negative: { args: ['$value'], result: '(-1 * $value)' } },
205
+ margin: '$$negative(2x) top',
206
+ }
207
+ ```
208
+
209
+ ## 7. `tastyStatic()`
210
+
211
+ The selector must be a string literal and valid CSS. Values must be static — strings, numbers, booleans, `null`, or objects/arrays of those. No variables, template literals, function calls, or spreads.
212
+
213
+ ## 8. Checklist
214
+
215
+ - Token, preset, recipe, unit and `@alias` names exist in the project config.
216
+ - Tasty shorthand chosen over CSS longhands; `flexGrow`/`flexShrink`/`flexBasis` over `flex`; `hide: true` over `display: 'none'`.
217
+ - Colors are `#tokens`, not hex/rgb/oklch/named; `$prop` not `var(--prop)`.
218
+ - Spacing uses `x`/`r`/`bw`/`ow` units; math uses `(…)`, not `calc(…)`.
219
+ - `true` only on the properties that accept it; no `!important`.
220
+ - Modifiers valid for the property; directional shorthand instead of placeholder zeros.
221
+ - Every state map starts with `''` (or `_`), is flat, and lives inside a property value.
222
+ - `@own()` only inside sub-elements; sub-element keys are capitalized and hold objects.
223
+ - At-rule keys are kebab-case (`@property`, `@font-face`, `@counter-style`, `@function`); `@function` names use `$$`, their args and locals use `$`.
224
+ - Values are static; dynamic behavior comes from `mods` / `tokens` / `styleProps`.
@@ -2,9 +2,9 @@
2
2
 
3
3
  Use this guide when you are deciding whether Tasty is the right tool. If you have already decided to adopt it and need rollout guidance, use the [Adoption Guide](adoption.md) instead.
4
4
 
5
- Tasty is best understood not as a general-purpose CSS framework, but as a **styling engine for design systems and shared component APIs**.
5
+ Tasty is CSS-in-JS for React design systems. It is built for the point where reusable components have enough intersecting states, variants, themes, and extension rules that ordinary selector competition becomes hard to reason about.
6
6
 
7
- It targets a different layer: helping design-system teams define a **house styling language** on top of CSS.
7
+ Its distinctive guarantee is narrow and concrete: each property’s state map compiles into selectors that cannot compete, so one declared branch wins regardless of CSS source order.
8
8
 
9
9
  That does not require a big upfront configuration step. Tasty's built-in units and normal CSS color values work out of the box, and `okhsl(...)` is available immediately as the recommended path for color authoring. The extra setup comes later if a team wants shared tokens, aliases, recipes, or stricter conventions.
10
10
 
@@ -16,7 +16,7 @@ Most styling tools focus on one of these layers:
16
16
  - utility composition
17
17
  - atomic CSS generation
18
18
 
19
- Tasty's house styling language can include tokens, state semantics, style props, recipes, custom units, and sub-element rules. In other words, it is a governed styling model, not just another syntax for writing CSS.
19
+ For design-system teams, that state model can sit alongside tokens, typed style props, recipes, custom units, and sub-element rules. Tasty can therefore define the styling language exposed by shared components, not just another syntax for writing CSS.
20
20
 
21
21
  That is why syntax-level comparisons are often shallow. The more meaningful comparison is about:
22
22
 
@@ -29,14 +29,14 @@ That is why syntax-level comparisons are often shallow. The more meaningful comp
29
29
 
30
30
  ## High-level positioning
31
31
 
32
- | System | Best described as | Main authoring model | Conflict model | RSC / zero-JS SSG | Best fit |
33
- |---|---|---|---|---|---|
34
- | **Tasty** | Design-system styling engine | Custom DSL with tokens, state maps, recipes, style props, sub-elements, custom units | **Mutually exclusive selector resolution** for stateful styles | Yes — hook-free, server components by default | Teams building shared component APIs or a house styling language |
35
- | **Tailwind CSS** | Utility-first styling framework | Utility classes in markup | Utility composition, variants, and framework-controlled ordering | Yes — no JS runtime | Product teams optimizing for speed and direct authoring |
36
- | **Panda CSS** | Typed styling engine with atomic output | Typed style objects, recipes, generated primitives, style props | Atomic CSS with static analysis | Yes — build-time extraction | Teams wanting a DS-friendly engine with typed primitives |
37
- | **vanilla-extract** | Zero-runtime TS-native stylesheet system | `.css.ts` files, theme contracts, style composition | Standard CSS semantics | Yes — build-time extraction | Teams wanting static CSS and low-level control |
38
- | **StyleX** | Compiler-based atomic styling system | JS authoring with compiler-generated atomic CSS | Compiler-controlled atomic composition | Yes — compiler-extracted | Large app teams wanting optimized, predictable atomic styling |
39
- | **Stitches** (deprecated) **/ Emotion** | Component-first CSS-in-JS | Styled components, `css()` APIs, object/string styles | Composition within CSS-in-JS rules | No — requires `'use client'` | Teams optimizing for component DX and flexible styling |
32
+ | System | Best described as | Main authoring model | Conflict model | RSC / zero-JS SSG | Best fit |
33
+ | --------------------------------------- | ---------------------------------------- | -------------------------------------------------------------------------------- | ---------------------------------------------------------------- | --------------------------------------------- | ------------------------------------------------------------- |
34
+ | **Tasty** | CSS-in-JS for React design systems | CSS-like objects with state maps, tokens, recipes, style props, and sub-elements | **Mutually exclusive selector resolution** for stateful styles | Yes — hook-free, server components by default | Teams building long-lived shared component APIs |
35
+ | **Tailwind CSS** | Utility-first styling framework | Utility classes in markup | Utility composition, variants, and framework-controlled ordering | Yes — no JS runtime | Product teams optimizing for speed and direct authoring |
36
+ | **Panda CSS** | Typed styling engine with atomic output | Typed style objects, recipes, generated primitives, style props | Atomic CSS with static analysis | Yes — build-time extraction | Teams wanting a DS-friendly engine with typed primitives |
37
+ | **vanilla-extract** | Zero-runtime TS-native stylesheet system | `.css.ts` files, theme contracts, style composition | Standard CSS semantics | Yes — build-time extraction | Teams wanting static CSS and low-level control |
38
+ | **StyleX** | Compiler-based atomic styling system | JS authoring with compiler-generated atomic CSS | Compiler-controlled atomic composition | Yes — compiler-extracted | Large app teams wanting optimized, predictable atomic styling |
39
+ | **Stitches** (deprecated) **/ Emotion** | Component-first CSS-in-JS | Styled components, `css()` APIs, object/string styles | Composition within CSS-in-JS rules | No — requires `'use client'` | Teams optimizing for component DX and flexible styling |
40
40
 
41
41
  ---
42
42
 
@@ -69,8 +69,12 @@ This is especially relevant for components with intersecting states such as:
69
69
  Here is a minimal example. Two CSS rules for a button's background — one for `:hover`, one for `[disabled]`:
70
70
 
71
71
  ```css
72
- .btn:hover { background: dodgerblue; }
73
- .btn[disabled] { background: gray; }
72
+ .btn:hover {
73
+ background: dodgerblue;
74
+ }
75
+ .btn[disabled] {
76
+ background: gray;
77
+ }
74
78
  ```
75
79
 
76
80
  When the button is both hovered **and** disabled, both selectors match with equal specificity. The last rule in source order wins. Swap the two lines and the visual behavior silently reverses — a hovered disabled button turns blue instead of gray.
@@ -81,11 +85,11 @@ In Tasty, the same intent is declared as a state map:
81
85
  fill: {
82
86
  '': '#primary',
83
87
  ':hover': '#primary-hover',
84
- 'disabled': '#surface',
88
+ '[disabled]': '#surface',
85
89
  }
86
90
  ```
87
91
 
88
- Tasty compiles this into selectors where `disabled` is guarded by `:not(:hover)` negations (and vice versa), so exactly one rule matches regardless of source order. The outcome is defined by the state map, not by which line comes last.
92
+ Because `[disabled]` is declared later, it has higher priority. Tasty excludes disabled buttons from the hover selector, so exactly one background rule matches. The outcome is defined by the state map, not by which CSS rule comes last.
89
93
 
90
94
  That makes Tasty less of a "better way to write CSS objects" and more of a **state-aware style compiler for design systems**.
91
95
 
@@ -131,16 +135,36 @@ To make this concrete, consider a button with `hover`, `disabled`, and `theme=da
131
135
  **Plain CSS** — you need a selector for every intersection, and equal-specificity rules depend on source order:
132
136
 
133
137
  ```css
134
- .btn { background: var(--primary); color: white; cursor: pointer; }
135
- .btn:hover { background: var(--primary-hover); }
136
- .btn:active { background: var(--primary-pressed); }
137
- .btn[disabled] { background: var(--surface); color: var(--text-40); cursor: not-allowed; }
138
+ .btn {
139
+ background: var(--primary);
140
+ color: white;
141
+ cursor: pointer;
142
+ }
143
+ .btn:hover {
144
+ background: var(--primary-hover);
145
+ }
146
+ .btn:active {
147
+ background: var(--primary-pressed);
148
+ }
149
+ .btn[disabled] {
150
+ background: var(--surface);
151
+ color: var(--text-40);
152
+ cursor: not-allowed;
153
+ }
138
154
 
139
155
  /* theme=danger overrides — must repeat disabled/hover/active */
140
- .btn[data-theme="danger"] { background: var(--danger); }
141
- .btn[data-theme="danger"]:hover { background: var(--danger-hover); }
142
- .btn[data-theme="danger"]:active { background: var(--danger-pressed); }
143
- .btn[data-theme="danger"][disabled] { background: var(--surface); }
156
+ .btn[data-theme='danger'] {
157
+ background: var(--danger);
158
+ }
159
+ .btn[data-theme='danger']:hover {
160
+ background: var(--danger-hover);
161
+ }
162
+ .btn[data-theme='danger']:active {
163
+ background: var(--danger-pressed);
164
+ }
165
+ .btn[data-theme='danger'][disabled] {
166
+ background: var(--surface);
167
+ }
144
168
 
145
169
  /* Bug: .btn:hover and .btn[disabled] have the same specificity.
146
170
  A hovered disabled button gets :hover styles — unless source order saves you. */
@@ -172,18 +196,18 @@ const Button = tasty({
172
196
  '': '#primary',
173
197
  ':hover': '#primary-hover',
174
198
  ':active': '#primary-pressed',
175
- 'disabled': '#surface',
199
+ disabled: '#surface',
176
200
  'theme=danger': '#danger',
177
201
  'theme=danger & :hover': '#danger-hover',
178
202
  'theme=danger & :active': '#danger-pressed',
179
203
  },
180
204
  color: {
181
205
  '': '#on-primary',
182
- 'disabled': '#text.40',
206
+ disabled: '#text.40',
183
207
  },
184
208
  cursor: {
185
209
  '': 'pointer',
186
- 'disabled': 'not-allowed',
210
+ disabled: 'not-allowed',
187
211
  },
188
212
  },
189
213
  });
@@ -235,12 +259,12 @@ Tasty is more opinionated.
235
259
 
236
260
  It behaves less like "TypeScript that outputs CSS" and more like a **state-aware style compiler**. It is designed to encode higher-level styling semantics rather than only expose CSS primitives in typed form.
237
261
 
238
- This also makes Tasty's rendering model notable:
262
+ This also makes Tasty's delivery model notable:
239
263
 
240
- - `tasty()` components are hook-free and work as React Server Components. In server-only contexts (Next.js RSC, Astro without islands), they produce static HTML + CSS with zero client JavaScript — the full feature set is available without sacrificing server rendering
241
- - `tastyStatic()` with the Babel plugin produces static class name strings via build-time extraction, with no React dependency at runtime — the output works with any JavaScript framework
264
+ - `tasty()` components are hook-free and work as React Server Components. In server-only contexts, they produce HTML + CSS with zero Tasty styling runtime in the browser while retaining the full React feature set. Astro without islands is the concrete integration; server-only Next.js RSC uses the same architecture.
265
+ - `tastyStatic()` with the Babel plugin produces static class-name objects via build-time extraction, with no React dependency — the output works with any JavaScript framework.
242
266
 
243
- Runtime features like `styleProps`, sub-element components, and dynamic variants are available in the `tasty()` path. The `tastyStatic()` path is framework-agnostic but limited to the DSL, tokens, and state logic.
267
+ React component features like `styleProps`, sub-element components, and dynamic variants are available in the `tasty()` path whether it renders on the server or in the browser. The `tastyStatic()` path is framework-agnostic but limited to the DSL, tokens, and state logic.
244
268
 
245
269
  So the tradeoff is roughly:
246
270
 
@@ -298,32 +322,32 @@ It is less focused on "how do I style this component ergonomically right now?" a
298
322
  - what should be allowed or constrained?
299
323
  - how do we keep style behavior deterministic as the system grows?
300
324
 
301
- So while Stitches and Emotion are strong tools for building components, Tasty is more naturally positioned as a **styling substrate for the design system itself**.
325
+ So while Stitches and Emotion are strong tools for building components, Tasty is more naturally positioned as the **styling engine underneath a design system’s component API**.
302
326
 
303
327
  That makes it narrower in audience, but deeper in architectural ambition.
304
328
 
305
- There is also a fundamental architectural difference: Emotion and styled-components rely on React context and hooks internally, which means they require `'use client'` in modern React and cannot run as React Server Components. Tasty's style functions and `tasty()` components are hook-free, so they work as server components by default and produce zero client JavaScript in server-only contexts. This is not a minor compatibility detail — it means Tasty-based components stay as server components until *your* code needs interactivity, while Emotion and styled-components force the client boundary at the styling layer.
329
+ There is also a fundamental architectural difference: Emotion and styled-components rely on React context and hooks internally, which means they require `'use client'` in modern React and cannot run as React Server Components. Tasty's style functions and `tasty()` components are hook-free, so they work as server components by default and ship no Tasty styling runtime in server-only contexts. This is not a minor compatibility detail — it means Tasty-based components stay as server components until _your_ code needs interactivity, while Emotion and styled-components force the client boundary at the styling layer.
306
330
 
307
331
  For teams evaluating runtime styling at scale, Tasty also documents its runtime benchmarks and caching model in the main [README](../README.md#performance). That matters, but it is still secondary to the core question of whether you want Tasty's deterministic selector model.
308
332
 
309
333
  ---
310
334
 
311
- ## Build-time vs runtime
335
+ ## Where styles are computed
312
336
 
313
337
  Tasty is not limited to one execution model.
314
338
 
315
- The term "runtime" in `tasty()` refers to *when* style computation happens — during React rendering — not to where that rendering occurs. In server-only contexts (Next.js RSC without `'use client'`, Astro without `client:*` directives, SSG), `tasty()` components render on the server, produce static HTML + CSS, and ship **zero client JavaScript**. The full feature set — `styleProps`, sub-elements, variants, state maps — is available. The result is the same as what `tastyStatic()` produces, but without giving up any runtime capabilities.
339
+ Zero-runtime delivery is an outcome, not a synonym for `tastyStatic()`. `tasty()` computes styles during React rendering, but that rendering can happen only on the server. In that case it produces HTML + CSS and ships **zero Tasty styling runtime** to the browser. The full feature set — `styleProps`, sub-elements, variants, state maps — remains available. Astro's `tastyIntegration({ islands: false })` is the explicit setup for this path. Server-only Next.js RSC follows the same architecture; verify the generated output for the target deployment.
316
340
 
317
341
  Client JavaScript only enters the picture when a component needs React interactivity (state, effects, event handlers) — and that is the consuming component's decision, never Tasty's. Tasty never forces the `'use client'` boundary.
318
342
 
319
- `tastyStatic()` with the Babel plugin is for a different scenario: when you want build-time CSS extraction **without React at runtime**. The output is framework-agnostic — any JavaScript framework can consume the resulting class names and CSS. This makes Tasty usable as the compiler layer underneath a design-system implementation, even outside the React ecosystem.
343
+ `tastyStatic()` with the Babel plugin reaches zero-runtime delivery differently: CSS is extracted **during the build**, without React. The output is framework-agnostic — any JavaScript framework can consume the resulting class names and CSS. This makes Tasty usable as the compiler layer underneath a design-system implementation, even outside the React ecosystem.
320
344
 
321
345
  The tradeoff is that some capabilities — `styleProps`, sub-element components (`<Card.Title>`), dynamic variants — are tied to the `tasty()` path. The `tastyStatic()` path is best understood as extraction and compilation of the DSL, tokens, and state logic without a React dependency.
322
346
 
323
- This flexibility is one of Tasty's more unusual strengths:
347
+ This gives Tasty two zero-runtime paths:
324
348
 
325
- - `tasty()` as the default for all React setups — zero client JS in server-only contexts, full feature set, SSR integration available when client hydration is needed
326
- - `tastyStatic()` as a static compiler whose output works with any framework, including non-React ones
349
+ - server-only `tasty()` for the full React component API, with CSS generated during server or static rendering
350
+ - `tastyStatic()` as a build-time compiler whose output works with any framework, including non-React ones
327
351
 
328
352
  ---
329
353
 
@@ -332,22 +356,27 @@ This flexibility is one of Tasty's more unusual strengths:
332
356
  Another useful way to think about the ecosystem is by abstraction level.
333
357
 
334
358
  ### Direct styling tools
359
+
335
360
  These are optimized for styling product code directly.
336
361
 
337
362
  Examples:
363
+
338
364
  - Tailwind CSS
339
365
  - Emotion (requires `'use client'` — not RSC-compatible)
340
366
  - Stitches (deprecated, requires `'use client'`)
341
367
 
342
368
  ### Typed styling engines
369
+
343
370
  These are optimized for generating CSS with stronger structure and tooling.
344
371
 
345
372
  Examples:
373
+
346
374
  - Panda CSS
347
375
  - vanilla-extract
348
376
  - StyleX
349
377
 
350
378
  ### Design-system language engines
379
+
351
380
  These are optimized for helping a team define its own styling grammar and semantics.
352
381
 
353
382
  Tasty belongs most naturally here.
@@ -364,7 +393,7 @@ Tasty makes the most sense when:
364
393
  - a shared component API is emerging even if the design system is still lightweight
365
394
  - styling should be governed through a central platform team
366
395
  - component state logic is complex
367
- - the team wants a house styling language instead of raw CSS-shaped authoring
396
+ - the team wants a design-system-owned styling language instead of raw CSS-shaped authoring
368
397
  - tokens, recipes, and sub-elements should be first-class
369
398
  - deterministic state resolution matters more than minimum abstraction overhead
370
399
  - the styling engine may need to work as either a runtime tool or a build-time compiler
@@ -414,6 +443,6 @@ Tasty is most compelling when the problem is not just "how do we write styles,"
414
443
  - [React API](react-api.md) — `tasty()` factory, component props, variants, sub-elements, style functions
415
444
  - [Style Properties](styles.md) — complete reference for all enhanced style properties
416
445
  - [Configuration](configuration.md) — tokens, recipes, custom units, style handlers, and TypeScript extensions
417
- - [Zero Runtime (tastyStatic)](tasty-static.md) — build-time static styling with Babel plugin
446
+ - [Build-Time Extraction (`tastyStatic`)](tasty-static.md) — static styling with the Babel plugin
418
447
  - [Adoption Guide](adoption.md) — where Tasty sits in the stack, incremental adoption, and what changes for product engineers
419
448
  - [Server-Side Rendering](ssr.md) — SSR setup for Next.js, Astro, and generic frameworks