@tenphi/tasty 0.0.0-snapshot.056b911

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 (332) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +635 -0
  3. package/dist/_virtual/_rolldown/runtime.js +7 -0
  4. package/dist/chunks/cacheKey.d.ts +1 -0
  5. package/dist/chunks/cacheKey.js +77 -0
  6. package/dist/chunks/cacheKey.js.map +1 -0
  7. package/dist/chunks/definitions.d.ts +37 -0
  8. package/dist/chunks/definitions.js +258 -0
  9. package/dist/chunks/definitions.js.map +1 -0
  10. package/dist/chunks/index.d.ts +1 -0
  11. package/dist/chunks/renderChunk.d.ts +1 -0
  12. package/dist/chunks/renderChunk.js +59 -0
  13. package/dist/chunks/renderChunk.js.map +1 -0
  14. package/dist/compute-styles.d.ts +31 -0
  15. package/dist/compute-styles.js +335 -0
  16. package/dist/compute-styles.js.map +1 -0
  17. package/dist/config.d.ts +409 -0
  18. package/dist/config.js +584 -0
  19. package/dist/config.js.map +1 -0
  20. package/dist/core/index.d.ts +34 -0
  21. package/dist/core/index.js +27 -0
  22. package/dist/counter-style/index.js +51 -0
  23. package/dist/counter-style/index.js.map +1 -0
  24. package/dist/debug.d.ts +89 -0
  25. package/dist/debug.js +453 -0
  26. package/dist/debug.js.map +1 -0
  27. package/dist/font-face/index.js +63 -0
  28. package/dist/font-face/index.js.map +1 -0
  29. package/dist/hooks/index.d.ts +7 -0
  30. package/dist/hooks/useCounterStyle.d.ts +36 -0
  31. package/dist/hooks/useCounterStyle.js +64 -0
  32. package/dist/hooks/useCounterStyle.js.map +1 -0
  33. package/dist/hooks/useFontFace.d.ts +45 -0
  34. package/dist/hooks/useFontFace.js +66 -0
  35. package/dist/hooks/useFontFace.js.map +1 -0
  36. package/dist/hooks/useGlobalStyles.d.ts +46 -0
  37. package/dist/hooks/useGlobalStyles.js +88 -0
  38. package/dist/hooks/useGlobalStyles.js.map +1 -0
  39. package/dist/hooks/useKeyframes.d.ts +58 -0
  40. package/dist/hooks/useKeyframes.js +54 -0
  41. package/dist/hooks/useKeyframes.js.map +1 -0
  42. package/dist/hooks/useProperty.d.ts +81 -0
  43. package/dist/hooks/useProperty.js +96 -0
  44. package/dist/hooks/useProperty.js.map +1 -0
  45. package/dist/hooks/useRawCSS.d.ts +22 -0
  46. package/dist/hooks/useRawCSS.js +103 -0
  47. package/dist/hooks/useRawCSS.js.map +1 -0
  48. package/dist/hooks/useStyles.d.ts +40 -0
  49. package/dist/hooks/useStyles.js +31 -0
  50. package/dist/hooks/useStyles.js.map +1 -0
  51. package/dist/index.d.ts +51 -0
  52. package/dist/index.js +36 -0
  53. package/dist/injector/index.d.ts +182 -0
  54. package/dist/injector/index.js +185 -0
  55. package/dist/injector/index.js.map +1 -0
  56. package/dist/injector/injector.d.ts +193 -0
  57. package/dist/injector/injector.js +564 -0
  58. package/dist/injector/injector.js.map +1 -0
  59. package/dist/injector/sheet-manager.d.ts +132 -0
  60. package/dist/injector/sheet-manager.js +698 -0
  61. package/dist/injector/sheet-manager.js.map +1 -0
  62. package/dist/injector/types.d.ts +228 -0
  63. package/dist/keyframes/index.js +206 -0
  64. package/dist/keyframes/index.js.map +1 -0
  65. package/dist/parser/classify.js +319 -0
  66. package/dist/parser/classify.js.map +1 -0
  67. package/dist/parser/const.js +60 -0
  68. package/dist/parser/const.js.map +1 -0
  69. package/dist/parser/lru.js +109 -0
  70. package/dist/parser/lru.js.map +1 -0
  71. package/dist/parser/parser.d.ts +25 -0
  72. package/dist/parser/parser.js +115 -0
  73. package/dist/parser/parser.js.map +1 -0
  74. package/dist/parser/tokenizer.js +69 -0
  75. package/dist/parser/tokenizer.js.map +1 -0
  76. package/dist/parser/types.d.ts +51 -0
  77. package/dist/parser/types.js +46 -0
  78. package/dist/parser/types.js.map +1 -0
  79. package/dist/pipeline/conditions.d.ts +134 -0
  80. package/dist/pipeline/conditions.js +406 -0
  81. package/dist/pipeline/conditions.js.map +1 -0
  82. package/dist/pipeline/exclusive.js +230 -0
  83. package/dist/pipeline/exclusive.js.map +1 -0
  84. package/dist/pipeline/index.d.ts +55 -0
  85. package/dist/pipeline/index.js +708 -0
  86. package/dist/pipeline/index.js.map +1 -0
  87. package/dist/pipeline/materialize.js +1103 -0
  88. package/dist/pipeline/materialize.js.map +1 -0
  89. package/dist/pipeline/parseStateKey.d.ts +15 -0
  90. package/dist/pipeline/parseStateKey.js +446 -0
  91. package/dist/pipeline/parseStateKey.js.map +1 -0
  92. package/dist/pipeline/simplify.js +515 -0
  93. package/dist/pipeline/simplify.js.map +1 -0
  94. package/dist/pipeline/warnings.js +18 -0
  95. package/dist/pipeline/warnings.js.map +1 -0
  96. package/dist/plugins/index.d.ts +2 -0
  97. package/dist/plugins/okhsl-plugin.d.ts +35 -0
  98. package/dist/plugins/okhsl-plugin.js +97 -0
  99. package/dist/plugins/okhsl-plugin.js.map +1 -0
  100. package/dist/plugins/types.d.ts +87 -0
  101. package/dist/properties/index.js +222 -0
  102. package/dist/properties/index.js.map +1 -0
  103. package/dist/properties/property-type-resolver.d.ts +24 -0
  104. package/dist/properties/property-type-resolver.js +90 -0
  105. package/dist/properties/property-type-resolver.js.map +1 -0
  106. package/dist/rsc-cache.js +81 -0
  107. package/dist/rsc-cache.js.map +1 -0
  108. package/dist/ssr/astro-client.d.ts +1 -0
  109. package/dist/ssr/astro-client.js +24 -0
  110. package/dist/ssr/astro-client.js.map +1 -0
  111. package/dist/ssr/astro-middleware.d.ts +15 -0
  112. package/dist/ssr/astro-middleware.js +19 -0
  113. package/dist/ssr/astro-middleware.js.map +1 -0
  114. package/dist/ssr/astro.d.ts +106 -0
  115. package/dist/ssr/astro.js +149 -0
  116. package/dist/ssr/astro.js.map +1 -0
  117. package/dist/ssr/async-storage.d.ts +17 -0
  118. package/dist/ssr/async-storage.js +44 -0
  119. package/dist/ssr/async-storage.js.map +1 -0
  120. package/dist/ssr/collect-auto-properties.js +58 -0
  121. package/dist/ssr/collect-auto-properties.js.map +1 -0
  122. package/dist/ssr/collector.d.ts +102 -0
  123. package/dist/ssr/collector.js +227 -0
  124. package/dist/ssr/collector.js.map +1 -0
  125. package/dist/ssr/context.js +16 -0
  126. package/dist/ssr/context.js.map +1 -0
  127. package/dist/ssr/format-global-rules.js +22 -0
  128. package/dist/ssr/format-global-rules.js.map +1 -0
  129. package/dist/ssr/format-keyframes.js +69 -0
  130. package/dist/ssr/format-keyframes.js.map +1 -0
  131. package/dist/ssr/format-property.js +49 -0
  132. package/dist/ssr/format-property.js.map +1 -0
  133. package/dist/ssr/format-rules.js +73 -0
  134. package/dist/ssr/format-rules.js.map +1 -0
  135. package/dist/ssr/hydrate.d.ts +22 -0
  136. package/dist/ssr/hydrate.js +49 -0
  137. package/dist/ssr/hydrate.js.map +1 -0
  138. package/dist/ssr/index.d.ts +4 -0
  139. package/dist/ssr/index.js +10 -0
  140. package/dist/ssr/index.js.map +1 -0
  141. package/dist/ssr/next.d.ts +45 -0
  142. package/dist/ssr/next.js +75 -0
  143. package/dist/ssr/next.js.map +1 -0
  144. package/dist/ssr/ssr-collector-ref.js +29 -0
  145. package/dist/ssr/ssr-collector-ref.js.map +1 -0
  146. package/dist/states/index.d.ts +49 -0
  147. package/dist/states/index.js +170 -0
  148. package/dist/states/index.js.map +1 -0
  149. package/dist/static/index.d.ts +5 -0
  150. package/dist/static/index.js +4 -0
  151. package/dist/static/inject.d.ts +5 -0
  152. package/dist/static/inject.js +17 -0
  153. package/dist/static/inject.js.map +1 -0
  154. package/dist/static/tastyStatic.d.ts +46 -0
  155. package/dist/static/tastyStatic.js +30 -0
  156. package/dist/static/tastyStatic.js.map +1 -0
  157. package/dist/static/types.d.ts +49 -0
  158. package/dist/static/types.js +24 -0
  159. package/dist/static/types.js.map +1 -0
  160. package/dist/styles/border.d.ts +25 -0
  161. package/dist/styles/border.js +120 -0
  162. package/dist/styles/border.js.map +1 -0
  163. package/dist/styles/color.d.ts +14 -0
  164. package/dist/styles/color.js +26 -0
  165. package/dist/styles/color.js.map +1 -0
  166. package/dist/styles/const.js +17 -0
  167. package/dist/styles/const.js.map +1 -0
  168. package/dist/styles/createStyle.js +79 -0
  169. package/dist/styles/createStyle.js.map +1 -0
  170. package/dist/styles/dimension.js +109 -0
  171. package/dist/styles/dimension.js.map +1 -0
  172. package/dist/styles/directional.js +133 -0
  173. package/dist/styles/directional.js.map +1 -0
  174. package/dist/styles/display.d.ts +30 -0
  175. package/dist/styles/display.js +73 -0
  176. package/dist/styles/display.js.map +1 -0
  177. package/dist/styles/fade.d.ts +15 -0
  178. package/dist/styles/fade.js +62 -0
  179. package/dist/styles/fade.js.map +1 -0
  180. package/dist/styles/fill.d.ts +42 -0
  181. package/dist/styles/fill.js +51 -0
  182. package/dist/styles/fill.js.map +1 -0
  183. package/dist/styles/flow.d.ts +16 -0
  184. package/dist/styles/flow.js +12 -0
  185. package/dist/styles/flow.js.map +1 -0
  186. package/dist/styles/gap.d.ts +31 -0
  187. package/dist/styles/gap.js +38 -0
  188. package/dist/styles/gap.js.map +1 -0
  189. package/dist/styles/height.d.ts +17 -0
  190. package/dist/styles/height.js +19 -0
  191. package/dist/styles/height.js.map +1 -0
  192. package/dist/styles/index.d.ts +1 -0
  193. package/dist/styles/index.js +8 -0
  194. package/dist/styles/index.js.map +1 -0
  195. package/dist/styles/inset.d.ts +24 -0
  196. package/dist/styles/inset.js +34 -0
  197. package/dist/styles/inset.js.map +1 -0
  198. package/dist/styles/list.d.ts +16 -0
  199. package/dist/styles/list.js +100 -0
  200. package/dist/styles/list.js.map +1 -0
  201. package/dist/styles/margin.d.ts +24 -0
  202. package/dist/styles/margin.js +32 -0
  203. package/dist/styles/margin.js.map +1 -0
  204. package/dist/styles/outline.d.ts +29 -0
  205. package/dist/styles/outline.js +55 -0
  206. package/dist/styles/outline.js.map +1 -0
  207. package/dist/styles/padding.d.ts +24 -0
  208. package/dist/styles/padding.js +32 -0
  209. package/dist/styles/padding.js.map +1 -0
  210. package/dist/styles/placement.d.ts +37 -0
  211. package/dist/styles/placement.js +74 -0
  212. package/dist/styles/placement.js.map +1 -0
  213. package/dist/styles/predefined.d.ts +71 -0
  214. package/dist/styles/predefined.js +237 -0
  215. package/dist/styles/predefined.js.map +1 -0
  216. package/dist/styles/preset.d.ts +52 -0
  217. package/dist/styles/preset.js +127 -0
  218. package/dist/styles/preset.js.map +1 -0
  219. package/dist/styles/radius.d.ts +12 -0
  220. package/dist/styles/radius.js +83 -0
  221. package/dist/styles/radius.js.map +1 -0
  222. package/dist/styles/scrollMargin.d.ts +24 -0
  223. package/dist/styles/scrollMargin.js +32 -0
  224. package/dist/styles/scrollMargin.js.map +1 -0
  225. package/dist/styles/scrollbar.d.ts +25 -0
  226. package/dist/styles/scrollbar.js +51 -0
  227. package/dist/styles/scrollbar.js.map +1 -0
  228. package/dist/styles/shadow.d.ts +14 -0
  229. package/dist/styles/shadow.js +25 -0
  230. package/dist/styles/shadow.js.map +1 -0
  231. package/dist/styles/shared.js +17 -0
  232. package/dist/styles/shared.js.map +1 -0
  233. package/dist/styles/transition.d.ts +14 -0
  234. package/dist/styles/transition.js +159 -0
  235. package/dist/styles/transition.js.map +1 -0
  236. package/dist/styles/types.d.ts +564 -0
  237. package/dist/styles/width.d.ts +17 -0
  238. package/dist/styles/width.js +19 -0
  239. package/dist/styles/width.js.map +1 -0
  240. package/dist/tasty.d.ts +134 -0
  241. package/dist/tasty.js +243 -0
  242. package/dist/tasty.js.map +1 -0
  243. package/dist/types.d.ts +184 -0
  244. package/dist/utils/cache-wrapper.js +21 -0
  245. package/dist/utils/cache-wrapper.js.map +1 -0
  246. package/dist/utils/case-converter.js +8 -0
  247. package/dist/utils/case-converter.js.map +1 -0
  248. package/dist/utils/color-math.d.ts +46 -0
  249. package/dist/utils/color-math.js +749 -0
  250. package/dist/utils/color-math.js.map +1 -0
  251. package/dist/utils/color-space.d.ts +5 -0
  252. package/dist/utils/color-space.js +228 -0
  253. package/dist/utils/color-space.js.map +1 -0
  254. package/dist/utils/colors.d.ts +5 -0
  255. package/dist/utils/colors.js +10 -0
  256. package/dist/utils/colors.js.map +1 -0
  257. package/dist/utils/css-types.d.ts +7 -0
  258. package/dist/utils/deps-equal.js +15 -0
  259. package/dist/utils/deps-equal.js.map +1 -0
  260. package/dist/utils/dotize.d.ts +26 -0
  261. package/dist/utils/dotize.js +122 -0
  262. package/dist/utils/dotize.js.map +1 -0
  263. package/dist/utils/filter-base-props.d.ts +15 -0
  264. package/dist/utils/filter-base-props.js +45 -0
  265. package/dist/utils/filter-base-props.js.map +1 -0
  266. package/dist/utils/get-display-name.d.ts +7 -0
  267. package/dist/utils/get-display-name.js +10 -0
  268. package/dist/utils/get-display-name.js.map +1 -0
  269. package/dist/utils/has-keys.js +13 -0
  270. package/dist/utils/has-keys.js.map +1 -0
  271. package/dist/utils/hash.js +14 -0
  272. package/dist/utils/hash.js.map +1 -0
  273. package/dist/utils/is-dev-env.js +19 -0
  274. package/dist/utils/is-dev-env.js.map +1 -0
  275. package/dist/utils/is-valid-element-type.js +15 -0
  276. package/dist/utils/is-valid-element-type.js.map +1 -0
  277. package/dist/utils/merge-styles.d.ts +7 -0
  278. package/dist/utils/merge-styles.js +145 -0
  279. package/dist/utils/merge-styles.js.map +1 -0
  280. package/dist/utils/mod-attrs.d.ts +6 -0
  281. package/dist/utils/mod-attrs.js +20 -0
  282. package/dist/utils/mod-attrs.js.map +1 -0
  283. package/dist/utils/process-tokens.d.ts +17 -0
  284. package/dist/utils/process-tokens.js +83 -0
  285. package/dist/utils/process-tokens.js.map +1 -0
  286. package/dist/utils/resolve-recipes.d.ts +17 -0
  287. package/dist/utils/resolve-recipes.js +146 -0
  288. package/dist/utils/resolve-recipes.js.map +1 -0
  289. package/dist/utils/selector-transform.js +32 -0
  290. package/dist/utils/selector-transform.js.map +1 -0
  291. package/dist/utils/string.js +8 -0
  292. package/dist/utils/string.js.map +1 -0
  293. package/dist/utils/styles.d.ts +99 -0
  294. package/dist/utils/styles.js +220 -0
  295. package/dist/utils/styles.js.map +1 -0
  296. package/dist/utils/typography.d.ts +58 -0
  297. package/dist/utils/typography.js +51 -0
  298. package/dist/utils/typography.js.map +1 -0
  299. package/dist/utils/warnings.d.ts +16 -0
  300. package/dist/utils/warnings.js +16 -0
  301. package/dist/utils/warnings.js.map +1 -0
  302. package/dist/zero/babel.d.ts +195 -0
  303. package/dist/zero/babel.js +456 -0
  304. package/dist/zero/babel.js.map +1 -0
  305. package/dist/zero/css-writer.d.ts +45 -0
  306. package/dist/zero/css-writer.js +73 -0
  307. package/dist/zero/css-writer.js.map +1 -0
  308. package/dist/zero/extractor.d.ts +24 -0
  309. package/dist/zero/extractor.js +266 -0
  310. package/dist/zero/extractor.js.map +1 -0
  311. package/dist/zero/index.d.ts +3 -0
  312. package/dist/zero/index.js +3 -0
  313. package/dist/zero/next.d.ts +86 -0
  314. package/dist/zero/next.js +143 -0
  315. package/dist/zero/next.js.map +1 -0
  316. package/docs/PIPELINE.md +519 -0
  317. package/docs/README.md +31 -0
  318. package/docs/adoption.md +298 -0
  319. package/docs/comparison.md +419 -0
  320. package/docs/configuration.md +389 -0
  321. package/docs/debug.md +318 -0
  322. package/docs/design-system.md +436 -0
  323. package/docs/dsl.md +688 -0
  324. package/docs/getting-started.md +217 -0
  325. package/docs/injector.md +544 -0
  326. package/docs/methodology.md +616 -0
  327. package/docs/react-api.md +557 -0
  328. package/docs/ssr.md +440 -0
  329. package/docs/styles.md +596 -0
  330. package/docs/tasty-static.md +532 -0
  331. package/package.json +221 -0
  332. package/tasty.config.ts +14 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"next.js","names":[],"sources":["../../src/zero/next.ts"],"sourcesContent":["/**\n * Next.js configuration wrapper for tasty-zero.\n *\n * Supports both webpack and Turbopack bundlers:\n * - **webpack**: Injects a babel-loader rule with the tasty-zero Babel plugin\n * via `webpack()` config hook. Config is passed as a jiti factory function.\n * - **Turbopack**: Adds a `turbopack.rules` entry with babel-loader and\n * JSON-serializable options (`configFile` path instead of a function).\n * The Babel plugin loads the config internally via jiti.\n *\n * The generated CSS is injected automatically — `@tenphi/tasty/static`\n * imports are replaced with an import of the output CSS file at build time.\n * No manual CSS import in layout files is needed.\n *\n * @example\n * ```javascript\n * // next.config.js\n * const { withTastyZero } = require('@tenphi/tasty/next');\n *\n * module.exports = withTastyZero({\n * output: 'public/tasty.css',\n * configFile: './app/tasty-zero.config.ts',\n * })({\n * // your Next.js config\n * });\n * ```\n */\n\nimport { createRequire } from 'module';\nimport * as path from 'path';\nimport { fileURLToPath } from 'url';\n\nimport { createJiti } from 'jiti';\n\nimport type { TastyZeroBabelOptions, TastyZeroConfig } from './babel';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Next.js types (inline to avoid requiring next as a dependency)\ninterface WebpackConfigContext {\n isServer: boolean;\n dev: boolean;\n buildId: string;\n dir: string;\n}\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- webpack/Next.js config types are complex */\ninterface TurbopackLoaderItem {\n loader: string;\n options?: Record<string, unknown>;\n}\n\ninterface TurbopackRuleConfig {\n loaders: (string | TurbopackLoaderItem)[];\n as?: string;\n condition?: unknown;\n}\n\ninterface TurbopackConfig {\n rules?: Record<string, TurbopackRuleConfig | TurbopackRuleConfig[]>;\n [key: string]: unknown;\n}\n\ninterface NextConfig {\n webpack?: (config: any, context: WebpackConfigContext) => any;\n turbopack?: TurbopackConfig;\n [key: string]: unknown;\n}\n\nexport interface TastyZeroNextOptions {\n /**\n * Output path for CSS relative to project root.\n * @default 'public/tasty.css'\n */\n output?: string;\n\n /**\n * Whether to enable the plugin.\n * @default true\n */\n enabled?: boolean;\n\n /**\n * Tasty configuration for build-time processing.\n * For static configs that don't change during dev.\n *\n * For configs that depend on theme files, use `configFile` instead.\n */\n config?: TastyZeroConfig;\n\n /**\n * Path to a TypeScript/JavaScript module that exports the tasty zero config\n * as its default export. The module is re-evaluated on each\n * compilation, enabling hot reload when the file (or its imports) change.\n *\n * @example './app/tasty-zero.config.ts'\n */\n configFile?: string;\n\n /**\n * Extra file paths (relative to project root) that the config depends on.\n * When any of these files change, the Babel cache is invalidated and\n * the config is re-evaluated.\n *\n * The `configFile` itself is always tracked automatically.\n * Use this for transitive dependencies that aren't directly imported\n * by the config file, or when using `config` instead of `configFile`.\n *\n * @example ['./app/theme.ts']\n */\n configDeps?: string[];\n\n /**\n * Output mode for extracted CSS.\n *\n * - `'file'` (default): CSS is written to a single output file.\n * - `'inject'`: CSS is embedded inline in JS and injected at runtime.\n * No CSS file is written. Best for reusable components and extensions.\n *\n * When `mode` is `'inject'`, `output` is ignored.\n *\n * @default 'file'\n */\n mode?: 'file' | 'inject';\n}\n\n/**\n * Next.js configuration wrapper for tasty-zero.\n * Configures both webpack and Turbopack bundlers automatically.\n */\nexport function withTastyZero(options: TastyZeroNextOptions = {}) {\n const {\n output = 'public/tasty.css',\n enabled = true,\n config: tastyConfig,\n configFile,\n configDeps = [],\n mode,\n } = options;\n\n return (nextConfig: NextConfig = {}): NextConfig => {\n if (!enabled) {\n return nextConfig;\n }\n\n const projectDir = process.cwd();\n const absoluteOutput = path.resolve(projectDir, output);\n const babelPluginPath = path.resolve(__dirname, 'babel.js');\n\n const absoluteConfigFile = configFile\n ? path.resolve(projectDir, configFile)\n : undefined;\n\n const allDeps = [\n ...(absoluteConfigFile ? [absoluteConfigFile] : []),\n ...configDeps.map((dep) => path.resolve(projectDir, dep)),\n ];\n\n // --- Turbopack configuration ---\n // Turbopack loader options must be JSON-serializable (no functions).\n // The Babel plugin loads config internally via `configFile` path + jiti.\n const turbopackBabelOptions: Record<string, unknown> = {\n babelrc: false,\n configFile: false,\n parserOpts: {\n plugins: ['typescript', 'jsx', 'decorators-legacy'],\n },\n plugins: [\n [\n babelPluginPath,\n {\n output: absoluteOutput,\n injectImport: true,\n ...(mode ? { mode } : {}),\n ...(absoluteConfigFile\n ? { configFile: absoluteConfigFile }\n : tastyConfig\n ? { config: tastyConfig }\n : {}),\n ...(allDeps.length > 0 ? { configDeps: allDeps } : {}),\n },\n ],\n ],\n };\n\n const existingTurbopack = nextConfig.turbopack || {};\n const existingRules = existingTurbopack.rules || {};\n\n const existingExperimental =\n (nextConfig.experimental as Record<string, unknown>) || {};\n\n return {\n ...nextConfig,\n\n experimental: {\n ...existingExperimental,\n turbopackUseBuiltinBabel: true,\n },\n\n turbopack: {\n ...existingTurbopack,\n rules: {\n ...existingRules,\n '*.{ts,tsx,js,jsx}': {\n condition: { not: 'foreign' },\n loaders: [\n {\n loader: 'babel-loader',\n options: turbopackBabelOptions,\n },\n ],\n },\n },\n },\n\n webpack(config: any, context: WebpackConfigContext) {\n const { dir } = context;\n\n const wpProjectDir = dir || projectDir;\n const wpAbsoluteOutput = path.resolve(wpProjectDir, output);\n const projectRequire = createRequire(\n path.resolve(wpProjectDir, 'package.json'),\n );\n\n const wpAbsoluteConfigFile = configFile\n ? path.resolve(wpProjectDir, configFile)\n : undefined;\n\n const wpAllDeps = [\n ...(wpAbsoluteConfigFile ? [wpAbsoluteConfigFile] : []),\n ...configDeps.map((dep) => path.resolve(wpProjectDir, dep)),\n ];\n\n const babelPluginOptions: TastyZeroBabelOptions = {\n output: wpAbsoluteOutput,\n injectImport: true,\n ...(mode ? { mode } : {}),\n };\n\n if (wpAbsoluteConfigFile) {\n const jiti = createJiti(wpProjectDir, {\n moduleCache: false,\n });\n\n babelPluginOptions.config = () => {\n return jiti(wpAbsoluteConfigFile) as TastyZeroConfig;\n };\n } else if (tastyConfig) {\n babelPluginOptions.config = tastyConfig;\n }\n\n if (wpAllDeps.length > 0) {\n babelPluginOptions.configDeps = wpAllDeps;\n }\n\n const babelPluginConfig = [babelPluginPath, babelPluginOptions];\n\n const existingRule = config.module?.rules?.find(\n (rule: any) =>\n rule.use?.loader === 'babel-loader' ||\n rule.use?.some?.((u: any) => u.loader === 'babel-loader'),\n );\n\n if (existingRule) {\n const babelUse = Array.isArray(existingRule.use)\n ? existingRule.use.find((u: any) => u.loader === 'babel-loader')\n : existingRule.use;\n\n if (babelUse?.options) {\n babelUse.options.plugins = babelUse.options.plugins || [];\n babelUse.options.plugins.push(babelPluginConfig);\n }\n } else {\n config.module = config.module || {};\n config.module.rules = config.module.rules || [];\n config.module.rules.push({\n test: /\\.(tsx?|jsx?)$/,\n exclude: /node_modules/,\n use: [\n {\n loader: projectRequire.resolve('babel-loader'),\n options: {\n babelrc: false,\n configFile: false,\n parserOpts: {\n plugins: ['typescript', 'jsx', 'decorators-legacy'],\n },\n plugins: [babelPluginConfig],\n },\n },\n ],\n });\n }\n\n if (typeof nextConfig.webpack === 'function') {\n return nextConfig.webpack(config, context);\n }\n\n return config;\n },\n };\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,519 @@
1
+ # Tasty Style Rendering Pipeline
2
+
3
+ This document describes the style rendering pipeline that transforms style objects into CSS rules. The pipeline ensures that each style value is applied to exactly one condition through exclusive condition building, boolean simplification, and intelligent CSS generation.
4
+
5
+ **Implementation:** [`src/pipeline/`](../src/pipeline/) — TypeScript file names below are relative to that directory.
6
+
7
+ ## Overview
8
+
9
+ The pipeline takes a `Styles` object and produces an array of `CSSRule` objects ready for injection into the DOM. Entry points include `renderStylesPipeline` (full pipeline + optional class-name prefixing) and `renderStyles` (direct selector/class mode). The per-handler flow has seven main stages:
10
+
11
+ ```
12
+ Input: Styles Object
13
+
14
+ ┌─────────────────────────────────────┐
15
+ │ 1. PARSE CONDITIONS │
16
+ │ parseStyleEntries + parseStateKey│
17
+ └─────────────────────────────────────┘
18
+
19
+ ┌─────────────────────────────────────┐
20
+ │ 2. BUILD EXCLUSIVE CONDITIONS │
21
+ │ Negate higher-priority entries │
22
+ └─────────────────────────────────────┘
23
+
24
+ ┌─────────────────────────────────────┐
25
+ │ 3. EXPAND AT-RULE OR BRANCHES │
26
+ │ expandExclusiveOrs (when needed)│
27
+ └─────────────────────────────────────┘
28
+
29
+ ┌─────────────────────────────────────┐
30
+ │ 4. COMPUTE STATE COMBINATIONS │
31
+ │ Cartesian product across styles │
32
+ └─────────────────────────────────────┘
33
+
34
+ ┌─────────────────────────────────────┐
35
+ │ 5. CALL HANDLERS │
36
+ │ Compute CSS declarations │
37
+ └─────────────────────────────────────┘
38
+
39
+ ┌─────────────────────────────────────┐
40
+ │ 6. MERGE BY VALUE │
41
+ │ Combine rules with same output │
42
+ └─────────────────────────────────────┘
43
+
44
+ ┌─────────────────────────────────────┐
45
+ │ 7. MATERIALIZE CSS │
46
+ │ Condition → selectors + at-rules│
47
+ └─────────────────────────────────────┘
48
+
49
+ ┌─────────────────────────────────────┐
50
+ │ runPipeline: dedupe identical rules │
51
+ └─────────────────────────────────────┘
52
+
53
+ Output: CSSRule[]
54
+ ```
55
+
56
+ **Simplification** (`simplifyCondition` in `simplify.ts`) is not a separate numbered stage. It runs inside exclusive building, `expandExclusiveOrs` branch cleanup, combination ANDs, merge-by-value ORs, and materialization as needed.
57
+
58
+ **Post-pass:** After `processStyles` collects rules from every handler, `runPipeline` filters duplicates using a key of `selector|declarations|atRules|rootPrefix` so identical emitted rules appear once.
59
+
60
+ ---
61
+
62
+ ## Stage 1: Parse Conditions
63
+
64
+ **Files:** `exclusive.ts` (`parseStyleEntries`), `parseStateKey.ts` (`parseStateKey`)
65
+
66
+ ### What It Does
67
+
68
+ Converts each state key in a style value map (like `'hovered & !disabled'`, `'@media(w < 768px)'`) into `ConditionNode` trees. `parseStyleEntries` walks the object keys in source order and assigns priorities; `parseStateKey` parses a single key string.
69
+
70
+ ### How It Works
71
+
72
+ 1. **Tokenization**: The state key is split into tokens using a regex pattern that recognizes:
73
+ - Operators: `&` (AND), `|` (OR), `!` (NOT), `^` (XOR)
74
+ - Parentheses for grouping
75
+ - State tokens: `@media(...)`, `@root(...)`, `@parent(...)`, `@own(...)`, `@supports(...)`, `@(...)`, `@starting`, predefined states, modifiers, pseudo-classes
76
+
77
+ 2. **Recursive Descent Parsing**: Tokens are parsed with operator precedence:
78
+ ```
79
+ ! (NOT) > ^ (XOR) > | (OR) > & (AND)
80
+ ```
81
+
82
+ 3. **State Token Interpretation**: Each state token is converted to a specific condition type:
83
+ - `hovered` → `ModifierCondition` with `attribute: 'data-hovered'`
84
+ - `theme=dark` → `ModifierCondition` with `attribute: 'data-theme', value: 'dark'`
85
+ - `:hover` → `PseudoCondition`
86
+ - `@media(w < 768px)` → `MediaCondition` (`subtype: 'dimension'`) with bounds
87
+ - `@media(prefers-color-scheme: dark)` → `MediaCondition` (`subtype: 'feature'`, `feature` + `featureValue`)
88
+ - `@root(schema=dark)` → `RootCondition` wrapping the inner condition
89
+ - `@parent(hovered)` → `ParentCondition` (optional `direct` for immediate parent)
90
+ - `@own(hovered)` → `OwnCondition` wrapping the parsed inner condition
91
+ - `@supports(display: grid)` → `SupportsCondition`
92
+ - `@(w < 600px)` → `ContainerCondition` (dimension, style, or raw subtypes)
93
+ - `@mobile` → Resolved via predefined states, then parsed recursively
94
+
95
+ Pipeline warnings for invalid inputs (e.g. bad `$` selector affix) are emitted from `warnings.ts`.
96
+
97
+ ### Why
98
+
99
+ The condition tree representation enables:
100
+ - Boolean algebra operations (simplification, negation)
101
+ - Semantic analysis (detect contradictions)
102
+ - Flexible CSS generation (different output for media vs. selectors)
103
+
104
+ ### Example
105
+
106
+ ```typescript
107
+ // Input
108
+ 'hovered & @media(w < 768px)'
109
+
110
+ // Output ConditionNode
111
+ {
112
+ kind: 'compound',
113
+ operator: 'AND',
114
+ children: [
115
+ { kind: 'state', type: 'modifier', attribute: 'data-hovered', ... },
116
+ { kind: 'state', type: 'media', subtype: 'dimension', upperBound: { value: '768px', ... }, ... }
117
+ ]
118
+ }
119
+ ```
120
+
121
+ ---
122
+
123
+ ## Stage 2: Build Exclusive Conditions
124
+
125
+ **File:** `exclusive.ts` (`buildExclusiveConditions`)
126
+
127
+ ### What It Does
128
+
129
+ Ensures each style entry applies in exactly one scenario by ANDing each condition with the negation of all higher-priority conditions.
130
+
131
+ ### How It Works
132
+
133
+ Given entries ordered by priority (highest first):
134
+ ```
135
+ A: value1 (priority 2)
136
+ B: value2 (priority 1)
137
+ C: value3 (priority 0)
138
+ ```
139
+
140
+ Produces:
141
+ ```
142
+ A: A (highest priority, no negation needed)
143
+ B: B & !A (applies only when A doesn't)
144
+ C: C & !A & !B (applies only when neither A nor B)
145
+ ```
146
+
147
+ Each exclusive condition is passed through `simplifyCondition`. Entries that simplify to `FALSE` (impossible) are filtered out. The default state (`''` → `TrueCondition`) is not added to the “prior” list for negation (see `buildExclusiveConditions`).
148
+
149
+ ### Why
150
+
151
+ This eliminates CSS specificity wars. Instead of relying on cascade order, each CSS rule matches in exactly one scenario. Benefits:
152
+ - Predictable styling regardless of rule order
153
+ - No conflicts from overlapping conditions
154
+ - Easier debugging (each rule is mutually exclusive)
155
+
156
+ ### Example
157
+
158
+ ```typescript
159
+ // Style value mapping
160
+ { padding: { '': '2x', 'compact': '1x', '@media(w < 768px)': '0.5x' } }
161
+
162
+ // After exclusive building (highest priority first):
163
+ // @media(w < 768px): applies when media matches
164
+ // compact & !@media(w < 768px): applies when compact but NOT media
165
+ // !compact & !@media(w < 768px): default, applies when neither
166
+ ```
167
+
168
+ ---
169
+
170
+ ## Stage 3: Expand At-Rule OR Branches
171
+
172
+ **File:** `exclusive.ts` (`expandExclusiveOrs`)
173
+
174
+ ### What It Does
175
+
176
+ Runs **after** `buildExclusiveConditions`. When an entry’s **exclusive** condition contains a top-level OR that mixes **at-rule** context (`media`, `container`, `supports`, `starting`) with other branches, those ORs are split into mutually exclusive branches so each branch keeps the correct at-rule wrapping (e.g. after De Morgan: `!(A & B)` → `!A | !B`).
177
+
178
+ ### How It Works
179
+
180
+ 1. Collect top-level OR branches of `exclusiveCondition`.
181
+ 2. If there is no OR, or **no** branch involves at-rule context, the entry is unchanged (pure selector ORs are handled later via `:is()` / variant merging in materialization).
182
+ 3. Otherwise, branches are sorted with `sortOrBranchesForExpansion` so at-rule-heavy branches come first, then each branch is made exclusive against prior branches: `branch & !prior[0] & !prior[1] & ...`, then simplified.
183
+ 4. Impossible branches are dropped; expanded entries get a synthetic `stateKey` suffix like `[or:0]`.
184
+
185
+ ### Why
186
+
187
+ Without this pass, a condition like `!(@supports & :has)` could produce one rule missing the `@supports` wrapper. Exclusive OR expansion ensures negated at-rule groups still nest modifiers correctly.
188
+
189
+ ### Example (conceptual)
190
+
191
+ See the comment block in `exclusive.ts` (~195–206): a default value’s exclusive condition can become `!@supports | !:has`; expansion yields one branch under `@supports (not …)` and another under `@supports (…) { :not(:has()) }` instead of a bare `:not(:has())` rule.
192
+
193
+ ---
194
+
195
+ ## Stage 4: Compute State Combinations
196
+
197
+ **File:** `index.ts` (`computeStateCombinations`)
198
+
199
+ ### What It Does
200
+
201
+ Computes the Cartesian product of all style entries for a handler, creating snapshots of which value each style has for each possible state combination.
202
+
203
+ ### How It Works
204
+
205
+ 1. Collect exclusive entries for each style the handler uses
206
+ 2. Compute Cartesian product: every combination of entries
207
+ 3. For each combination:
208
+ - AND all `exclusiveCondition` values together
209
+ - `simplifyCondition` the result
210
+ - Skip if simplified to `FALSE`
211
+ - Record the values for each style
212
+
213
+ ### Why
214
+
215
+ Style handlers often depend on multiple style properties (e.g., `padding` might look at both `padding` and `gap`). By computing all valid combinations, we can call the handler once per unique state and get the correct CSS output.
216
+
217
+ ### Example
218
+
219
+ ```typescript
220
+ // Handler looks up: ['padding', 'size']
221
+ // padding has entries: [{ value: '2x', condition: A }, { value: '1x', condition: B }]
222
+ // size has entries: [{ value: 'large', condition: C }, { value: 'small', condition: D }]
223
+
224
+ // Combinations:
225
+ // { padding: '2x', size: 'large', condition: A & C }
226
+ // { padding: '2x', size: 'small', condition: A & D }
227
+ // { padding: '1x', size: 'large', condition: B & C }
228
+ // { padding: '1x', size: 'small', condition: B & D }
229
+ ```
230
+
231
+ ---
232
+
233
+ ## Stage 5: Call Handlers
234
+
235
+ **File:** `index.ts` (within `processStyles`)
236
+
237
+ ### What It Does
238
+
239
+ Invokes style handlers with computed value snapshots to produce CSS declarations.
240
+
241
+ ### How It Works
242
+
243
+ 1. For each state snapshot (condition + values):
244
+ - Call the handler with the values
245
+ - Handler returns CSS properties (e.g., `{ 'padding-top': '16px', 'padding-bottom': '16px' }`)
246
+ - Handler may also return `$` (selector suffix) for pseudo-elements
247
+ 2. Create computed rules with the condition, declarations, and selector suffix
248
+
249
+ ### Why
250
+
251
+ Style handlers encapsulate the logic for translating design tokens (like `'2x'`) to actual CSS values (like `'16px'`). They can also handle complex multi-property styles (e.g., `padding` → `padding-top`, `padding-right`, etc.).
252
+
253
+ ---
254
+
255
+ ## Stage 6: Merge By Value
256
+
257
+ **File:** `index.ts` (`mergeByValue`)
258
+
259
+ ### What It Does
260
+
261
+ Combines rules that have identical CSS output into a single rule with an OR condition.
262
+
263
+ ### How It Works
264
+
265
+ 1. Group rules by `selectorSuffix` plus a stable string for declarations (JSON via an internal `declStringCache` `WeakMap` on declaration objects)
266
+ 2. For rules in the same group:
267
+ - Merge their conditions with OR
268
+ - `simplifyCondition` the resulting condition
269
+ 3. Output one rule per group
270
+
271
+ ### Why
272
+
273
+ Different state combinations might produce the same CSS output. Rather than emitting duplicate CSS, we combine them into a single rule. This reduces CSS size and improves performance.
274
+
275
+ ### Example
276
+
277
+ ```typescript
278
+ // Before merging:
279
+ // condition: A → { color: 'red' }
280
+ // condition: B → { color: 'red' }
281
+
282
+ // After merging:
283
+ // condition: A | B → { color: 'red' }
284
+ ```
285
+
286
+ ---
287
+
288
+ ## Stage 7: Materialize CSS
289
+
290
+ **File:** `materialize.ts` (`conditionToCSS`, `materializeComputedRule` in `index.ts`)
291
+
292
+ ### What It Does
293
+
294
+ Converts condition trees into actual CSS selectors and at-rules.
295
+
296
+ ### How It Works
297
+
298
+ 1. **Condition to CSS components** (`conditionToCSS`): Walk the condition tree and build `SelectorVariant` data:
299
+ - `ModifierCondition` → attribute selectors (e.g. `[data-hovered]`); optional `operator` (`=`, `^=`, `$=`, `*=`)
300
+ - `PseudoCondition` → pseudo-class (e.g. `:hover`)
301
+ - `MediaCondition` → `@media` (dimension, feature, or type)
302
+ - `ContainerCondition` → `@container` (dimension, style query, or raw)
303
+ - `RootCondition` → `rootGroups` / root prefix fragments
304
+ - `ParentCondition` → `parentGroups` / ancestor selectors (`direct` → child combinator path)
305
+ - `OwnCondition` → `ownGroups` on the **styled** element (sub-element / `&` scope), optimized with `optimizeGroups`
306
+ - `SupportsCondition` → `@supports` at-rules
307
+ - `StartingCondition` → `@starting-style` wrapper
308
+
309
+ 2. **AND / OR on variants**: AND merges variant dimensions; OR yields multiple variants (later merged into `:is()` / `:not()` groups where appropriate).
310
+
311
+ 3. **Contradiction detection**: During variant merging, impossible combinations are dropped (e.g. conflicting media, root, or modifier negations).
312
+
313
+ 4. **`materializeComputedRule`**: Groups variants by sorted at-rules plus root-prefix key; within each group, `mergeVariantsIntoSelectorGroups` merges variants that differ only in flat modifier/pseudo parts; builds selector strings and emits one or more `CSSRule` objects.
314
+
315
+ ### Why
316
+
317
+ CSS has different mechanisms for different condition types:
318
+ - Modifiers → attribute selectors
319
+ - Media queries → `@media` blocks
320
+ - Container queries → `@container` blocks
321
+ - Root state → `:root` / root groups
322
+ - Supports → `@supports` blocks
323
+
324
+ The materialization layer handles these differences while maintaining the logical semantics of the condition tree.
325
+
326
+ ### Output Structure
327
+
328
+ ```typescript
329
+ interface CSSRule {
330
+ selector: string | string[]; // Selector fragment(s); array when OR’d selector branches
331
+ declarations: string; // CSS declarations (e.g. 'color: red;')
332
+ atRules?: string[]; // Wrapping at-rules
333
+ rootPrefix?: string; // Root state prefix
334
+ }
335
+ ```
336
+
337
+ When `renderStylesPipeline` runs **without** a class name, returned rules include `needsClassName: true` (compatibility field for the injector); that flag is not part of `CSSRule` inside `materialize.ts`.
338
+
339
+ ---
340
+
341
+ ## Condition Types
342
+
343
+ **File:** `conditions.ts`
344
+
345
+ ### ConditionNode Hierarchy
346
+
347
+ ```
348
+ ConditionNode
349
+ ├── TrueCondition (matches everything)
350
+ ├── FalseCondition (matches nothing)
351
+ ├── CompoundCondition (AND/OR of children)
352
+ └── StateCondition
353
+ ├── ModifierCondition (data attributes; optional value + match operator)
354
+ ├── PseudoCondition (CSS pseudo-classes: :hover)
355
+ ├── MediaCondition (subtype: dimension | feature | type)
356
+ ├── ContainerCondition (subtype: dimension | style | raw)
357
+ ├── RootCondition (inner condition under :root)
358
+ ├── ParentCondition (@parent(...); optional direct parent)
359
+ ├── OwnCondition (@own(...); scoped to styled / sub-element)
360
+ ├── SupportsCondition (@supports(...))
361
+ └── StartingCondition (@starting-style wrapper)
362
+ ```
363
+
364
+ ### Key Operations
365
+
366
+ - `and(...conditions)`: Create AND with short-circuit and flattening
367
+ - `or(...conditions)`: Create OR with short-circuit and flattening
368
+ - `not(condition)`: Negate with De Morgan's law support
369
+ - `getConditionUniqueId(condition)`: Get canonical ID for comparison
370
+
371
+ ---
372
+
373
+ ## Simplification
374
+
375
+ **File:** `simplify.ts`
376
+
377
+ ### What It Does
378
+
379
+ Applies boolean algebra rules to reduce condition complexity and detect impossible combinations.
380
+
381
+ ### Rules Applied
382
+
383
+ 1. **Identity Laws**:
384
+ - `A & TRUE = A`
385
+ - `A | FALSE = A`
386
+
387
+ 2. **Annihilator Laws**:
388
+ - `A & FALSE = FALSE`
389
+ - `A | TRUE = TRUE`
390
+
391
+ 3. **Contradiction Detection**:
392
+ - `A & !A = FALSE`
393
+
394
+ 4. **Tautology Detection**:
395
+ - `A | !A = TRUE`
396
+
397
+ 5. **Idempotent Laws** (via deduplication):
398
+ - `A & A = A`
399
+ - `A | A = A`
400
+
401
+ 6. **Absorption Laws**:
402
+ - `A & (A | B) = A`
403
+ - `A | (A & B) = A`
404
+
405
+ 7. **Range intersection**: For **media and container** dimension queries, impossible ranges simplify to `FALSE` (e.g. `@media(w > 400px) & @media(w < 300px)`).
406
+
407
+ 8. **Container style queries**: Conflicting or redundant `@container` style conditions on the same property can be reduced (see `simplify.ts` around the container-style conflict pass).
408
+
409
+ 9. **Attribute conflict detection**:
410
+ - `[data-theme="dark"] & [data-theme="light"] = FALSE`
411
+
412
+ ### Why
413
+
414
+ Simplification reduces CSS output size and catches impossible combinations early, preventing invalid CSS rules from being generated.
415
+
416
+ ---
417
+
418
+ ## Caching Strategy
419
+
420
+ LRU and small auxiliary caches:
421
+
422
+ | Cache | Size | Key | Purpose |
423
+ |-------|------|-----|---------|
424
+ | `pipelineCache` | 5000 | `pipelineCacheKey \|\| stringifyStyles(styles)` | Skip full pipeline for identical styles |
425
+ | `parseCache` | 5000 | `trimmedStateKey + '\\0' + isSubElement + '\\0' + JSON.stringify(localPredefinedStates)` | Skip re-parsing identical state keys in context |
426
+ | `simplifyCache` | 5000 | `getConditionUniqueId(node)` | Skip re-simplifying identical conditions |
427
+ | `conditionCache` | 3000 | `getConditionUniqueId(node)` in `conditionToCSS` | Skip re-materializing identical conditions |
428
+ | `variantKeyCache` | — | `WeakMap<SelectorVariant, string>` | Stable string keys for variants during materialization |
429
+ | `declStringCache` | — | `WeakMap<Record<string,string>, string>` | Stable JSON keys for declaration objects in `mergeByValue` |
430
+
431
+ ---
432
+
433
+ ## Example Walkthrough
434
+
435
+ ### Input
436
+
437
+ ```typescript
438
+ const styles = {
439
+ color: {
440
+ '': '#white',
441
+ '@media(prefers-color-scheme: dark)': '#dark',
442
+ hovered: '#highlight',
443
+ },
444
+ };
445
+ ```
446
+
447
+ ### Stage 1: Parse Conditions
448
+
449
+ ```
450
+ '' → TrueCondition
451
+ '@media(prefers-color-scheme: dark)' → MediaCondition(subtype: 'feature', feature: 'prefers-color-scheme', featureValue: 'dark')
452
+ 'hovered' → ModifierCondition(attribute: 'data-hovered')
453
+ ```
454
+
455
+ ### Stages 2–3: Exclusive conditions + expand OR
456
+
457
+ Processing order (highest priority first): `hovered`, `@media(dark)`, default.
458
+
459
+ ```
460
+ hovered: [data-hovered]
461
+ @media(dark) & !hovered: @media(dark) & :not([data-hovered])
462
+ !hovered & !@media(dark): :not([data-hovered]) & not @media(dark)
463
+ ```
464
+
465
+ No at-rule OR expansion needed on these exclusives.
466
+
467
+ ### Stages 4–5: Compute combinations and call handler
468
+
469
+ Single style, three snapshots; the `color` handler emits `color` plus `--current-color*` variables.
470
+
471
+ ### Stage 6: Merge by value
472
+
473
+ Each snapshot yields distinct declarations; no merge.
474
+
475
+ ### Stage 7: Materialize CSS
476
+
477
+ Using `renderStyles(styles, '.t1')` (single class prefix; `renderStylesPipeline` doubles the class for specificity when a class name is supplied):
478
+
479
+ ```css
480
+ .t1[data-hovered] {
481
+ color: var(--highlight-color);
482
+ --current-color: var(--highlight-color);
483
+ --current-color-oklch: var(--highlight-color-oklch);
484
+ }
485
+ @media (prefers-color-scheme: dark) {
486
+ .t1:not([data-hovered]) {
487
+ color: var(--dark-color);
488
+ --current-color: var(--dark-color);
489
+ --current-color-oklch: var(--dark-color-oklch);
490
+ }
491
+ }
492
+ @media (not (prefers-color-scheme: dark)) {
493
+ .t1:not([data-hovered]) {
494
+ color: var(--white-color);
495
+ --current-color: var(--white-color);
496
+ --current-color-oklch: var(--white-color-oklch);
497
+ }
498
+ }
499
+ ```
500
+
501
+ ---
502
+
503
+ ## Key Design Decisions
504
+
505
+ ### 1. Exclusive Conditions Over CSS Specificity
506
+
507
+ Rather than relying on CSS cascade rules, we generate mutually exclusive selectors. This makes styling predictable and debuggable.
508
+
509
+ ### 2. OR Handling: DNF, `:is()`, and `expandExclusiveOrs`
510
+
511
+ OR of conditions is ultimately expressed as DNF (OR of ANDs) for CSS—comma-separated selectors, multiple rules, or `:is()` / `:not()` groups. **User-authored** ORs on pure selector conditions are handled in materialization. **`expandExclusiveOrs`** is an additional, **post-exclusive** pass for ORs that appear on **exclusive** conditions and involve **at-rule** branches (often from De Morgan on `@supports` / `@media` / `@container` / `@starting`), so each branch keeps correct at-rule nesting.
512
+
513
+ ### 3. Early Contradiction Detection
514
+
515
+ Impossible combinations are detected at multiple levels (simplification, variant merging) to avoid generating invalid CSS.
516
+
517
+ ### 4. Aggressive Caching
518
+
519
+ Parse, simplify, condition-to-CSS, and full-pipeline results are cached independently, enabling fast re-rendering when only parts of the style object change.
package/docs/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # Tasty Docs
2
+
3
+ Tasty is a styling engine for design systems that turns component state into deterministic CSS by compiling state maps into mutually exclusive selectors. Use this hub to choose the right guide once you know whether you are evaluating the model, adopting it in a design system, or implementing reusable, stateful components day to day.
4
+
5
+ ## Start Here
6
+
7
+ - **New to Tasty**: [Getting Started](getting-started.md) for installation, the first component, optional shared `configure()`, ESLint, editor tooling, and rendering mode selection.
8
+ - **Learning the component model**: [Methodology](methodology.md) for root + sub-elements, `styleProps`, tokens, extension, and recommended boundaries between `styles`, `style`, and wrappers.
9
+ - **Evaluating the selector model**: [Style rendering pipeline](PIPELINE.md) for how mutually exclusive selectors make stateful styling deterministic.
10
+ - **Evaluating fit**: [Comparison](comparison.md) for tool-selection context, then [Adoption Guide](adoption.md) for audience fit and rollout strategy inside a design system.
11
+
12
+ ## By Role
13
+
14
+ - **Application developer using an existing design system**: [Getting Started](getting-started.md), then [React API](react-api.md).
15
+ - **Design-system author**: [Methodology](methodology.md), [Building a Design System](design-system.md), [Configuration](configuration.md), and [Adoption Guide](adoption.md).
16
+ - **Platform or tooling engineer**: [Configuration](configuration.md), [Zero Runtime (tastyStatic)](tasty-static.md), [Server-Side Rendering](ssr.md), and [Debug Utilities](debug.md).
17
+
18
+ ## By Styling Approach
19
+
20
+ - **React components**: [React API](react-api.md)
21
+ - **Zero-runtime / build-time extraction**: [Zero Runtime (tastyStatic)](tasty-static.md)
22
+ - **Runtime `tasty()` with server collection and hydration**: [Server-Side Rendering](ssr.md)
23
+
24
+ ## By Task
25
+
26
+ - **Learn the style language**: [Style DSL](dsl.md)
27
+ - **Look up a property handler**: [Style Properties](styles.md)
28
+ - **Define tokens, units, recipes, keyframes, or properties globally**: [Configuration](configuration.md)
29
+ - **Debug generated CSS or cache behavior**: [Debug Utilities](debug.md)
30
+ - **Understand how selector generation works internally**: [Style rendering pipeline](PIPELINE.md)
31
+ - **Understand runtime injection internals**: [Style Injector](injector.md)