@tenphi/tasty 0.0.0-snapshot.1c5dff6 → 0.0.0-snapshot.1f1c00a

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 (331) hide show
  1. package/README.md +377 -238
  2. package/dist/babel-rZcEwPLM.d.ts +82 -0
  3. package/dist/chunks/async-storage-CG5HnUT5.js +20 -0
  4. package/dist/chunks/async-storage-CG5HnUT5.js.map +1 -0
  5. package/dist/chunks/build-config-CL4eRu2z.js +42 -0
  6. package/dist/chunks/build-config-CL4eRu2z.js.map +1 -0
  7. package/dist/chunks/collector-D2ARGT2L.js +208 -0
  8. package/dist/chunks/collector-D2ARGT2L.js.map +1 -0
  9. package/dist/chunks/config-engine-DEmmPPdp.js +272 -0
  10. package/dist/chunks/config-engine-DEmmPPdp.js.map +1 -0
  11. package/dist/chunks/css-definitions-DotDkIXt.js +876 -0
  12. package/dist/chunks/css-definitions-DotDkIXt.js.map +1 -0
  13. package/dist/chunks/css-resources-P7IBUUZs.js +142 -0
  14. package/dist/chunks/css-resources-P7IBUUZs.js.map +1 -0
  15. package/dist/chunks/debug-D8pg9RGI.js +502 -0
  16. package/dist/chunks/debug-D8pg9RGI.js.map +1 -0
  17. package/dist/chunks/dsl-DRhFWQzo.js +1378 -0
  18. package/dist/chunks/dsl-DRhFWQzo.js.map +1 -0
  19. package/dist/chunks/hydration-TRcz5vnk.js +41 -0
  20. package/dist/chunks/hydration-TRcz5vnk.js.map +1 -0
  21. package/dist/chunks/react-runtime-BpxlOTbO.js +562 -0
  22. package/dist/chunks/react-runtime-BpxlOTbO.js.map +1 -0
  23. package/dist/chunks/runtime-engine-CKFXUH-A.js +2416 -0
  24. package/dist/chunks/runtime-engine-CKFXUH-A.js.map +1 -0
  25. package/dist/chunks/runtime-state-BxaLhVdP.js +127 -0
  26. package/dist/chunks/runtime-state-BxaLhVdP.js.map +1 -0
  27. package/dist/{utils/merge-styles.js → chunks/shared-utils-y-HjvPN4.js} +61 -58
  28. package/dist/chunks/shared-utils-y-HjvPN4.js.map +1 -0
  29. package/dist/chunks/style-engine-BBbBuSkT.js +5260 -0
  30. package/dist/chunks/style-engine-BBbBuSkT.js.map +1 -0
  31. package/dist/{zero/extractor.js → chunks/zero-engine-gJrKBRiw.js} +126 -66
  32. package/dist/chunks/zero-engine-gJrKBRiw.js.map +1 -0
  33. package/dist/collector-w4mBj5gd.d.ts +164 -0
  34. package/dist/config-CZWCzBr6.d.ts +1349 -0
  35. package/dist/core/index.d.ts +5 -33
  36. package/dist/core/index.js +9 -26
  37. package/dist/index-G9JuwM9p.d.ts +1920 -0
  38. package/dist/index.d.ts +5 -48
  39. package/dist/index.js +11 -33
  40. package/dist/{utils/merge-styles.d.ts → merge-styles-VXwJ0Frr.d.ts} +3 -4
  41. package/dist/ssr/astro-client.d.ts +1 -0
  42. package/dist/ssr/astro-client.js +6 -0
  43. package/dist/ssr/astro-client.js.map +1 -0
  44. package/dist/ssr/astro-middleware-extract-static.d.ts +10 -0
  45. package/dist/ssr/astro-middleware-extract-static.js +9 -0
  46. package/dist/ssr/astro-middleware-extract-static.js.map +1 -0
  47. package/dist/ssr/astro-middleware-extract.d.ts +10 -0
  48. package/dist/ssr/astro-middleware-extract.js +9 -0
  49. package/dist/ssr/astro-middleware-extract.js.map +1 -0
  50. package/dist/ssr/astro-middleware-static.d.ts +17 -0
  51. package/dist/ssr/astro-middleware-static.js +7 -0
  52. package/dist/ssr/astro-middleware-static.js.map +1 -0
  53. package/dist/ssr/astro-middleware.d.ts +18 -0
  54. package/dist/ssr/astro-middleware.js +7 -0
  55. package/dist/ssr/astro-middleware.js.map +1 -0
  56. package/dist/ssr/astro.d.ts +127 -0
  57. package/dist/ssr/astro.js +221 -0
  58. package/dist/ssr/astro.js.map +1 -0
  59. package/dist/ssr/index.d.ts +36 -0
  60. package/dist/ssr/index.js +10 -0
  61. package/dist/ssr/index.js.map +1 -0
  62. package/dist/ssr/next-config.d.ts +64 -0
  63. package/dist/ssr/next-config.js +128 -0
  64. package/dist/ssr/next-config.js.map +1 -0
  65. package/dist/ssr/next.d.ts +46 -0
  66. package/dist/ssr/next.js +60 -0
  67. package/dist/ssr/next.js.map +1 -0
  68. package/dist/static/index.d.ts +90 -5
  69. package/dist/static/index.js +31 -4
  70. package/dist/static/index.js.map +1 -0
  71. package/dist/static/inject.d.ts +4 -0
  72. package/dist/static/inject.js +17 -0
  73. package/dist/static/inject.js.map +1 -0
  74. package/dist/types-JEY8PVYP.d.ts +1567 -0
  75. package/dist/zero/babel.d.ts +2 -113
  76. package/dist/zero/babel.js +230 -106
  77. package/dist/zero/babel.js.map +1 -1
  78. package/dist/zero/index.d.ts +80 -3
  79. package/dist/zero/index.js +2 -4
  80. package/dist/zero/next.d.ts +57 -33
  81. package/dist/zero/next.js +93 -59
  82. package/dist/zero/next.js.map +1 -1
  83. package/docs/README.md +39 -0
  84. package/docs/adoption.md +323 -0
  85. package/docs/ai-agents.md +250 -0
  86. package/docs/comparison.md +448 -0
  87. package/docs/configuration.md +684 -56
  88. package/docs/debug.md +170 -343
  89. package/docs/design-system.md +455 -0
  90. package/docs/dsl.md +1065 -0
  91. package/docs/getting-started.md +240 -0
  92. package/docs/injector.md +159 -53
  93. package/docs/methodology.md +648 -0
  94. package/docs/migration-v3.md +285 -0
  95. package/docs/pipeline.md +765 -0
  96. package/docs/plugins.md +353 -0
  97. package/docs/react-api.md +683 -0
  98. package/docs/runtime-benchmarks.md +388 -0
  99. package/docs/ssr.md +610 -0
  100. package/docs/styles.md +211 -63
  101. package/docs/tasty-static.md +273 -67
  102. package/package.json +182 -58
  103. package/tasty.config.ts +6 -0
  104. package/dist/_virtual/_rolldown/runtime.js +0 -8
  105. package/dist/chunks/cacheKey.js +0 -70
  106. package/dist/chunks/cacheKey.js.map +0 -1
  107. package/dist/chunks/definitions.d.ts +0 -37
  108. package/dist/chunks/definitions.js +0 -260
  109. package/dist/chunks/definitions.js.map +0 -1
  110. package/dist/chunks/renderChunk.js +0 -61
  111. package/dist/chunks/renderChunk.js.map +0 -1
  112. package/dist/config.d.ts +0 -288
  113. package/dist/config.js +0 -403
  114. package/dist/config.js.map +0 -1
  115. package/dist/debug.d.ts +0 -204
  116. package/dist/debug.js +0 -733
  117. package/dist/debug.js.map +0 -1
  118. package/dist/hooks/useGlobalStyles.d.ts +0 -27
  119. package/dist/hooks/useGlobalStyles.js +0 -56
  120. package/dist/hooks/useGlobalStyles.js.map +0 -1
  121. package/dist/hooks/useKeyframes.d.ts +0 -56
  122. package/dist/hooks/useKeyframes.js +0 -54
  123. package/dist/hooks/useKeyframes.js.map +0 -1
  124. package/dist/hooks/useProperty.d.ts +0 -79
  125. package/dist/hooks/useProperty.js +0 -91
  126. package/dist/hooks/useProperty.js.map +0 -1
  127. package/dist/hooks/useRawCSS.d.ts +0 -53
  128. package/dist/hooks/useRawCSS.js +0 -28
  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 -169
  132. package/dist/hooks/useStyles.js.map +0 -1
  133. package/dist/injector/index.d.ts +0 -157
  134. package/dist/injector/index.js +0 -154
  135. package/dist/injector/index.js.map +0 -1
  136. package/dist/injector/injector.d.ts +0 -139
  137. package/dist/injector/injector.js +0 -417
  138. package/dist/injector/injector.js.map +0 -1
  139. package/dist/injector/sheet-manager.d.ts +0 -135
  140. package/dist/injector/sheet-manager.js +0 -728
  141. package/dist/injector/sheet-manager.js.map +0 -1
  142. package/dist/injector/types.d.ts +0 -144
  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 -33
  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 -116
  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 -231
  163. package/dist/pipeline/exclusive.js.map +0 -1
  164. package/dist/pipeline/index.d.ts +0 -53
  165. package/dist/pipeline/index.js +0 -660
  166. package/dist/pipeline/index.js.map +0 -1
  167. package/dist/pipeline/materialize.js +0 -856
  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 -451
  171. package/dist/pipeline/parseStateKey.js.map +0 -1
  172. package/dist/pipeline/simplify.js +0 -516
  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/okhsl-plugin.d.ts +0 -35
  177. package/dist/plugins/okhsl-plugin.js +0 -371
  178. package/dist/plugins/okhsl-plugin.js.map +0 -1
  179. package/dist/plugins/types.d.ts +0 -69
  180. package/dist/properties/index.js +0 -221
  181. package/dist/properties/index.js.map +0 -1
  182. package/dist/properties/property-type-resolver.d.ts +0 -24
  183. package/dist/properties/property-type-resolver.js +0 -83
  184. package/dist/properties/property-type-resolver.js.map +0 -1
  185. package/dist/states/index.d.ts +0 -49
  186. package/dist/states/index.js +0 -416
  187. package/dist/states/index.js.map +0 -1
  188. package/dist/static/tastyStatic.d.ts +0 -46
  189. package/dist/static/tastyStatic.js +0 -31
  190. package/dist/static/tastyStatic.js.map +0 -1
  191. package/dist/static/types.d.ts +0 -49
  192. package/dist/static/types.js +0 -24
  193. package/dist/static/types.js.map +0 -1
  194. package/dist/styles/align.d.ts +0 -15
  195. package/dist/styles/align.js +0 -14
  196. package/dist/styles/align.js.map +0 -1
  197. package/dist/styles/border.d.ts +0 -25
  198. package/dist/styles/border.js +0 -114
  199. package/dist/styles/border.js.map +0 -1
  200. package/dist/styles/color.d.ts +0 -14
  201. package/dist/styles/color.js +0 -23
  202. package/dist/styles/color.js.map +0 -1
  203. package/dist/styles/createStyle.js +0 -77
  204. package/dist/styles/createStyle.js.map +0 -1
  205. package/dist/styles/dimension.js +0 -97
  206. package/dist/styles/dimension.js.map +0 -1
  207. package/dist/styles/display.d.ts +0 -37
  208. package/dist/styles/display.js +0 -67
  209. package/dist/styles/display.js.map +0 -1
  210. package/dist/styles/fade.d.ts +0 -15
  211. package/dist/styles/fade.js +0 -58
  212. package/dist/styles/fade.js.map +0 -1
  213. package/dist/styles/fill.d.ts +0 -42
  214. package/dist/styles/fill.js +0 -52
  215. package/dist/styles/fill.js.map +0 -1
  216. package/dist/styles/flow.d.ts +0 -16
  217. package/dist/styles/flow.js +0 -12
  218. package/dist/styles/flow.js.map +0 -1
  219. package/dist/styles/gap.d.ts +0 -31
  220. package/dist/styles/gap.js +0 -37
  221. package/dist/styles/gap.js.map +0 -1
  222. package/dist/styles/height.d.ts +0 -17
  223. package/dist/styles/height.js +0 -20
  224. package/dist/styles/height.js.map +0 -1
  225. package/dist/styles/index.d.ts +0 -2
  226. package/dist/styles/index.js +0 -9
  227. package/dist/styles/index.js.map +0 -1
  228. package/dist/styles/inset.d.ts +0 -52
  229. package/dist/styles/inset.js +0 -150
  230. package/dist/styles/inset.js.map +0 -1
  231. package/dist/styles/justify.d.ts +0 -15
  232. package/dist/styles/justify.js +0 -14
  233. package/dist/styles/justify.js.map +0 -1
  234. package/dist/styles/list.d.ts +0 -16
  235. package/dist/styles/list.js +0 -98
  236. package/dist/styles/list.js.map +0 -1
  237. package/dist/styles/margin.d.ts +0 -24
  238. package/dist/styles/margin.js +0 -104
  239. package/dist/styles/margin.js.map +0 -1
  240. package/dist/styles/outline.d.ts +0 -29
  241. package/dist/styles/outline.js +0 -65
  242. package/dist/styles/outline.js.map +0 -1
  243. package/dist/styles/padding.d.ts +0 -24
  244. package/dist/styles/padding.js +0 -104
  245. package/dist/styles/padding.js.map +0 -1
  246. package/dist/styles/predefined.d.ts +0 -73
  247. package/dist/styles/predefined.js +0 -241
  248. package/dist/styles/predefined.js.map +0 -1
  249. package/dist/styles/preset.d.ts +0 -47
  250. package/dist/styles/preset.js +0 -126
  251. package/dist/styles/preset.js.map +0 -1
  252. package/dist/styles/radius.d.ts +0 -14
  253. package/dist/styles/radius.js +0 -51
  254. package/dist/styles/radius.js.map +0 -1
  255. package/dist/styles/scrollbar.d.ts +0 -23
  256. package/dist/styles/scrollbar.js +0 -34
  257. package/dist/styles/scrollbar.js.map +0 -1
  258. package/dist/styles/shadow.d.ts +0 -14
  259. package/dist/styles/shadow.js +0 -24
  260. package/dist/styles/shadow.js.map +0 -1
  261. package/dist/styles/styledScrollbar.d.ts +0 -47
  262. package/dist/styles/styledScrollbar.js +0 -38
  263. package/dist/styles/styledScrollbar.js.map +0 -1
  264. package/dist/styles/transition.d.ts +0 -14
  265. package/dist/styles/transition.js +0 -158
  266. package/dist/styles/transition.js.map +0 -1
  267. package/dist/styles/types.d.ts +0 -498
  268. package/dist/styles/width.d.ts +0 -17
  269. package/dist/styles/width.js +0 -20
  270. package/dist/styles/width.js.map +0 -1
  271. package/dist/tasty.d.ts +0 -982
  272. package/dist/tasty.js +0 -206
  273. package/dist/tasty.js.map +0 -1
  274. package/dist/tokens/typography.d.ts +0 -19
  275. package/dist/tokens/typography.js +0 -237
  276. package/dist/tokens/typography.js.map +0 -1
  277. package/dist/types.d.ts +0 -184
  278. package/dist/utils/cache-wrapper.js +0 -26
  279. package/dist/utils/cache-wrapper.js.map +0 -1
  280. package/dist/utils/case-converter.js +0 -8
  281. package/dist/utils/case-converter.js.map +0 -1
  282. package/dist/utils/colors.d.ts +0 -5
  283. package/dist/utils/colors.js +0 -9
  284. package/dist/utils/colors.js.map +0 -1
  285. package/dist/utils/css-types.d.ts +0 -7
  286. package/dist/utils/dotize.d.ts +0 -26
  287. package/dist/utils/dotize.js +0 -122
  288. package/dist/utils/dotize.js.map +0 -1
  289. package/dist/utils/filter-base-props.d.ts +0 -15
  290. package/dist/utils/filter-base-props.js +0 -45
  291. package/dist/utils/filter-base-props.js.map +0 -1
  292. package/dist/utils/get-display-name.d.ts +0 -7
  293. package/dist/utils/get-display-name.js +0 -10
  294. package/dist/utils/get-display-name.js.map +0 -1
  295. package/dist/utils/hsl-to-rgb.js +0 -38
  296. package/dist/utils/hsl-to-rgb.js.map +0 -1
  297. package/dist/utils/is-dev-env.js +0 -19
  298. package/dist/utils/is-dev-env.js.map +0 -1
  299. package/dist/utils/is-valid-element-type.js +0 -15
  300. package/dist/utils/is-valid-element-type.js.map +0 -1
  301. package/dist/utils/merge-styles.js.map +0 -1
  302. package/dist/utils/mod-attrs.d.ts +0 -8
  303. package/dist/utils/mod-attrs.js +0 -21
  304. package/dist/utils/mod-attrs.js.map +0 -1
  305. package/dist/utils/okhsl-to-rgb.js +0 -296
  306. package/dist/utils/okhsl-to-rgb.js.map +0 -1
  307. package/dist/utils/process-tokens.d.ts +0 -31
  308. package/dist/utils/process-tokens.js +0 -171
  309. package/dist/utils/process-tokens.js.map +0 -1
  310. package/dist/utils/resolve-recipes.d.ts +0 -17
  311. package/dist/utils/resolve-recipes.js +0 -147
  312. package/dist/utils/resolve-recipes.js.map +0 -1
  313. package/dist/utils/selector-transform.js +0 -32
  314. package/dist/utils/selector-transform.js.map +0 -1
  315. package/dist/utils/string.js +0 -8
  316. package/dist/utils/string.js.map +0 -1
  317. package/dist/utils/styles.d.ts +0 -177
  318. package/dist/utils/styles.js +0 -730
  319. package/dist/utils/styles.js.map +0 -1
  320. package/dist/utils/typography.d.ts +0 -36
  321. package/dist/utils/typography.js +0 -53
  322. package/dist/utils/typography.js.map +0 -1
  323. package/dist/utils/warnings.d.ts +0 -16
  324. package/dist/utils/warnings.js +0 -16
  325. package/dist/utils/warnings.js.map +0 -1
  326. package/dist/zero/css-writer.d.ts +0 -45
  327. package/dist/zero/css-writer.js +0 -74
  328. package/dist/zero/css-writer.js.map +0 -1
  329. package/dist/zero/extractor.d.ts +0 -24
  330. package/dist/zero/extractor.js.map +0 -1
  331. package/docs/usage.md +0 -695
@@ -0,0 +1,683 @@
1
+ # React API
2
+
3
+ Use `tasty()` to turn state-aware style definitions into normal React components, extend existing Tasty components, and expose typed styling APIs to consumers.
4
+
5
+ All Tasty style functions — `tasty()` components, `useStyles()`, `useGlobalStyles()`, `useRawCSS()`, `useKeyframes()`, `useProperty()`, `useFontFace()`, `useCounterStyle()`, and `useFunction()` — are hook-free and compatible with React Server Components. No `'use client'` directive is needed. For state maps, tokens, units, and extension semantics, see [Style DSL](dsl.md). For global configuration, see [Configuration](configuration.md). For the broader docs map, see the [Docs Hub](README.md).
6
+
7
+ > **Note:** This file was previously named `runtime.md`. All functionality documented here works in both server and client contexts — "runtime" referred to style computation during React rendering, not to client-side JavaScript.
8
+
9
+ ---
10
+
11
+ ## Component Creation
12
+
13
+ ### Create a new component
14
+
15
+ ```jsx
16
+ import { tasty } from '@tenphi/tasty';
17
+
18
+ const Card = tasty({
19
+ as: 'div',
20
+ styles: {
21
+ padding: '4x',
22
+ fill: '#white',
23
+ border: true,
24
+ radius: true,
25
+ },
26
+ styleProps: ['padding', 'fill'],
27
+ });
28
+
29
+ <Card>Hello World</Card>
30
+ <Card padding="6x" fill="#gray.05">Custom Card</Card>
31
+ ```
32
+
33
+ ### Extend an existing component
34
+
35
+ ```jsx
36
+ const PrimaryButton = tasty(Button, {
37
+ styles: {
38
+ fill: '#purple',
39
+ color: '#white',
40
+ padding: '2x 4x',
41
+ },
42
+ });
43
+ ```
44
+
45
+ Style maps merge intelligently — see [Style DSL — Extending vs. Replacing State Maps](dsl.md#extending-vs-replacing-state-maps) for extend mode, replace mode, `@inherit`, `null`, and `false` tombstones.
46
+
47
+ `tasty(Component, ...)` always wraps `Component` and forwards every prop to it, so `Component` must be Tasty-aware (i.e. it accepts `styles`, `mods`, `qa`, etc. and renders them through its own Tasty pipeline). To apply styles to a third-party component or a string DOM tag via `className`, use the options-only form with `as`:
48
+
49
+ ```jsx
50
+ import NextLink from 'next/link';
51
+
52
+ const Link = tasty({
53
+ as: NextLink,
54
+ styles: {
55
+ color: { '': '#accent-text', ':hover': '#text' },
56
+ textDecoration: 'underline',
57
+ },
58
+ styleProps: ['padding'],
59
+ });
60
+
61
+ const Span = tasty({
62
+ as: 'span',
63
+ styles: { preset: 'strong' },
64
+ });
65
+
66
+ <Link href="/blog" padding="1x">
67
+ Blog
68
+ </Link>;
69
+ ```
70
+
71
+ The wrapped component only needs to forward `className` (and ideally `style`/`ref`). Tasty-specific props (`qa`, `qaVal`, `mods`, `tokens`, `styleProps`, `modProps`, `tokenProps`) are consumed by Tasty and never leak to the DOM.
72
+
73
+ Props forwarded to the `as` component keep their declared types and requiredness. For example, a component with `position: 'top-right' | 'bottom-left'` accepts those values through `tasty({ as: Component })`; CSS positioning can still be set through `styles.position`. A prop explicitly listed in `styleProps` is consumed as a style value instead. The always-available base style props below are also consumed by Tasty, so keep component-specific prop names separate from those reserved names.
74
+
75
+ ---
76
+
77
+ ## Style Props
78
+
79
+ Use `styleProps` to expose style properties as direct component props:
80
+
81
+ ```jsx
82
+ const FlexibleBox = tasty({
83
+ as: 'div',
84
+ styles: {
85
+ display: 'flex',
86
+ padding: '2x',
87
+ },
88
+ styleProps: ['gap', 'align', 'placeContent', 'fill'],
89
+ });
90
+
91
+ <FlexibleBox gap="2x" align="center" fill="#surface">
92
+ Content
93
+ </FlexibleBox>;
94
+ ```
95
+
96
+ Style props accept state maps, so responsive values work through the same API:
97
+
98
+ ```jsx
99
+ <FlexibleBox
100
+ gap={{ '': '2x', '@tablet': '4x' }}
101
+ fill={{ '': '#surface', '@dark': '#surface-dark' }}
102
+ >
103
+ ```
104
+
105
+ For predefined style prop lists (`FLOW_STYLES`, `POSITION_STYLES`, `DIMENSION_STYLES`, etc.) and guidance on which props to expose per component category, see [Methodology — styleProps as the public API](methodology.md#styleprops-as-the-public-api).
106
+
107
+ ### Always-available style props
108
+
109
+ A small set of style properties — `display`, `font`, `preset`, `hide`, `whiteSpace`, `opacity`, and `transition` — are always harvested as style props on every `tasty()` component, even when `styleProps` is omitted. This means you can pass `<Card display="grid" />` or `<Text preset="h1" />` without declaring them. When you do declare `styleProps`, these base props are unioned with your list (not replaced).
110
+
111
+ ---
112
+
113
+ ## `Element`
114
+
115
+ `Element` is the unstyled base component exported from the main entry — equivalent to `tasty({})` (a `div` with no default styles). It accepts all Tasty props (`styles`, `styleProps`, `mods`, `tokens`, `as`, `qa`, `theme`, the `is*` props, etc.) and is useful as a generic styled box or as a building block for layout primitives.
116
+
117
+ ```jsx
118
+ import { Element } from '@tenphi/tasty';
119
+
120
+ <Element as="section" padding="4x" fill="#surface">
121
+ Content
122
+ </Element>;
123
+ ```
124
+
125
+ > Note: `Element` shadows the global DOM `Element` type when imported from `@tenphi/tasty`. In files that need the DOM type, alias the import: `import { Element as TastyElement } from '@tenphi/tasty'`.
126
+
127
+ ---
128
+
129
+ ## Mod Props
130
+
131
+ Use `modProps` to expose modifier keys as direct component props instead of requiring the `mods` object:
132
+
133
+ ```jsx
134
+ // Before: mods object
135
+ <Button mods={{ isLoading: true, size: 'large' }}>Submit</Button>
136
+
137
+ // After: mod props
138
+ <Button isLoading size="large">Submit</Button>
139
+ ```
140
+
141
+ ### Array form
142
+
143
+ List modifier key names. Types default to `ModValue` (`boolean | string | number | undefined | null`):
144
+
145
+ ```jsx
146
+ const Button = tasty({
147
+ modProps: ['isLoading', 'isSelected'] as const,
148
+ styles: {
149
+ fill: { '': '#surface', isLoading: '#surface.5' },
150
+ border: { '': '1bw solid #outline', isSelected: '2bw solid #primary' },
151
+ },
152
+ });
153
+
154
+ <Button isLoading isSelected>Submit</Button>
155
+ // Renders: <button data-is-loading="" data-is-selected="">Submit</button>
156
+ ```
157
+
158
+ ### Object form (typed)
159
+
160
+ Map modifier names to type descriptors for precise TypeScript types:
161
+
162
+ ```tsx
163
+ const Button = tasty({
164
+ modProps: {
165
+ isLoading: Boolean, // isLoading?: boolean
166
+ isSelected: Boolean, // isSelected?: boolean
167
+ size: ['small', 'medium', 'large'] as const, // size?: 'small' | 'medium' | 'large'
168
+ },
169
+ styles: {
170
+ padding: { '': '2x 4x', 'size=small': '1x 2x', 'size=large': '3x 6x' },
171
+ fill: { '': '#surface', isLoading: '#surface.5' },
172
+ },
173
+ });
174
+
175
+ <Button isLoading size="large">
176
+ Submit
177
+ </Button>;
178
+ // Renders: <button data-is-loading="" data-size="large">Submit</button>
179
+ ```
180
+
181
+ Available type descriptors:
182
+
183
+ | Descriptor | TypeScript type | Example |
184
+ | --------------------- | --------------- | ----------------------------------- |
185
+ | `Boolean` | `boolean` | `isLoading: Boolean` |
186
+ | `String` | `string` | `label: String` |
187
+ | `Number` | `number` | `count: Number` |
188
+ | `['a', 'b'] as const` | `'a' \| 'b'` | `size: ['sm', 'md', 'lg'] as const` |
189
+
190
+ ### Merge with `mods`
191
+
192
+ Mod props and the `mods` object can be used together. Mod props take precedence:
193
+
194
+ ```jsx
195
+ <Button mods={{ isLoading: false, extra: true }} isLoading>
196
+ // isLoading=true wins (from mod prop), extra=true preserved from mods
197
+ ```
198
+
199
+ ### When to use `modProps` vs `mods`
200
+
201
+ | Use case | Recommendation |
202
+ | -------------------------------------------- | -------------------------------------------------------- |
203
+ | Component has a fixed set of known modifiers | `modProps` — cleaner API, better TypeScript autocomplete |
204
+ | Component needs arbitrary/dynamic modifiers | `mods` — open-ended `Record<string, ModValue>` |
205
+ | Both fixed and dynamic | Combine: `modProps` for known keys, `mods` for ad-hoc |
206
+
207
+ For architecture guidance on when to use modifiers vs `styleProps`, see [Methodology — modProps and mods](methodology.md#modprops-and-mods).
208
+
209
+ ---
210
+
211
+ ## Token Props
212
+
213
+ Use `tokenProps` to expose token keys as direct component props instead of requiring the `tokens` object:
214
+
215
+ ```jsx
216
+ // Before: tokens object
217
+ <ProgressBar tokens={{ $progress: '75%', '#accent': '#purple' }} />
218
+
219
+ // After: token props
220
+ <ProgressBar progress="75%" accentColor="#purple" />
221
+ ```
222
+
223
+ ### Array form
224
+
225
+ List prop names. Names ending in `Color` map to `#` color tokens; everything else maps to `$` custom property tokens:
226
+
227
+ ```jsx
228
+ const ProgressBar = tasty({
229
+ tokenProps: ['progress', 'accentColor'] as const,
230
+ styles: { width: '$progress', fill: '#accent' },
231
+ });
232
+
233
+ <ProgressBar progress="75%" accentColor="#purple" />
234
+ // 'progress' → $progress → --progress
235
+ // 'accentColor' → #accent → --accent-color
236
+ ```
237
+
238
+ ### Object form
239
+
240
+ Map prop names to explicit `$`/`#`-prefixed token keys:
241
+
242
+ ```tsx
243
+ const Card = tasty({
244
+ tokenProps: {
245
+ size: '$card-size',
246
+ color: '#card-accent',
247
+ },
248
+ styles: { padding: '$card-size', fill: '#card-accent' },
249
+ });
250
+
251
+ <Card size="4x" color="#purple" />;
252
+ ```
253
+
254
+ ### Merge with `tokens`
255
+
256
+ Token props and the `tokens` prop can be used together. Token props take precedence over `tokens`, which takes precedence over default `tokens` in `tasty({...})`:
257
+
258
+ ```jsx
259
+ const Bar = tasty({
260
+ tokenProps: ['progress'] as const,
261
+ tokens: { $progress: '0%' }, // default
262
+ });
263
+
264
+ <Bar tokens={{ $progress: '50%' }} progress="90%" />
265
+ // progress="90%" wins (from token prop)
266
+ ```
267
+
268
+ ### When to use `tokenProps` vs `tokens`
269
+
270
+ | Use case | Recommendation |
271
+ | ---------------------------------------------- | ---------------------------------------------------------- |
272
+ | Component has a fixed set of known token keys | `tokenProps` — cleaner API, better TypeScript autocomplete |
273
+ | Component needs arbitrary/dynamic token values | `tokens` — open-ended `Record<string, TokenValue>` |
274
+ | Both fixed and dynamic | Combine: `tokenProps` for known keys, `tokens` for ad-hoc |
275
+
276
+ For architecture guidance, see [Methodology — tokenProps](methodology.md#tokenprops).
277
+
278
+ ---
279
+
280
+ ## Variants
281
+
282
+ Define named style variations. Only CSS for variants actually used at runtime is injected:
283
+
284
+ ```jsx
285
+ const Button = tasty({
286
+ styles: {
287
+ padding: '2x 4x',
288
+ border: true,
289
+ },
290
+ variants: {
291
+ default: { fill: '#blue', color: '#white' },
292
+ danger: { fill: '#red', color: '#white' },
293
+ outline: { fill: '#clear', color: '#blue', border: '1bw solid #blue' },
294
+ },
295
+ });
296
+
297
+ <Button variant="danger">Delete</Button>;
298
+ ```
299
+
300
+ ### Extending Variants with Base State Maps
301
+
302
+ When base `styles` contain an extend-mode state map (an object **without** a `''` key), it is applied **after** the variant merge. This lets you add or override states across all variants without repeating yourself:
303
+
304
+ ```jsx
305
+ const Badge = tasty({
306
+ styles: {
307
+ padding: '1x 2x',
308
+ border: {
309
+ 'type=primary': '#clear',
310
+ },
311
+ },
312
+ variants: {
313
+ primary: {
314
+ border: { '': '#white.2', pressed: '#primary-text', disabled: '#clear' },
315
+ fill: { '': '#white #primary', hovered: '#white #primary-text' },
316
+ },
317
+ secondary: {
318
+ border: { '': '#primary.15', pressed: '#primary.3' },
319
+ fill: '#primary.10',
320
+ },
321
+ },
322
+ });
323
+
324
+ // Both variants get 'type=primary': '#clear' appended to their border map
325
+ ```
326
+
327
+ Properties that are **not** extend-mode (simple values, state maps with `''`, `null`, `false`, selectors, sub-elements) merge with variants as before — the variant can fully replace them.
328
+
329
+ ---
330
+
331
+ ## Sub-element Styling
332
+
333
+ Sub-elements are inner parts of a compound component, styled via capitalized keys in `styles` and identified by `data-element` attributes in the DOM.
334
+
335
+ > Use the `elements` prop to declare sub-element components. This gives you typed, reusable sub-components (`Card.Title`, `Card.Content`) instead of manually writing `data-element` attributes.
336
+
337
+ ```jsx
338
+ const Card = tasty({
339
+ styles: {
340
+ padding: '4x',
341
+ Title: { preset: 'h3', color: '#primary' },
342
+ Content: { color: '#text' },
343
+ },
344
+ elements: {
345
+ Title: 'h3',
346
+ Content: 'div',
347
+ },
348
+ });
349
+
350
+ <Card>
351
+ <Card.Title>Card Title</Card.Title>
352
+ <Card.Content>Card content</Card.Content>
353
+ </Card>;
354
+ ```
355
+
356
+ Each entry in `elements` can be a tag name string or a config object:
357
+
358
+ ```jsx
359
+ elements: {
360
+ Title: 'h3', // shorthand: tag name only
361
+ Icon: { as: 'span', qa: 'card-icon' }, // full form: tag + QA attribute
362
+ }
363
+ ```
364
+
365
+ The sub-components produced by `elements` support `mods`, `tokens`, `isDisabled`, `isHidden`, and `isChecked` props — the same modifier interface as the root component.
366
+
367
+ If you don't need sub-components (e.g., the inner elements are already rendered by a third-party library), you can still style them by key alone — just omit `elements` and apply `data-element` manually:
368
+
369
+ ```jsx
370
+ const Card = tasty({
371
+ styles: {
372
+ padding: '4x',
373
+ Title: { preset: 'h3', color: '#primary' },
374
+ },
375
+ });
376
+
377
+ <Card>
378
+ <div data-element="Title">Card Title</div>
379
+ </Card>;
380
+ ```
381
+
382
+ ### Selector Affix (`$`)
383
+
384
+ The `$` property inside a sub-element's styles controls how its selector attaches to the root selector — combinators, HTML tags, pseudo-elements, the `@` placeholder, and more. For the full reference table and injection rules, see [DSL — Selector Affix](dsl.md#selector-affix-).
385
+
386
+ For the mental model behind sub-elements — how they share root state context and how this differs from BEM — see [Methodology — Component architecture](methodology.md#component-architecture-root--sub-elements).
387
+
388
+ ---
389
+
390
+ ## computeStyles
391
+
392
+ Hook-free, synchronous style computation. Can be used anywhere — including React Server Components, plain functions, and non-React code:
393
+
394
+ ```tsx
395
+ import { computeStyles } from '@tenphi/tasty';
396
+
397
+ const { className } = computeStyles({
398
+ padding: '2x',
399
+ fill: '#surface',
400
+ radius: '1r',
401
+ });
402
+ ```
403
+
404
+ On the client, CSS is injected synchronously into the DOM (idempotent via the injector cache). On the server, CSS is collected via the SSR collector if one is available. This is the same function that `tasty()` components use internally.
405
+
406
+ ---
407
+
408
+ ## Style Functions
409
+
410
+ All style functions below are plain functions (not React hooks) and can be used in any environment: client components, SSR with a `ServerStyleCollector`, and React Server Components. They retain their `use` prefix for backward compatibility, but do not use any React hooks internally.
411
+
412
+ In server-only contexts, components that use only Tasty style functions ship no Tasty styling runtime. Astro without `client:*` directives produces no client JavaScript; server-only Next.js RSC follows the same Tasty architecture, while final output depends on the application deployment. Tasty never forces the `'use client'` boundary — that decision belongs to your component when it needs React interactivity (state, effects, event handlers).
413
+
414
+ ### useStyles
415
+
416
+ Generate a className from a style object. Thin wrapper around `computeStyles()`:
417
+
418
+ ```tsx
419
+ import { useStyles } from '@tenphi/tasty';
420
+
421
+ function MyComponent() {
422
+ const { className } = useStyles({
423
+ padding: '2x',
424
+ fill: '#surface',
425
+ radius: '1r',
426
+ });
427
+
428
+ return <div className={className}>Styled content</div>;
429
+ }
430
+ ```
431
+
432
+ ### useGlobalStyles
433
+
434
+ Inject global styles for a CSS selector. Accepts an optional third argument with an `id` for update tracking — when the styles change, the previous injection is disposed and the new one is injected:
435
+
436
+ ```tsx
437
+ import { useGlobalStyles } from '@tenphi/tasty';
438
+
439
+ function ThemeStyles() {
440
+ useGlobalStyles('.card', {
441
+ padding: '4x',
442
+ fill: '#surface',
443
+ radius: '1r',
444
+ });
445
+
446
+ return null;
447
+ }
448
+ ```
449
+
450
+ A slot — the `id`, or the selector when no `id` is given — holds exactly one
451
+ injection **per `root`**. So:
452
+
453
+ - Changing the styles replaces the previous CSS rather than adding to it.
454
+ - Passing styles that produce no CSS (for example `{}`) clears the slot.
455
+ - The same selector used in two shadow roots keeps a separate injection in each.
456
+ - Two independent call sites that share a selector share a slot, and the last
457
+ render wins. Give them distinct `id`s if they should coexist.
458
+
459
+ ### useRawCSS
460
+
461
+ Inject raw CSS strings. Accepts an optional `id` in the options for update tracking — when the CSS changes for the same id, the previous injection is replaced:
462
+
463
+ ```tsx
464
+ import { useRawCSS } from '@tenphi/tasty';
465
+
466
+ function GlobalReset() {
467
+ useRawCSS(`
468
+ body { margin: 0; padding: 0; }
469
+ `);
470
+
471
+ return null;
472
+ }
473
+ ```
474
+
475
+ An `id` slot holds one injection per `root`. Without an `id` the CSS is deduped
476
+ by content and permanent — there is nothing to replace it with later.
477
+
478
+ ### useKeyframes
479
+
480
+ Inject `@keyframes` rules and return the generated animation name:
481
+
482
+ ```tsx
483
+ import { useKeyframes } from '@tenphi/tasty';
484
+
485
+ function Spinner() {
486
+ const spin = useKeyframes(
487
+ {
488
+ from: { transform: 'rotate(0deg)' },
489
+ to: { transform: 'rotate(360deg)' },
490
+ },
491
+ { name: 'spin' },
492
+ );
493
+
494
+ return <div style={{ animation: `${spin} 1s linear infinite` }} />;
495
+ }
496
+ ```
497
+
498
+ `useKeyframes()` also supports a factory function. Without a `name` the factory runs on every invocation and deduplication is handled internally by content hash; with a `name`, matching deps skip the factory entirely:
499
+
500
+ ```tsx
501
+ function Pulse({ scale }: { scale: number }) {
502
+ const pulse = useKeyframes(
503
+ () => ({
504
+ '0%': { transform: 'scale(1)' },
505
+ '100%': { transform: `scale(${scale})` },
506
+ }),
507
+ [scale],
508
+ { name: 'pulse' },
509
+ );
510
+
511
+ return (
512
+ <div
513
+ style={{ animation: `${pulse} 500ms ease-in-out alternate infinite` }}
514
+ />
515
+ );
516
+ }
517
+ ```
518
+
519
+ Passing `name` claims a slot owned by that one call site, per `root` — much like
520
+ `id` in `useGlobalStyles()` and `useRawCSS()`. When the steps change the previous
521
+ `@keyframes` rule is disposed and the name is reused, so the rules don't
522
+ accumulate and the returned name stays stable. Anonymous keyframes are permanent
523
+ and shared by content.
524
+
525
+ ### useProperty
526
+
527
+ Register a CSS `@property` rule so a custom property can animate smoothly:
528
+
529
+ ```tsx
530
+ import { useProperty } from '@tenphi/tasty';
531
+
532
+ function Spinner() {
533
+ useProperty('$rotation', {
534
+ syntax: '<angle>',
535
+ inherits: false,
536
+ initialValue: '0deg',
537
+ });
538
+
539
+ return <div style={{ transform: 'rotate(var(--rotation))' }} />;
540
+ }
541
+ ```
542
+
543
+ `useProperty()` accepts Tasty token syntax for the property name:
544
+
545
+ - `$name` defines `--name`
546
+ - `#name` defines `--name-color` and auto-infers `<color>`
547
+ - `--name` is also supported for existing CSS variables
548
+
549
+ ### useFontFace
550
+
551
+ Inject `@font-face` rules for custom fonts. Permanent — no cleanup on unmount. Deduplicates by content.
552
+
553
+ ```tsx
554
+ import { useFontFace } from '@tenphi/tasty';
555
+
556
+ function App() {
557
+ useFontFace('Brand Sans', {
558
+ src: 'url("/fonts/brand-sans.woff2") format("woff2")',
559
+ fontWeight: '400 700',
560
+ fontDisplay: 'swap',
561
+ });
562
+
563
+ return <div style={{ fontFamily: '"Brand Sans", sans-serif' }}>Hello</div>;
564
+ }
565
+ ```
566
+
567
+ For multiple weights/styles, pass an array:
568
+
569
+ ```tsx
570
+ useFontFace('Brand Sans', [
571
+ {
572
+ src: 'url("/fonts/brand-regular.woff2") format("woff2")',
573
+ fontWeight: 400,
574
+ fontDisplay: 'swap',
575
+ },
576
+ {
577
+ src: 'url("/fonts/brand-bold.woff2") format("woff2")',
578
+ fontWeight: 700,
579
+ fontDisplay: 'swap',
580
+ },
581
+ ]);
582
+ ```
583
+
584
+ Signature:
585
+
586
+ ```ts
587
+ function useFontFace(family: string, input: FontFaceInput): void;
588
+ ```
589
+
590
+ ### useCounterStyle
591
+
592
+ Inject a `@counter-style` rule and get back the counter style name. Permanent — no cleanup on unmount. Deduplicates by name.
593
+
594
+ ```tsx
595
+ import { useCounterStyle } from '@tenphi/tasty';
596
+
597
+ function EmojiList() {
598
+ const styleName = useCounterStyle(
599
+ {
600
+ system: 'cyclic',
601
+ symbols: '"👍"',
602
+ suffix: '" "',
603
+ },
604
+ { name: 'thumbs' },
605
+ );
606
+
607
+ return (
608
+ <ol style={{ listStyleType: styleName }}>
609
+ <li>First</li>
610
+ <li>Second</li>
611
+ </ol>
612
+ );
613
+ }
614
+ ```
615
+
616
+ Signature:
617
+
618
+ ```ts
619
+ function useCounterStyle(
620
+ descriptors: CounterStyleDescriptors,
621
+ options?: { name?: string; root?: Document | ShadowRoot },
622
+ ): string;
623
+ ```
624
+
625
+ ### useFunction
626
+
627
+ Register a CSS `@function` (custom function). Permanent — no cleanup on unmount. Deduplicates by function name. The function name accepts `$$name` (matching the call site `$$name(...)`), `$name`, or `--name`.
628
+
629
+ ```tsx
630
+ import { tasty, useFunction } from '@tenphi/tasty';
631
+
632
+ const Box = tasty({ styles: { margin: '$$negative(10px) top' } });
633
+
634
+ function Layout() {
635
+ useFunction('$$negative', { args: ['$value'], result: '(-1 * $value)' });
636
+ return <Box />;
637
+ }
638
+ ```
639
+
640
+ Call the function through the Tasty DSL rather than a raw `style` prop. An inline `style` value reaches the browser unparsed, so the `$$name(...)` sugar is never expanded — and under `configure({ polyfills: { functions: true } })` it silently does nothing, because the polyfill rewrites calls at parse time.
641
+
642
+ Inside a `tasty()` component you call functions with the same `$$name(...)` sugar:
643
+
644
+ ```tsx
645
+ const Box = tasty({
646
+ styles: {
647
+ '@function': { $$negative: { args: ['$value'], result: '(-1 * $value)' } },
648
+ margin: '$$negative(10px) top',
649
+ },
650
+ });
651
+ ```
652
+
653
+ Signature:
654
+
655
+ ```ts
656
+ function useFunction(
657
+ name: string,
658
+ definition: FunctionDefinition,
659
+ options?: { root?: Document | ShadowRoot },
660
+ ): void;
661
+ ```
662
+
663
+ See the [Functions section of the DSL reference](dsl.md#functions-function) for the full descriptor shape, token conventions, and value-sugar support. `@function` is an experimental CSS feature — unsupported browsers safely ignore the native rule, or enable the [`polyfills.functions`](configuration.md#polyfills) inline polyfill for full cross-browser support.
664
+
665
+ ### Troubleshooting
666
+
667
+ - Styles are not updating: make sure `configure()` runs before first render, and verify the generated class name or global rule with [Debug Utilities](debug.md).
668
+ - SSR output looks wrong: check the [SSR guide](ssr.md) for collector setup. All style functions discover the SSR collector via `AsyncLocalStorage` or the global getter registered by `TastyRegistry`.
669
+ - Animation/custom property issues: prefer `useKeyframes()` and `useProperty()` over raw CSS when you want Tasty to manage injection and SSR collection for you.
670
+ - For dynamic styles that change over the component lifecycle, use the `id` option in `useGlobalStyles()` and `useRawCSS()` to enable update tracking.
671
+ - RSC inline mode: CSS accumulated by standalone style functions (`useGlobalStyles`, `useRawCSS`, etc.) is flushed into inline `<style>` tags by the next `tasty()` component in the render tree. If your page uses only standalone style functions without any `tasty()` component, the CSS will not be emitted. Ensure at least one `tasty()` component is present in each RSC render tree.
672
+
673
+ ---
674
+
675
+ ## Learn more
676
+
677
+ - **[Style DSL](dsl.md)** — State maps, tokens, units, extending semantics, keyframes, @property
678
+ - **[Methodology](methodology.md)** — Recommended patterns: root + sub-elements, styleProps, tokens, wrapping
679
+ - **[Configuration](configuration.md)** — Tokens, recipes, custom units, style handlers, TypeScript extensions
680
+ - **[Style Properties](styles.md)** — Complete reference for all enhanced style properties
681
+ - **[Build-Time Extraction (`tastyStatic`)](tasty-static.md)** — Static styling with the Babel plugin
682
+ - **[Server-Side Rendering](ssr.md)** — SSR setup for Next.js, Astro, and generic frameworks
683
+ - **[Debug Utilities](debug.md)** — Inspect injected CSS, cache state, and active styles at runtime