@tenphi/tasty 0.0.0-snapshot.fafcbfe → 0.0.0-snapshot.fdb16ea

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 (309) hide show
  1. package/README.md +397 -185
  2. package/dist/async-storage-DKK-wTD4.js +44 -0
  3. package/dist/async-storage-DKK-wTD4.js.map +1 -0
  4. package/dist/babel-CMYf4JZ2.d.ts +83 -0
  5. package/dist/collector-Ctn5s74s.d.ts +145 -0
  6. package/dist/collector-DMSR6ANK.js +304 -0
  7. package/dist/collector-DMSR6ANK.js.map +1 -0
  8. package/dist/config-B23gZrfP.js +12317 -0
  9. package/dist/config-B23gZrfP.js.map +1 -0
  10. package/dist/config-CZx8_DCu.d.ts +1296 -0
  11. package/dist/context-CA8YKeMn.js +24 -0
  12. package/dist/context-CA8YKeMn.js.map +1 -0
  13. package/dist/core/index.d.ts +5 -33
  14. package/dist/core/index.js +6 -26
  15. package/dist/core-B1tyn69d.js +1573 -0
  16. package/dist/core-B1tyn69d.js.map +1 -0
  17. package/dist/css-writer-CvAxkR6S.js +390 -0
  18. package/dist/css-writer-CvAxkR6S.js.map +1 -0
  19. package/dist/format-global-rules-DklyaXv-.js +22 -0
  20. package/dist/format-global-rules-DklyaXv-.js.map +1 -0
  21. package/dist/format-rules-BCTWVvcS.js +130 -0
  22. package/dist/format-rules-BCTWVvcS.js.map +1 -0
  23. package/dist/hydrate-BCPvkJj3.js +37 -0
  24. package/dist/hydrate-BCPvkJj3.js.map +1 -0
  25. package/dist/index-B3o0aCYV.d.ts +1538 -0
  26. package/dist/index-CsvB0EXZ.d.ts +1909 -0
  27. package/dist/index.d.ts +5 -40
  28. package/dist/index.js +921 -32
  29. package/dist/index.js.map +1 -0
  30. package/dist/keyframes-Dxdx3KOo.js +493 -0
  31. package/dist/keyframes-Dxdx3KOo.js.map +1 -0
  32. package/dist/{utils/merge-styles.js → merge-styles-C-uwpoNW.js} +4 -6
  33. package/dist/merge-styles-C-uwpoNW.js.map +1 -0
  34. package/dist/{utils/merge-styles.d.ts → merge-styles-DJTSfz_M.d.ts} +3 -3
  35. package/dist/{utils/resolve-recipes.js → resolve-recipes-BuOvSEa2.js} +5 -8
  36. package/dist/resolve-recipes-BuOvSEa2.js.map +1 -0
  37. package/dist/ssr/astro-client.d.ts +1 -0
  38. package/dist/ssr/astro-client.js +19 -0
  39. package/dist/ssr/astro-client.js.map +1 -0
  40. package/dist/ssr/astro-middleware-static.d.ts +16 -0
  41. package/dist/ssr/astro-middleware-static.js +18 -0
  42. package/dist/ssr/astro-middleware-static.js.map +1 -0
  43. package/dist/ssr/astro-middleware.d.ts +17 -0
  44. package/dist/ssr/astro-middleware.js +19 -0
  45. package/dist/ssr/astro-middleware.js.map +1 -0
  46. package/dist/ssr/astro.d.ts +93 -0
  47. package/dist/ssr/astro.js +157 -0
  48. package/dist/ssr/astro.js.map +1 -0
  49. package/dist/ssr/index.d.ts +37 -0
  50. package/dist/ssr/index.js +10 -0
  51. package/dist/ssr/index.js.map +1 -0
  52. package/dist/ssr/next.d.ts +45 -0
  53. package/dist/ssr/next.js +74 -0
  54. package/dist/ssr/next.js.map +1 -0
  55. package/dist/static/index.d.ts +91 -5
  56. package/dist/static/index.js +49 -4
  57. package/dist/static/index.js.map +1 -0
  58. package/dist/static/inject.d.ts +5 -0
  59. package/dist/static/inject.js +17 -0
  60. package/dist/static/inject.js.map +1 -0
  61. package/dist/zero/babel.d.ts +2 -108
  62. package/dist/zero/babel.js +235 -39
  63. package/dist/zero/babel.js.map +1 -1
  64. package/dist/zero/index.d.ts +81 -3
  65. package/dist/zero/index.js +2 -4
  66. package/dist/zero/next.d.ts +56 -30
  67. package/dist/zero/next.js +106 -41
  68. package/dist/zero/next.js.map +1 -1
  69. package/docs/README.md +39 -0
  70. package/docs/adoption.md +323 -0
  71. package/docs/ai-agents.md +224 -0
  72. package/docs/comparison.md +448 -0
  73. package/docs/configuration.md +823 -0
  74. package/docs/debug.md +322 -0
  75. package/docs/design-system.md +455 -0
  76. package/docs/dsl.md +1021 -0
  77. package/docs/getting-started.md +238 -0
  78. package/docs/injector.md +614 -0
  79. package/docs/methodology.md +648 -0
  80. package/docs/migration-v3.md +285 -0
  81. package/docs/pipeline.md +741 -0
  82. package/docs/plugins.md +353 -0
  83. package/docs/react-api.md +681 -0
  84. package/docs/runtime-benchmarks.md +216 -0
  85. package/docs/ssr.md +451 -0
  86. package/docs/styles.md +645 -0
  87. package/docs/tasty-static.md +582 -0
  88. package/package.json +149 -40
  89. package/tasty.config.ts +6 -0
  90. package/dist/_virtual/_rolldown/runtime.js +0 -8
  91. package/dist/chunks/cacheKey.js +0 -70
  92. package/dist/chunks/cacheKey.js.map +0 -1
  93. package/dist/chunks/definitions.d.ts +0 -37
  94. package/dist/chunks/definitions.js +0 -260
  95. package/dist/chunks/definitions.js.map +0 -1
  96. package/dist/chunks/renderChunk.js +0 -61
  97. package/dist/chunks/renderChunk.js.map +0 -1
  98. package/dist/config.d.ts +0 -280
  99. package/dist/config.js +0 -403
  100. package/dist/config.js.map +0 -1
  101. package/dist/debug.d.ts +0 -204
  102. package/dist/debug.js +0 -733
  103. package/dist/debug.js.map +0 -1
  104. package/dist/hooks/useGlobalStyles.d.ts +0 -27
  105. package/dist/hooks/useGlobalStyles.js +0 -56
  106. package/dist/hooks/useGlobalStyles.js.map +0 -1
  107. package/dist/hooks/useKeyframes.d.ts +0 -56
  108. package/dist/hooks/useKeyframes.js +0 -54
  109. package/dist/hooks/useKeyframes.js.map +0 -1
  110. package/dist/hooks/useProperty.d.ts +0 -79
  111. package/dist/hooks/useProperty.js +0 -91
  112. package/dist/hooks/useProperty.js.map +0 -1
  113. package/dist/hooks/useRawCSS.d.ts +0 -53
  114. package/dist/hooks/useRawCSS.js +0 -28
  115. package/dist/hooks/useRawCSS.js.map +0 -1
  116. package/dist/hooks/useStyles.d.ts +0 -40
  117. package/dist/hooks/useStyles.js +0 -169
  118. package/dist/hooks/useStyles.js.map +0 -1
  119. package/dist/injector/index.d.ts +0 -157
  120. package/dist/injector/index.js +0 -154
  121. package/dist/injector/index.js.map +0 -1
  122. package/dist/injector/injector.d.ts +0 -139
  123. package/dist/injector/injector.js +0 -404
  124. package/dist/injector/injector.js.map +0 -1
  125. package/dist/injector/sheet-manager.d.ts +0 -127
  126. package/dist/injector/sheet-manager.js +0 -714
  127. package/dist/injector/sheet-manager.js.map +0 -1
  128. package/dist/injector/types.d.ts +0 -135
  129. package/dist/keyframes/index.js +0 -206
  130. package/dist/keyframes/index.js.map +0 -1
  131. package/dist/parser/classify.js +0 -319
  132. package/dist/parser/classify.js.map +0 -1
  133. package/dist/parser/const.js +0 -33
  134. package/dist/parser/const.js.map +0 -1
  135. package/dist/parser/lru.js +0 -109
  136. package/dist/parser/lru.js.map +0 -1
  137. package/dist/parser/parser.d.ts +0 -25
  138. package/dist/parser/parser.js +0 -116
  139. package/dist/parser/parser.js.map +0 -1
  140. package/dist/parser/tokenizer.js +0 -69
  141. package/dist/parser/tokenizer.js.map +0 -1
  142. package/dist/parser/types.d.ts +0 -51
  143. package/dist/parser/types.js +0 -46
  144. package/dist/parser/types.js.map +0 -1
  145. package/dist/pipeline/conditions.d.ts +0 -134
  146. package/dist/pipeline/conditions.js +0 -400
  147. package/dist/pipeline/conditions.js.map +0 -1
  148. package/dist/pipeline/exclusive.js +0 -231
  149. package/dist/pipeline/exclusive.js.map +0 -1
  150. package/dist/pipeline/index.d.ts +0 -53
  151. package/dist/pipeline/index.js +0 -641
  152. package/dist/pipeline/index.js.map +0 -1
  153. package/dist/pipeline/materialize.js +0 -908
  154. package/dist/pipeline/materialize.js.map +0 -1
  155. package/dist/pipeline/parseStateKey.d.ts +0 -15
  156. package/dist/pipeline/parseStateKey.js +0 -430
  157. package/dist/pipeline/parseStateKey.js.map +0 -1
  158. package/dist/pipeline/simplify.js +0 -557
  159. package/dist/pipeline/simplify.js.map +0 -1
  160. package/dist/plugins/okhsl-plugin.d.ts +0 -35
  161. package/dist/plugins/okhsl-plugin.js +0 -371
  162. package/dist/plugins/okhsl-plugin.js.map +0 -1
  163. package/dist/plugins/types.d.ts +0 -69
  164. package/dist/properties/index.js +0 -158
  165. package/dist/properties/index.js.map +0 -1
  166. package/dist/states/index.d.ts +0 -49
  167. package/dist/states/index.js +0 -416
  168. package/dist/states/index.js.map +0 -1
  169. package/dist/static/tastyStatic.d.ts +0 -46
  170. package/dist/static/tastyStatic.js +0 -31
  171. package/dist/static/tastyStatic.js.map +0 -1
  172. package/dist/static/types.d.ts +0 -49
  173. package/dist/static/types.js +0 -24
  174. package/dist/static/types.js.map +0 -1
  175. package/dist/styles/align.d.ts +0 -15
  176. package/dist/styles/align.js +0 -14
  177. package/dist/styles/align.js.map +0 -1
  178. package/dist/styles/border.d.ts +0 -25
  179. package/dist/styles/border.js +0 -114
  180. package/dist/styles/border.js.map +0 -1
  181. package/dist/styles/color.d.ts +0 -14
  182. package/dist/styles/color.js +0 -23
  183. package/dist/styles/color.js.map +0 -1
  184. package/dist/styles/createStyle.js +0 -77
  185. package/dist/styles/createStyle.js.map +0 -1
  186. package/dist/styles/dimension.js +0 -97
  187. package/dist/styles/dimension.js.map +0 -1
  188. package/dist/styles/display.d.ts +0 -37
  189. package/dist/styles/display.js +0 -67
  190. package/dist/styles/display.js.map +0 -1
  191. package/dist/styles/fade.d.ts +0 -15
  192. package/dist/styles/fade.js +0 -58
  193. package/dist/styles/fade.js.map +0 -1
  194. package/dist/styles/fill.d.ts +0 -44
  195. package/dist/styles/fill.js +0 -51
  196. package/dist/styles/fill.js.map +0 -1
  197. package/dist/styles/flow.d.ts +0 -16
  198. package/dist/styles/flow.js +0 -12
  199. package/dist/styles/flow.js.map +0 -1
  200. package/dist/styles/gap.d.ts +0 -31
  201. package/dist/styles/gap.js +0 -37
  202. package/dist/styles/gap.js.map +0 -1
  203. package/dist/styles/height.d.ts +0 -17
  204. package/dist/styles/height.js +0 -20
  205. package/dist/styles/height.js.map +0 -1
  206. package/dist/styles/index.d.ts +0 -2
  207. package/dist/styles/index.js +0 -9
  208. package/dist/styles/index.js.map +0 -1
  209. package/dist/styles/inset.d.ts +0 -52
  210. package/dist/styles/inset.js +0 -150
  211. package/dist/styles/inset.js.map +0 -1
  212. package/dist/styles/justify.d.ts +0 -15
  213. package/dist/styles/justify.js +0 -14
  214. package/dist/styles/justify.js.map +0 -1
  215. package/dist/styles/list.d.ts +0 -16
  216. package/dist/styles/list.js +0 -98
  217. package/dist/styles/list.js.map +0 -1
  218. package/dist/styles/margin.d.ts +0 -24
  219. package/dist/styles/margin.js +0 -104
  220. package/dist/styles/margin.js.map +0 -1
  221. package/dist/styles/outline.d.ts +0 -29
  222. package/dist/styles/outline.js +0 -65
  223. package/dist/styles/outline.js.map +0 -1
  224. package/dist/styles/padding.d.ts +0 -24
  225. package/dist/styles/padding.js +0 -104
  226. package/dist/styles/padding.js.map +0 -1
  227. package/dist/styles/predefined.d.ts +0 -73
  228. package/dist/styles/predefined.js +0 -241
  229. package/dist/styles/predefined.js.map +0 -1
  230. package/dist/styles/preset.d.ts +0 -47
  231. package/dist/styles/preset.js +0 -126
  232. package/dist/styles/preset.js.map +0 -1
  233. package/dist/styles/radius.d.ts +0 -14
  234. package/dist/styles/radius.js +0 -51
  235. package/dist/styles/radius.js.map +0 -1
  236. package/dist/styles/scrollbar.d.ts +0 -21
  237. package/dist/styles/scrollbar.js +0 -105
  238. package/dist/styles/scrollbar.js.map +0 -1
  239. package/dist/styles/shadow.d.ts +0 -14
  240. package/dist/styles/shadow.js +0 -24
  241. package/dist/styles/shadow.js.map +0 -1
  242. package/dist/styles/styledScrollbar.d.ts +0 -47
  243. package/dist/styles/styledScrollbar.js +0 -38
  244. package/dist/styles/styledScrollbar.js.map +0 -1
  245. package/dist/styles/transition.d.ts +0 -14
  246. package/dist/styles/transition.js +0 -158
  247. package/dist/styles/transition.js.map +0 -1
  248. package/dist/styles/types.d.ts +0 -498
  249. package/dist/styles/width.d.ts +0 -17
  250. package/dist/styles/width.js +0 -20
  251. package/dist/styles/width.js.map +0 -1
  252. package/dist/tasty.d.ts +0 -982
  253. package/dist/tasty.js +0 -191
  254. package/dist/tasty.js.map +0 -1
  255. package/dist/tokens/typography.d.ts +0 -19
  256. package/dist/tokens/typography.js +0 -237
  257. package/dist/tokens/typography.js.map +0 -1
  258. package/dist/types.d.ts +0 -184
  259. package/dist/utils/cache-wrapper.js +0 -26
  260. package/dist/utils/cache-wrapper.js.map +0 -1
  261. package/dist/utils/case-converter.js +0 -8
  262. package/dist/utils/case-converter.js.map +0 -1
  263. package/dist/utils/colors.d.ts +0 -5
  264. package/dist/utils/colors.js +0 -9
  265. package/dist/utils/colors.js.map +0 -1
  266. package/dist/utils/css-types.d.ts +0 -7
  267. package/dist/utils/dotize.d.ts +0 -26
  268. package/dist/utils/dotize.js +0 -122
  269. package/dist/utils/dotize.js.map +0 -1
  270. package/dist/utils/filter-base-props.d.ts +0 -15
  271. package/dist/utils/filter-base-props.js +0 -45
  272. package/dist/utils/filter-base-props.js.map +0 -1
  273. package/dist/utils/get-display-name.d.ts +0 -7
  274. package/dist/utils/get-display-name.js +0 -10
  275. package/dist/utils/get-display-name.js.map +0 -1
  276. package/dist/utils/hsl-to-rgb.js +0 -38
  277. package/dist/utils/hsl-to-rgb.js.map +0 -1
  278. package/dist/utils/is-dev-env.js +0 -19
  279. package/dist/utils/is-dev-env.js.map +0 -1
  280. package/dist/utils/is-valid-element-type.js +0 -15
  281. package/dist/utils/is-valid-element-type.js.map +0 -1
  282. package/dist/utils/merge-styles.js.map +0 -1
  283. package/dist/utils/mod-attrs.d.ts +0 -8
  284. package/dist/utils/mod-attrs.js +0 -21
  285. package/dist/utils/mod-attrs.js.map +0 -1
  286. package/dist/utils/okhsl-to-rgb.js +0 -296
  287. package/dist/utils/okhsl-to-rgb.js.map +0 -1
  288. package/dist/utils/process-tokens.d.ts +0 -31
  289. package/dist/utils/process-tokens.js +0 -171
  290. package/dist/utils/process-tokens.js.map +0 -1
  291. package/dist/utils/resolve-recipes.d.ts +0 -17
  292. package/dist/utils/resolve-recipes.js.map +0 -1
  293. package/dist/utils/string.js +0 -8
  294. package/dist/utils/string.js.map +0 -1
  295. package/dist/utils/styles.d.ts +0 -183
  296. package/dist/utils/styles.js +0 -585
  297. package/dist/utils/styles.js.map +0 -1
  298. package/dist/utils/typography.d.ts +0 -36
  299. package/dist/utils/typography.js +0 -53
  300. package/dist/utils/typography.js.map +0 -1
  301. package/dist/utils/warnings.d.ts +0 -16
  302. package/dist/utils/warnings.js +0 -16
  303. package/dist/utils/warnings.js.map +0 -1
  304. package/dist/zero/css-writer.d.ts +0 -45
  305. package/dist/zero/css-writer.js +0 -74
  306. package/dist/zero/css-writer.js.map +0 -1
  307. package/dist/zero/extractor.d.ts +0 -24
  308. package/dist/zero/extractor.js +0 -150
  309. package/dist/zero/extractor.js.map +0 -1
@@ -0,0 +1,448 @@
1
+ # Comparison
2
+
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
+
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
+
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
+
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
+
11
+ Most styling tools focus on one of these layers:
12
+
13
+ - direct app styling
14
+ - component-level styling
15
+ - typed CSS authoring
16
+ - utility composition
17
+ - atomic CSS generation
18
+
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
+
21
+ That is why syntax-level comparisons are often shallow. The more meaningful comparison is about:
22
+
23
+ - **which layer** a tool is designed for
24
+ - **who owns the authoring language**
25
+ - **how state conflicts are resolved**
26
+ - **what kind of predictability** the tool actually guarantees
27
+
28
+ ---
29
+
30
+ ## High-level positioning
31
+
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
+
41
+ ---
42
+
43
+ ## What makes Tasty different
44
+
45
+ Tasty is built around a stronger goal than generic "predictable styling."
46
+
47
+ In many systems, predictability means:
48
+
49
+ - the compiler controls ordering
50
+ - class names do not accidentally collide
51
+ - specificity is reduced or normalized
52
+ - atomic rules compose in a stable way
53
+
54
+ Those are useful guarantees, but they are not the same as Tasty's main idea.
55
+
56
+ Tasty focuses on **stateful style resolution**. Instead of relying on ordinary cascade competition between matching rules, it compiles style logic into **mutually exclusive selectors**. For a given property and state combination, the system ensures that exactly one branch is eligible to win.
57
+
58
+ This is especially relevant for components with intersecting states such as:
59
+
60
+ - hover
61
+ - focus
62
+ - pressed
63
+ - disabled
64
+ - selected
65
+ - theme variants
66
+ - responsive conditions
67
+ - parent or root-driven conditions
68
+
69
+ Here is a minimal example. Two CSS rules for a button's background — one for `:hover`, one for `[disabled]`:
70
+
71
+ ```css
72
+ .btn:hover {
73
+ background: dodgerblue;
74
+ }
75
+ .btn[disabled] {
76
+ background: gray;
77
+ }
78
+ ```
79
+
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
+
82
+ In Tasty, the same intent is declared as a state map:
83
+
84
+ ```tsx
85
+ fill: {
86
+ '': '#primary',
87
+ ':hover': '#primary-hover',
88
+ '[disabled]': '#surface',
89
+ }
90
+ ```
91
+
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.
93
+
94
+ That makes Tasty less of a "better way to write CSS objects" and more of a **state-aware style compiler for design systems**.
95
+
96
+ Beyond state resolution, Tasty also provides several structural capabilities that reinforce the design-system layer:
97
+
98
+ - **CSS properties as typed React props** — `styleProps` lets a component expose selected style properties as normal props (`<Button placeSelf="end">`), including state maps for responsive values. This keeps layout and composition controls inside a governed component API instead of pushing teams back to ad hoc styling escapes.
99
+ - **Sub-element styling** — Compound components declare inner parts via capitalized keys in `styles` and `data-element` attributes. States, tokens, and recipes apply across the entire element tree from a single definition. See [React API — Sub-element Styling](react-api.md#sub-element-styling).
100
+ - **Auto-inferred `@property`** — When a custom property is assigned a concrete value, Tasty infers the CSS `@property` syntax and registers it automatically, enabling smooth transitions on custom properties without manual declarations.
101
+ - **AI-friendly style definitions** — Style definitions are declarative, self-contained, and structurally consistent. AI tools can read, refactor, and generate Tasty styles as confidently as a human — no hidden cascade logic or implicit ordering to second-guess.
102
+ - **Companion ecosystem** — An [ESLint plugin](https://github.com/tenphi/eslint-plugin-tasty) with 27 lint rules, a [VS Code extension](https://github.com/tenphi/tasty-vscode-extension) for syntax highlighting, and [Glaze](https://github.com/tenphi/glaze) for OKHSL color theme generation with automatic WCAG contrast solving.
103
+
104
+ ---
105
+
106
+ ## Comparison by system
107
+
108
+ ### Tasty vs Tailwind CSS
109
+
110
+ Tailwind is centered on **direct authoring in markup**.
111
+
112
+ Its strength is speed: developers compose utilities directly where they use them, with responsive and state modifiers layered on top. This works extremely well for app teams that want a shared utility vocabulary with minimal ceremony.
113
+
114
+ Tasty solves a different problem.
115
+
116
+ Tasty is more appropriate when styling should be exposed through a **design-system-owned API** rather than through raw utility composition. You can start using Tasty immediately with its built-in DSL, but it becomes especially compelling when a team wants to define:
117
+
118
+ - approved style props
119
+ - semantic tokens
120
+ - custom units
121
+ - recipes
122
+ - state semantics
123
+ - sub-element rules
124
+ - constrained component-facing styling APIs
125
+
126
+ So this is not mainly a comparison of syntax. It is a comparison of **governance models**:
127
+
128
+ - Tailwind: developers author directly with framework vocabulary
129
+ - Tasty: design-system authors define the vocabulary product teams consume
130
+
131
+ Tailwind is a stronger fit for fast product styling with framework-owned vocabulary. Tasty is a stronger fit when styling should be exposed through a design-system-owned API and state resolution needs to stay deterministic as the component model grows.
132
+
133
+ To make this concrete, consider a button with `hover`, `disabled`, and `theme=danger` states.
134
+
135
+ **Plain CSS** — you need a selector for every intersection, and equal-specificity rules depend on source order:
136
+
137
+ ```css
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
+ }
154
+
155
+ /* theme=danger overrides — must repeat disabled/hover/active */
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
+ }
168
+
169
+ /* Bug: .btn:hover and .btn[disabled] have the same specificity.
170
+ A hovered disabled button gets :hover styles — unless source order saves you. */
171
+ ```
172
+
173
+ Every new state doubles the selector count. Miss one intersection and you ship a visual bug.
174
+
175
+ **Tailwind** — state intersections move into conditional className logic:
176
+
177
+ ```tsx
178
+ <button className={cn(
179
+ 'bg-primary text-white cursor-pointer',
180
+ 'hover:bg-primary-hover active:bg-primary-pressed',
181
+ 'disabled:bg-surface disabled:text-text-40 disabled:cursor-not-allowed',
182
+ theme === 'danger' && 'bg-danger hover:bg-danger-hover active:bg-danger-pressed',
183
+ theme === 'danger' && disabled && '!bg-surface',
184
+ )}>
185
+ ```
186
+
187
+ The `theme` branch is runtime JS, not CSS. Intersections like `disabled + hover` need manual `!important` or extra utilities to override correctly.
188
+
189
+ **Tasty** — each property declares all its states in one map. The engine generates mutually exclusive selectors:
190
+
191
+ ```tsx
192
+ const Button = tasty({
193
+ as: 'button',
194
+ styles: {
195
+ fill: {
196
+ '': '#primary',
197
+ ':hover': '#primary-hover',
198
+ ':active': '#primary-pressed',
199
+ disabled: '#surface',
200
+ 'theme=danger': '#danger',
201
+ 'theme=danger & :hover': '#danger-hover',
202
+ 'theme=danger & :active': '#danger-pressed',
203
+ },
204
+ color: {
205
+ '': '#on-primary',
206
+ disabled: '#text.40',
207
+ },
208
+ cursor: {
209
+ '': 'pointer',
210
+ disabled: 'not-allowed',
211
+ },
212
+ },
213
+ });
214
+ ```
215
+
216
+ `disabled` always wins over `:hover` because Tasty emits negation selectors — no source-order dependence, no manual intersection management, no `!important`.
217
+
218
+ ---
219
+
220
+ ### Tasty vs Panda CSS
221
+
222
+ Panda is one of the closest comparisons.
223
+
224
+ Like Tasty, Panda sits closer to the design-system layer than many other tools. It supports typed style authoring, recipes, generated primitives, and a DS-friendly workflow. It is much more than a basic styling helper.
225
+
226
+ The difference is where each system puts its core idea.
227
+
228
+ Panda is centered on **typed atomic generation** and static analysis. It gives teams a structured, modern, design-system-friendly engine with a strong build-time story.
229
+
230
+ Tasty is more centered on:
231
+
232
+ - a custom DSL
233
+ - state mapping
234
+ - mutually exclusive resolution
235
+ - defining a team-specific styling language
236
+
237
+ So while both can support serious design-system work, they do not optimize for exactly the same thing:
238
+
239
+ - **Panda** is closer to a typed styling engine with strong DS ergonomics
240
+ - **Tasty** is closer to a design-system style compiler with explicit state semantics
241
+
242
+ If a team mostly wants typed primitives, recipes, and extracted CSS, Panda may feel more straightforward.
243
+
244
+ If a team wants to define a more opinionated styling language with stronger control over state logic and rule exclusivity, Tasty has a more specialized angle.
245
+
246
+ ---
247
+
248
+ ### Tasty vs vanilla-extract
249
+
250
+ vanilla-extract is a lower-level foundation.
251
+
252
+ It gives teams a zero-runtime TypeScript-native way to generate CSS, plus strong theming primitives and the ability to build architecture on top. It is excellent when a team wants maximum control over structure while staying close to normal CSS semantics.
253
+
254
+ That last point matters.
255
+
256
+ With vanilla-extract, styles are still fundamentally governed by **standard CSS behavior**. Ordering, layering, and media-query structure still matter in the usual CSS sense. That is not a flaw; it is simply a different abstraction level.
257
+
258
+ Tasty is more opinionated.
259
+
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.
261
+
262
+ This also makes Tasty's delivery model notable:
263
+
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.
266
+
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.
268
+
269
+ So the tradeoff is roughly:
270
+
271
+ - **vanilla-extract**: lower-level, static, explicit, architecture left to the team
272
+ - **Tasty**: more opinionated, more state-aware, more language-defining
273
+
274
+ ---
275
+
276
+ ### Tasty vs StyleX
277
+
278
+ This comparison needs extra precision, because both systems care about predictability, but not in the same way.
279
+
280
+ StyleX is a compiler-based atomic system with strong guarantees around consistency and composition. Its model is designed to avoid many classic CSS pitfalls such as accidental rule collisions and specificity-driven unpredictability.
281
+
282
+ That is real value.
283
+
284
+ But it is still a different kind of guarantee from Tasty's.
285
+
286
+ StyleX predictability comes from:
287
+
288
+ - atomic decomposition
289
+ - compiler control
290
+ - constrained composition
291
+ - normalized style behavior
292
+
293
+ Tasty's differentiator is stronger in a specific area:
294
+
295
+ - **stateful per-property resolution**
296
+ - **mutually exclusive selectors**
297
+ - **conflict avoidance by construction**, not only by atomic normalization
298
+
299
+ So "collision-free atomic CSS" should not be treated as equivalent to Tasty's approach.
300
+
301
+ A better framing is:
302
+
303
+ - **StyleX** provides compiler-controlled atomic predictability
304
+ - **Tasty** provides mutually exclusive selector resolution for stateful component styling
305
+
306
+ That makes Tasty especially interesting when the hardest problem is not just style composition, but **complex intersecting component states**.
307
+
308
+ ---
309
+
310
+ ### Tasty vs Stitches (deprecated) / Emotion
311
+
312
+ Stitches and Emotion are component-first styling systems. (Note: Stitches has been archived and is no longer maintained. It is included here because it remains widely referenced in comparisons.)
313
+
314
+ They optimize for developer experience, flexible composition, reusable styled primitives, and ergonomic component authoring. For many teams, that is exactly the right abstraction level.
315
+
316
+ Tasty targets a different question.
317
+
318
+ It is less focused on "how do I style this component ergonomically right now?" and more focused on:
319
+
320
+ - what styling language should this design system expose?
321
+ - how should states be modeled?
322
+ - what should be allowed or constrained?
323
+ - how do we keep style behavior deterministic as the system grows?
324
+
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**.
326
+
327
+ That makes it narrower in audience, but deeper in architectural ambition.
328
+
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.
330
+
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.
332
+
333
+ ---
334
+
335
+ ## Where styles are computed
336
+
337
+ Tasty is not limited to one execution model.
338
+
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.
340
+
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.
342
+
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.
344
+
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.
346
+
347
+ This gives Tasty two zero-runtime paths:
348
+
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
351
+
352
+ ---
353
+
354
+ ## Comparison by abstraction level
355
+
356
+ Another useful way to think about the ecosystem is by abstraction level.
357
+
358
+ ### Direct styling tools
359
+
360
+ These are optimized for styling product code directly.
361
+
362
+ Examples:
363
+
364
+ - Tailwind CSS
365
+ - Emotion (requires `'use client'` — not RSC-compatible)
366
+ - Stitches (deprecated, requires `'use client'`)
367
+
368
+ ### Typed styling engines
369
+
370
+ These are optimized for generating CSS with stronger structure and tooling.
371
+
372
+ Examples:
373
+
374
+ - Panda CSS
375
+ - vanilla-extract
376
+ - StyleX
377
+
378
+ ### Design-system language engines
379
+
380
+ These are optimized for helping a team define its own styling grammar and semantics.
381
+
382
+ Tasty belongs most naturally here.
383
+
384
+ That is why generic "feature matrix" comparisons often miss the point. Tasty is not only trying to style elements. It is trying to help define **how a design system talks about styling**.
385
+
386
+ ---
387
+
388
+ ## When Tasty is a strong fit
389
+
390
+ Tasty makes the most sense when:
391
+
392
+ - a real design system exists or is being built
393
+ - a shared component API is emerging even if the design system is still lightweight
394
+ - styling should be governed through a central platform team
395
+ - component state logic is complex
396
+ - the team wants a design-system-owned styling language instead of raw CSS-shaped authoring
397
+ - tokens, recipes, and sub-elements should be first-class
398
+ - deterministic state resolution matters more than minimum abstraction overhead
399
+ - the styling engine may need to work as either a runtime tool or a build-time compiler
400
+
401
+ ---
402
+
403
+ ## When another tool may be a better fit
404
+
405
+ A different tool may be more appropriate when:
406
+
407
+ - the main goal is styling app code directly with minimal setup and without defining shared styling conventions
408
+ - the team prefers a shared framework vocabulary over a custom design-system language
409
+ - the complexity of intersecting states is low
410
+ - low ceremony matters more than central governance
411
+ - the team wants static CSS primitives without a more opinionated state model
412
+ - component-level DX is the primary optimization target
413
+
414
+ ---
415
+
416
+ ## Summary
417
+
418
+ Tasty is not best compared as "another CSS framework."
419
+
420
+ Its more meaningful comparison point is this:
421
+
422
+ > Tasty is a styling engine for building a design-system-defined authoring language, with a particular focus on explicit state semantics and mutually exclusive selector resolution.
423
+
424
+ That puts it in a different category from:
425
+
426
+ - utility-first tools that optimize for direct authoring
427
+ - component-first CSS-in-JS libraries that optimize for DX
428
+ - typed static CSS systems that expose lower-level primitives
429
+ - atomic compilers that focus on normalized composition
430
+
431
+ Those systems are all useful, but they optimize for different layers.
432
+
433
+ Tasty is most compelling when the problem is not just "how do we write styles," but:
434
+
435
+ > "How do we define a scalable, deterministic styling model for the design system itself?"
436
+
437
+ ---
438
+
439
+ ## Learn more
440
+
441
+ - [README](../README.md) — overview, quick start, and feature highlights
442
+ - [Style DSL](dsl.md) — state maps, tokens, units, extending semantics, keyframes, @property
443
+ - [React API](react-api.md) — `tasty()` factory, component props, variants, sub-elements, style functions
444
+ - [Style Properties](styles.md) — complete reference for all enhanced style properties
445
+ - [Configuration](configuration.md) — tokens, recipes, custom units, style handlers, and TypeScript extensions
446
+ - [Build-Time Extraction (`tastyStatic`)](tasty-static.md) — static styling with the Babel plugin
447
+ - [Adoption Guide](adoption.md) — where Tasty sits in the stack, incremental adoption, and what changes for product engineers
448
+ - [Server-Side Rendering](ssr.md) — SSR setup for Next.js, Astro, and generic frameworks