decantr 0.9.0

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 (382) hide show
  1. package/AGENTS.md +868 -0
  2. package/CHANGELOG.md +255 -0
  3. package/CLAUDE.md +178 -0
  4. package/LICENSE +21 -0
  5. package/README.md +229 -0
  6. package/cli/art.js +127 -0
  7. package/cli/commands/a11y.js +61 -0
  8. package/cli/commands/audit.js +225 -0
  9. package/cli/commands/build.js +38 -0
  10. package/cli/commands/dev.js +18 -0
  11. package/cli/commands/doctor.js +197 -0
  12. package/cli/commands/figma-sync.js +48 -0
  13. package/cli/commands/figma-tokens.js +55 -0
  14. package/cli/commands/generate.js +26 -0
  15. package/cli/commands/init.js +116 -0
  16. package/cli/commands/lint.js +209 -0
  17. package/cli/commands/mcp.js +530 -0
  18. package/cli/commands/migrate.js +175 -0
  19. package/cli/commands/test.js +38 -0
  20. package/cli/commands/validate.js +354 -0
  21. package/cli/index.js +113 -0
  22. package/package.json +95 -0
  23. package/reference/atoms.md +517 -0
  24. package/reference/behaviors.md +384 -0
  25. package/reference/build-tooling.md +275 -0
  26. package/reference/color-guidelines.md +965 -0
  27. package/reference/component-lifecycle.md +137 -0
  28. package/reference/compound-spacing.md +95 -0
  29. package/reference/decantation-process.md +499 -0
  30. package/reference/dev-server-routes.md +93 -0
  31. package/reference/form-system.md +253 -0
  32. package/reference/i18n.md +336 -0
  33. package/reference/icons.md +576 -0
  34. package/reference/llm-primer.md +953 -0
  35. package/reference/plugins.md +252 -0
  36. package/reference/registry-consumption.md +76 -0
  37. package/reference/router.md +217 -0
  38. package/reference/shells.md +116 -0
  39. package/reference/spatial-guidelines.md +541 -0
  40. package/reference/ssr.md +234 -0
  41. package/reference/state-data.md +215 -0
  42. package/reference/state-patterns.md +166 -0
  43. package/reference/state.md +194 -0
  44. package/reference/style-system.md +110 -0
  45. package/reference/tokens.md +460 -0
  46. package/src/app.js +19 -0
  47. package/src/chart/_animate.js +266 -0
  48. package/src/chart/_base.js +109 -0
  49. package/src/chart/_data.js +209 -0
  50. package/src/chart/_format.js +106 -0
  51. package/src/chart/_interact.js +364 -0
  52. package/src/chart/_palette.js +105 -0
  53. package/src/chart/_renderer.js +52 -0
  54. package/src/chart/_scene.js +262 -0
  55. package/src/chart/_shared.js +371 -0
  56. package/src/chart/index.js +637 -0
  57. package/src/chart/layouts/_layout-base.js +328 -0
  58. package/src/chart/layouts/cartesian.js +148 -0
  59. package/src/chart/layouts/hierarchy.js +562 -0
  60. package/src/chart/layouts/polar.js +101 -0
  61. package/src/chart/renderers/canvas.js +179 -0
  62. package/src/chart/renderers/svg.js +256 -0
  63. package/src/chart/renderers/webgpu.js +715 -0
  64. package/src/chart/types/_type-base.js +26 -0
  65. package/src/chart/types/area.js +134 -0
  66. package/src/chart/types/bar.js +173 -0
  67. package/src/chart/types/box-plot.js +125 -0
  68. package/src/chart/types/bubble.js +63 -0
  69. package/src/chart/types/candlestick.js +115 -0
  70. package/src/chart/types/chord.js +85 -0
  71. package/src/chart/types/combination.js +108 -0
  72. package/src/chart/types/funnel.js +68 -0
  73. package/src/chart/types/gauge.js +163 -0
  74. package/src/chart/types/heatmap.js +98 -0
  75. package/src/chart/types/histogram.js +71 -0
  76. package/src/chart/types/line.js +111 -0
  77. package/src/chart/types/org-chart.js +93 -0
  78. package/src/chart/types/pie.js +81 -0
  79. package/src/chart/types/radar.js +96 -0
  80. package/src/chart/types/radial.js +68 -0
  81. package/src/chart/types/range-area.js +55 -0
  82. package/src/chart/types/range-bar.js +61 -0
  83. package/src/chart/types/sankey.js +73 -0
  84. package/src/chart/types/scatter.js +66 -0
  85. package/src/chart/types/sparkline.js +81 -0
  86. package/src/chart/types/sunburst.js +69 -0
  87. package/src/chart/types/swimlane.js +88 -0
  88. package/src/chart/types/treemap.js +62 -0
  89. package/src/chart/types/waterfall.js +100 -0
  90. package/src/components/_base.js +1658 -0
  91. package/src/components/_behaviors.js +1140 -0
  92. package/src/components/_primitives.js +534 -0
  93. package/src/components/_qr-encoder.js +539 -0
  94. package/src/components/accordion.js +207 -0
  95. package/src/components/affix.js +62 -0
  96. package/src/components/alert-dialog.js +75 -0
  97. package/src/components/alert.js +47 -0
  98. package/src/components/aspect-ratio.js +24 -0
  99. package/src/components/avatar-group.js +55 -0
  100. package/src/components/avatar.js +38 -0
  101. package/src/components/back-top.js +75 -0
  102. package/src/components/badge.js +74 -0
  103. package/src/components/banner.js +68 -0
  104. package/src/components/breadcrumb.js +162 -0
  105. package/src/components/button.js +115 -0
  106. package/src/components/calendar.js +131 -0
  107. package/src/components/card.js +192 -0
  108. package/src/components/carousel.js +98 -0
  109. package/src/components/cascader.js +261 -0
  110. package/src/components/checkbox.js +80 -0
  111. package/src/components/chip.js +81 -0
  112. package/src/components/code-block.js +82 -0
  113. package/src/components/collapsible.js +50 -0
  114. package/src/components/color-palette.js +438 -0
  115. package/src/components/color-picker.js +314 -0
  116. package/src/components/combobox.js +181 -0
  117. package/src/components/command.js +174 -0
  118. package/src/components/comment.js +206 -0
  119. package/src/components/context-menu.js +76 -0
  120. package/src/components/data-table.js +724 -0
  121. package/src/components/date-picker.js +217 -0
  122. package/src/components/date-range-picker.js +244 -0
  123. package/src/components/datetime-picker.js +271 -0
  124. package/src/components/descriptions.js +68 -0
  125. package/src/components/drawer.js +179 -0
  126. package/src/components/dropdown.js +88 -0
  127. package/src/components/empty.js +41 -0
  128. package/src/components/float-button.js +90 -0
  129. package/src/components/form.js +106 -0
  130. package/src/components/hover-card.js +49 -0
  131. package/src/components/icon.js +87 -0
  132. package/src/components/image.js +97 -0
  133. package/src/components/index.js +117 -0
  134. package/src/components/input-group.js +75 -0
  135. package/src/components/input-number.js +155 -0
  136. package/src/components/input-otp.js +178 -0
  137. package/src/components/input.js +91 -0
  138. package/src/components/kbd.js +36 -0
  139. package/src/components/label.js +25 -0
  140. package/src/components/list.js +118 -0
  141. package/src/components/masked-input.js +236 -0
  142. package/src/components/mentions.js +165 -0
  143. package/src/components/menu.js +259 -0
  144. package/src/components/message.js +80 -0
  145. package/src/components/modal.js +147 -0
  146. package/src/components/navigation-menu.js +166 -0
  147. package/src/components/notification.js +84 -0
  148. package/src/components/pagination.js +104 -0
  149. package/src/components/placeholder.js +132 -0
  150. package/src/components/popconfirm.js +70 -0
  151. package/src/components/popover.js +58 -0
  152. package/src/components/progress.js +61 -0
  153. package/src/components/qrcode.js +251 -0
  154. package/src/components/radiogroup.js +120 -0
  155. package/src/components/range-slider.js +176 -0
  156. package/src/components/rate.js +186 -0
  157. package/src/components/resizable.js +83 -0
  158. package/src/components/result.js +57 -0
  159. package/src/components/scroll-area.js +43 -0
  160. package/src/components/segmented.js +97 -0
  161. package/src/components/select.js +165 -0
  162. package/src/components/separator.js +31 -0
  163. package/src/components/shell.js +407 -0
  164. package/src/components/skeleton.js +39 -0
  165. package/src/components/slider.js +141 -0
  166. package/src/components/sortable-list.js +176 -0
  167. package/src/components/space.js +42 -0
  168. package/src/components/spinner.js +112 -0
  169. package/src/components/splitter.js +147 -0
  170. package/src/components/statistic.js +136 -0
  171. package/src/components/steps.js +99 -0
  172. package/src/components/switch.js +95 -0
  173. package/src/components/table.js +44 -0
  174. package/src/components/tabs.js +216 -0
  175. package/src/components/tag.js +115 -0
  176. package/src/components/textarea.js +82 -0
  177. package/src/components/time-picker.js +153 -0
  178. package/src/components/time-range-picker.js +170 -0
  179. package/src/components/timeline.js +226 -0
  180. package/src/components/toast.js +71 -0
  181. package/src/components/toggle.js +213 -0
  182. package/src/components/tooltip.js +57 -0
  183. package/src/components/tour.js +159 -0
  184. package/src/components/transfer.js +163 -0
  185. package/src/components/tree-select.js +274 -0
  186. package/src/components/tree.js +141 -0
  187. package/src/components/typography.js +136 -0
  188. package/src/components/upload.js +118 -0
  189. package/src/components/visually-hidden.js +20 -0
  190. package/src/components/watermark.js +124 -0
  191. package/src/core/index.js +539 -0
  192. package/src/core/lifecycle.js +69 -0
  193. package/src/css/atoms.js +651 -0
  194. package/src/css/components.js +940 -0
  195. package/src/css/derive.js +1296 -0
  196. package/src/css/index.js +265 -0
  197. package/src/css/runtime.js +268 -0
  198. package/src/css/styles/addons/bioluminescent.js +93 -0
  199. package/src/css/styles/addons/clay.js +70 -0
  200. package/src/css/styles/addons/clean.js +57 -0
  201. package/src/css/styles/addons/command-center.js +143 -0
  202. package/src/css/styles/addons/dopamine.js +83 -0
  203. package/src/css/styles/addons/editorial.js +80 -0
  204. package/src/css/styles/addons/glassmorphism.js +99 -0
  205. package/src/css/styles/addons/liquid-glass.js +105 -0
  206. package/src/css/styles/addons/prismatic.js +100 -0
  207. package/src/css/styles/addons/retro.js +63 -0
  208. package/src/css/styles/auradecantism.js +96 -0
  209. package/src/css/theme-registry.js +444 -0
  210. package/src/data/entity.js +281 -0
  211. package/src/data/index.js +13 -0
  212. package/src/data/persist.js +225 -0
  213. package/src/data/query.js +839 -0
  214. package/src/data/realtime.js +299 -0
  215. package/src/data/url.js +177 -0
  216. package/src/data/worker.js +134 -0
  217. package/src/explorer/archetypes.js +243 -0
  218. package/src/explorer/atoms.js +228 -0
  219. package/src/explorer/charts.js +497 -0
  220. package/src/explorer/components.js +129 -0
  221. package/src/explorer/foundations.js +949 -0
  222. package/src/explorer/icons.js +178 -0
  223. package/src/explorer/patterns.js +247 -0
  224. package/src/explorer/recipes.js +194 -0
  225. package/src/explorer/shared/pattern-examples.js +1337 -0
  226. package/src/explorer/shared/showcase-renderer.js +958 -0
  227. package/src/explorer/shared/spec-table.js +41 -0
  228. package/src/explorer/shared/usage-links.js +87 -0
  229. package/src/explorer/shell-config.js +10 -0
  230. package/src/explorer/shells.js +551 -0
  231. package/src/explorer/styles.js +161 -0
  232. package/src/explorer/tokens.js +262 -0
  233. package/src/explorer/tools.js +525 -0
  234. package/src/form/index.js +804 -0
  235. package/src/i18n/index.js +251 -0
  236. package/src/icons/essential.js +479 -0
  237. package/src/icons/index.js +53 -0
  238. package/src/plugins/index.js +282 -0
  239. package/src/registry/archetypes/content-site.json +71 -0
  240. package/src/registry/archetypes/docs-explorer.json +23 -0
  241. package/src/registry/archetypes/ecommerce.json +104 -0
  242. package/src/registry/archetypes/financial-dashboard.json +77 -0
  243. package/src/registry/archetypes/index.json +41 -0
  244. package/src/registry/archetypes/portfolio.json +82 -0
  245. package/src/registry/archetypes/recipe-community.json +159 -0
  246. package/src/registry/archetypes/saas-dashboard.json +86 -0
  247. package/src/registry/architect/cross-cutting.json +45 -0
  248. package/src/registry/architect/domains/ecommerce.json +294 -0
  249. package/src/registry/architect/domains/financial-services.json +302 -0
  250. package/src/registry/architect/index.json +26 -0
  251. package/src/registry/architect/traits.json +379 -0
  252. package/src/registry/atoms.json +16 -0
  253. package/src/registry/chart-showcase.json +160 -0
  254. package/src/registry/chart.json +136 -0
  255. package/src/registry/components.json +8616 -0
  256. package/src/registry/core.json +216 -0
  257. package/src/registry/css.json +319 -0
  258. package/src/registry/data.json +135 -0
  259. package/src/registry/foundations.json +11 -0
  260. package/src/registry/icons.json +463 -0
  261. package/src/registry/index.json +101 -0
  262. package/src/registry/patterns/activity-feed.json +37 -0
  263. package/src/registry/patterns/article-content.json +27 -0
  264. package/src/registry/patterns/auth-form.json +37 -0
  265. package/src/registry/patterns/author-card.json +20 -0
  266. package/src/registry/patterns/card-grid.json +127 -0
  267. package/src/registry/patterns/category-nav.json +26 -0
  268. package/src/registry/patterns/chart-grid.json +36 -0
  269. package/src/registry/patterns/chat-interface.json +37 -0
  270. package/src/registry/patterns/checklist-card.json +55 -0
  271. package/src/registry/patterns/comparison-panel.json +27 -0
  272. package/src/registry/patterns/component-showcase.json +24 -0
  273. package/src/registry/patterns/contact-form.json +31 -0
  274. package/src/registry/patterns/cta-section.json +20 -0
  275. package/src/registry/patterns/data-table.json +37 -0
  276. package/src/registry/patterns/detail-header.json +83 -0
  277. package/src/registry/patterns/detail-panel.json +27 -0
  278. package/src/registry/patterns/explorer-shell.json +22 -0
  279. package/src/registry/patterns/filter-bar.json +33 -0
  280. package/src/registry/patterns/filter-sidebar.json +27 -0
  281. package/src/registry/patterns/form-sections.json +110 -0
  282. package/src/registry/patterns/goal-tracker.json +27 -0
  283. package/src/registry/patterns/hero.json +107 -0
  284. package/src/registry/patterns/index.json +47 -0
  285. package/src/registry/patterns/kpi-grid.json +36 -0
  286. package/src/registry/patterns/media-gallery.json +20 -0
  287. package/src/registry/patterns/order-history.json +20 -0
  288. package/src/registry/patterns/pagination.json +19 -0
  289. package/src/registry/patterns/photo-to-recipe.json +36 -0
  290. package/src/registry/patterns/pipeline-tracker.json +28 -0
  291. package/src/registry/patterns/post-list.json +27 -0
  292. package/src/registry/patterns/pricing-table.json +32 -0
  293. package/src/registry/patterns/scorecard.json +28 -0
  294. package/src/registry/patterns/search-bar.json +20 -0
  295. package/src/registry/patterns/specimen-grid.json +19 -0
  296. package/src/registry/patterns/stat-card.json +55 -0
  297. package/src/registry/patterns/stats-bar.json +55 -0
  298. package/src/registry/patterns/steps-card.json +55 -0
  299. package/src/registry/patterns/table-of-contents.json +19 -0
  300. package/src/registry/patterns/testimonials.json +21 -0
  301. package/src/registry/patterns/timeline.json +27 -0
  302. package/src/registry/patterns/token-inspector.json +21 -0
  303. package/src/registry/patterns/wizard.json +27 -0
  304. package/src/registry/recipe-auradecantism.json +69 -0
  305. package/src/registry/recipe-clean.json +65 -0
  306. package/src/registry/recipe-command-center.json +78 -0
  307. package/src/registry/router.json +73 -0
  308. package/src/registry/schema/README.md +197 -0
  309. package/src/registry/skeletons.json +259 -0
  310. package/src/registry/state.json +137 -0
  311. package/src/registry/tokens.json +40 -0
  312. package/src/router/hash.js +17 -0
  313. package/src/router/history.js +18 -0
  314. package/src/router/index.js +598 -0
  315. package/src/ssr/index.js +922 -0
  316. package/src/state/arrays.js +181 -0
  317. package/src/state/devtools.js +647 -0
  318. package/src/state/index.js +498 -0
  319. package/src/state/middleware.js +288 -0
  320. package/src/state/scheduler.js +206 -0
  321. package/src/state/store.js +300 -0
  322. package/src/tags/index.js +19 -0
  323. package/src/tannins/auth.js +396 -0
  324. package/src/test/dom.js +352 -0
  325. package/src/test/index.js +62 -0
  326. package/src/test/state.js +306 -0
  327. package/tools/a11y-audit.js +487 -0
  328. package/tools/analyzer.js +315 -0
  329. package/tools/audit.js +706 -0
  330. package/tools/builder.js +1422 -0
  331. package/tools/css-extract.js +188 -0
  332. package/tools/dev-server.js +316 -0
  333. package/tools/dts-gen.js +1260 -0
  334. package/tools/figma-components.js +329 -0
  335. package/tools/figma-patterns.js +516 -0
  336. package/tools/figma-plugin/code.js +453 -0
  337. package/tools/figma-plugin/manifest.json +14 -0
  338. package/tools/figma-plugin/ui.html +268 -0
  339. package/tools/figma-render.js +293 -0
  340. package/tools/figma-tokens.js +712 -0
  341. package/tools/figma-upload.js +318 -0
  342. package/tools/generate.js +738 -0
  343. package/tools/icons.js +133 -0
  344. package/tools/init-templates.js +265 -0
  345. package/tools/install-hooks.sh +5 -0
  346. package/tools/migrations/0.5.0.js +53 -0
  347. package/tools/migrations/0.6.0.js +95 -0
  348. package/tools/minify.js +170 -0
  349. package/tools/pre-commit +4 -0
  350. package/tools/registry.js +662 -0
  351. package/tools/reset-playground.js +61 -0
  352. package/tools/starter-templates/content-site/app.js +49 -0
  353. package/tools/starter-templates/content-site/essence.js +19 -0
  354. package/tools/starter-templates/content-site/pages.js +31 -0
  355. package/tools/starter-templates/ecommerce/app.js +50 -0
  356. package/tools/starter-templates/ecommerce/essence.js +19 -0
  357. package/tools/starter-templates/ecommerce/pages.js +31 -0
  358. package/tools/starter-templates/landing-page/app.js +38 -0
  359. package/tools/starter-templates/landing-page/essence.js +18 -0
  360. package/tools/starter-templates/landing-page/pages.js +21 -0
  361. package/tools/starter-templates/portfolio/app.js +45 -0
  362. package/tools/starter-templates/portfolio/essence.js +19 -0
  363. package/tools/starter-templates/portfolio/pages.js +33 -0
  364. package/tools/starter-templates/saas-dashboard/app.js +70 -0
  365. package/tools/starter-templates/saas-dashboard/essence.js +19 -0
  366. package/tools/starter-templates/saas-dashboard/pages.js +31 -0
  367. package/tools/verify-pack.js +203 -0
  368. package/types/chart.d.ts +77 -0
  369. package/types/components.d.ts +587 -0
  370. package/types/core.d.ts +89 -0
  371. package/types/css.d.ts +149 -0
  372. package/types/data.d.ts +238 -0
  373. package/types/form.d.ts +164 -0
  374. package/types/i18n.d.ts +51 -0
  375. package/types/icons.d.ts +27 -0
  376. package/types/index.d.ts +13 -0
  377. package/types/router.d.ts +116 -0
  378. package/types/ssr.d.ts +102 -0
  379. package/types/state.d.ts +83 -0
  380. package/types/tags.d.ts +62 -0
  381. package/types/tannins.d.ts +63 -0
  382. package/types/test.d.ts +48 -0
@@ -0,0 +1,953 @@
1
+ # Decantr LLM Primer
2
+
3
+ Single-file reference for code generation. Covers the 20% that handles 80% of scaffolding needs.
4
+
5
+ ---
6
+
7
+ ## 1. All Imports
8
+
9
+ ```js
10
+ import { tags } from 'decantr/tags'; // HTML elements
11
+ import { h, text, cond, list, mount, onMount, onDestroy } from 'decantr/core';
12
+ import { createSignal, createEffect, createMemo, createStore, batch } from 'decantr/state';
13
+ import { createRouter, link, navigate, useRoute, back, forward, isNavigating } from 'decantr/router';
14
+ import { css, setStyle, setMode } from 'decantr/css';
15
+ import { Button, Input, Card, Modal, Tabs, Select, ... } from 'decantr/components';
16
+ import { Chart, Sparkline, chartSpec, createStream } from 'decantr/chart';
17
+ ```
18
+
19
+ **Usage pattern:**
20
+ ```js
21
+ const { div, span, h1, p, a, nav, main, header, aside, section, ul, li } = tags;
22
+ div({ class: css('_flex _col _gap4 _p6') }, child1, child2);
23
+ ```
24
+
25
+ ---
26
+
27
+ ## 2. Common Atoms (with Tailwind equivalences)
28
+
29
+ Both naming styles work. Decantr terse names are canonical.
30
+
31
+ | Decantr Atom | Tailwind Alias | CSS Output |
32
+ |-------------|---------------|-----------|
33
+ | `_flex` | — | `display:flex` |
34
+ | `_grid` | — | `display:grid` |
35
+ | `_col` | `_flex-col` | `flex-direction:column` |
36
+ | `_row` | `_flex-row` | `flex-direction:row` |
37
+ | `_aic` | `_items-center` | `align-items:center` |
38
+ | `_aifs` | `_items-start` | `align-items:flex-start` |
39
+ | `_aife` | `_items-end` | `align-items:flex-end` |
40
+ | `_ais` | `_items-stretch` | `align-items:stretch` |
41
+ | `_aibs` | `_items-baseline` | `align-items:baseline` |
42
+ | `_jcc` | `_justify-center` | `justify-content:center` |
43
+ | `_jcsb` | `_justify-between` | `justify-content:space-between` |
44
+ | `_jcsa` | `_justify-around` | `justify-content:space-around` |
45
+ | `_jcse` | `_justify-evenly` | `justify-content:space-evenly` |
46
+ | `_jcfs` | `_justify-start` | `justify-content:flex-start` |
47
+ | `_jcfe` | `_justify-end` | `justify-content:flex-end` |
48
+ | `_center` | — | `align-items:center;justify-content:center` |
49
+ | `_wrap` | `_flex-wrap` | `flex-wrap:wrap` |
50
+ | `_flex1` | `_flex-1` | `flex:1 1 0%` |
51
+ | `_gap4` | `_gap-4` | `gap:1rem` |
52
+ | `_p4` | `_p-4` | `padding:1rem` |
53
+ | `_px6` | `_px-6` | `padding-inline:1.5rem` |
54
+ | `_py3` | `_py-3` | `padding-block:0.75rem` |
55
+ | `_m0` | `_m-0` | `margin:0` |
56
+ | `_mt4` | — | `margin-top:1rem` |
57
+ | `_mxa` | — | `margin-inline:auto` |
58
+ | `_gc2` | `_grid-cols-2` | `grid-template-columns:repeat(2,minmax(0,1fr))` |
59
+ | `_gc4` | `_grid-cols-4` | `grid-template-columns:repeat(4,minmax(0,1fr))` |
60
+ | `_span2` | `_col-span-2` | `grid-column:span 2/span 2` |
61
+ | `_wfull` | `_w-full` | `width:100%` |
62
+ | `_hfull` | `_h-full` | `height:100%` |
63
+ | `_tc` | `_text-center` | `text-align:center` |
64
+ | `_bold` | `_font-bold` | `font-weight:700` |
65
+ | `_fgfg` | — | `color:var(--d-fg)` |
66
+ | `_fgmuted` | `_text-muted` | `color:var(--d-muted)` |
67
+ | `_fgmutedfg` | `_text-muted-foreground` | `color:var(--d-muted-fg)` |
68
+ | `_bgmuted` | `_bg-muted` | `background:var(--d-muted)` |
69
+ | `_bgprimary` | `_bg-primary` | `background:var(--d-primary)` |
70
+ | `_b1` | `_border` | `border:1px solid` |
71
+ | `_r4` | `_rounded-lg` | `border-radius:1rem` |
72
+ | `_rfull` | `_rounded-full` | `border-radius:9999px` |
73
+ | `_shadow` | `_shadow` | `box-shadow:0 1px 3px ...` |
74
+ | `_shadowmd` | `_shadow-md` | `box-shadow:0 4px 6px ...` |
75
+ | `_trans` | `_transition` | `transition:all 0.2s ease` |
76
+ | `_pointer` | `_cursor-pointer` | `cursor:pointer` |
77
+ | `_ohidden` | `_overflow-hidden` | `overflow:hidden` |
78
+ | `_relative` | `_relative` | `position:relative` |
79
+ | `_absolute` | `_absolute` | `position:absolute` |
80
+ | `_none` | — | `display:none` |
81
+ | `_truncate` | `_truncate` | `overflow:hidden;text-overflow:ellipsis;white-space:nowrap` |
82
+ | `_nounder` | `_no-underline` | `text-decoration:none` |
83
+ | `_borderB` | — | `border-bottom:1px solid var(--d-border)` |
84
+ | `_bcborder` | `_border-border` | `border-color:var(--d-border)` |
85
+
86
+ ### Spacing Scale
87
+ `0`=0, `1`=0.25rem, `2`=0.5rem, `3`=0.75rem, `4`=1rem, `5`=1.25rem, `6`=1.5rem, `8`=2rem, `10`=2.5rem, `12`=3rem, `16`=4rem, `20`=5rem, `24`=6rem
88
+
89
+ > For spacing decision rules beyond defaults, see `reference/spatial-guidelines.md`.
90
+
91
+ ### Opacity Modifiers
92
+ Append `/N` to any semantic color atom for alpha transparency (uses `color-mix()`):
93
+ `_bgprimary/50` (50% opacity bg), `_fgaccent/30` (30% opacity text), `_bcborder/80` (80% opacity border).
94
+ Valid opacities: 5, 10, 15, 20, 25, 30, 40, 50, 60, 70, 75, 80, 90, 95. Works with responsive prefixes: `_sm:bgprimary/20`.
95
+
96
+ ### Arbitrary Transitions
97
+ Use `_trans[...]` for custom transitions: `_trans[color_0.15s_ease]` (underscores become spaces).
98
+ Standard shortcuts: `_trans` (all 0.2s ease), `_transfast` (0.1s), `_transslow` (0.4s), `_transnone`.
99
+
100
+ ### Custom values
101
+ Use bracket syntax: `_w[300px]`, `_h[100vh]`, `_p[2px]`, `_bg[#ff0000]`, `_grid-template-columns[240px_1fr]`
102
+
103
+ ### Typography Presets
104
+ `_heading1`–`_heading6`, `_body`, `_bodylg`, `_caption`, `_label`, `_overline`
105
+
106
+ Font sizes: `_textxs` (0.625rem) → `_textsm` → `_textbase` → `_textmd` → `_textlg` → `_textxl` → `_text2xl` → `_text3xl` → `_text4xl` (2.5rem)
107
+
108
+ ### Semantic Colors
109
+ | Role | Background | Foreground | Border |
110
+ |------|-----------|-----------|--------|
111
+ | Primary | `_bgprimary` | `_fgprimary` | `_bcprimary` |
112
+ | Success | `_bgsuccess` | `_fgsuccess` | `_bcsuccess` |
113
+ | Warning | `_bgwarning` | `_fgwarning` | `_bcwarning` |
114
+ | Error | `_bgerror` | `_fgerror` | `_bcerror` |
115
+ | Subtle | `_bgprimarysub` | `_fgprimarysub` | — |
116
+
117
+ ---
118
+
119
+ ## 3. Top 15 Component Signatures
120
+
121
+ ```js
122
+ // Button
123
+ Button({ variant: 'primary'|'outline'|'ghost'|'destructive'|'link', size: 'sm'|'md'|'lg',
124
+ disabled, loading, onclick, class }, ...children)
125
+
126
+ // Input
127
+ Input({ label, type, placeholder, value, onchange, oninput, disabled, class })
128
+
129
+ // Card (with sub-components)
130
+ Card({ title, extra, hoverable, bordered, loading, size, type, cover, actions, class },
131
+ Card.Header({ extra }, ...), Card.Body({}, ...), Card.Footer({}, ...),
132
+ Card.Cover({}, img), Card.Meta({ avatar, title, description }),
133
+ Card.Grid({ hoverable }, ...), Card.Actions({}, ...)
134
+ )
135
+
136
+ // Select
137
+ Select({ label, value, onchange, options: [{ label, value }], placeholder, class })
138
+
139
+ // Modal
140
+ Modal({ visible: signal, onClose: fn, class }, ...children)
141
+
142
+ // Tabs
143
+ Tabs({ items: [{ label, content: () => node }], class })
144
+
145
+ // DataTable
146
+ DataTable({ columns: [{ key, label, sortable, render }], data, sortable, paginate, pageSize, class })
147
+
148
+ // Statistic
149
+ Statistic({ label, value, prefix, suffix, trend: 'up'|'down', trendValue, icon, class })
150
+
151
+ // Badge
152
+ Badge({ variant: 'default'|'primary'|'success'|'warning'|'error', size }, ...children)
153
+
154
+ // Alert
155
+ Alert({ variant: 'default'|'info'|'success'|'warning'|'error' }, ...children)
156
+
157
+ // Dropdown
158
+ Dropdown({ trigger, items: [{ label, onclick }], class })
159
+
160
+ // Breadcrumb
161
+ Breadcrumb({ items: [{ label, href }] })
162
+
163
+ // Pagination
164
+ Pagination({ current, total, onChange })
165
+
166
+ // Progress
167
+ Progress({ value: 0-100, class })
168
+
169
+ // Skeleton
170
+ Skeleton({ width, height, class }) // Loading placeholder
171
+
172
+ // Avatar
173
+ Avatar({ src, name, size: 'sm'|'md'|'lg' })
174
+
175
+ // Separator
176
+ Separator() // Horizontal divider
177
+
178
+ // Chip
179
+ Chip({ label, onClose, variant })
180
+
181
+ // icon
182
+ icon('icon-name') // Returns SVG icon element
183
+ ```
184
+
185
+ ---
186
+
187
+ ## 4. Chart API
188
+
189
+ ```js
190
+ import { Chart, Sparkline } from 'decantr/chart';
191
+
192
+ // Full chart
193
+ Chart({
194
+ type: 'line'|'bar'|'area'|'pie'|'scatter'|'bubble'|'histogram'|'funnel'|
195
+ 'radar'|'gauge'|'heatmap'|'treemap'|'candlestick'|'waterfall'|
196
+ 'box-plot'|'range-bar'|'range-area'|'radial'|'sunburst'|'sankey'|
197
+ 'chord'|'swimlane'|'org-chart'|'combination',
198
+ data: arrayOrSignal,
199
+ x: 'fieldName', // x-axis data key
200
+ y: 'fieldName', // y-axis data key (or array for multi-series)
201
+ title: 'Chart Title',
202
+ height: 300, // pixels
203
+ live: false, // enable streaming updates
204
+ })
205
+
206
+ // Inline sparkline (no axes, no labels)
207
+ Sparkline({ data: [1, 4, 2, 8, 3], height: 32 })
208
+ ```
209
+
210
+ ### Working Examples
211
+
212
+ **Line chart:**
213
+ ```js
214
+ Chart({
215
+ type: 'line',
216
+ data: [
217
+ { date: '2024-01', revenue: 4200 },
218
+ { date: '2024-02', revenue: 5100 },
219
+ { date: '2024-03', revenue: 4800 },
220
+ ],
221
+ x: 'date', y: 'revenue', title: 'Monthly Revenue', height: 300
222
+ })
223
+ ```
224
+
225
+ **Bar chart:**
226
+ ```js
227
+ Chart({
228
+ type: 'bar',
229
+ data: [
230
+ { category: 'Electronics', sales: 1200 },
231
+ { category: 'Clothing', sales: 800 },
232
+ { category: 'Books', sales: 600 },
233
+ ],
234
+ x: 'category', y: 'sales', title: 'Sales by Category', height: 280
235
+ })
236
+ ```
237
+
238
+ **Pie chart:**
239
+ ```js
240
+ Chart({
241
+ type: 'pie',
242
+ data: [
243
+ { name: 'Desktop', value: 65 },
244
+ { name: 'Mobile', value: 30 },
245
+ { name: 'Tablet', value: 5 },
246
+ ],
247
+ x: 'name', y: 'value', title: 'Traffic Sources', height: 280
248
+ })
249
+ ```
250
+
251
+ ---
252
+
253
+ ## 5. Skeleton Code (5 layouts)
254
+
255
+ ### sidebar-main
256
+ ```js
257
+ function SidebarMain({ nav, children }) {
258
+ const { div, aside, main, header, span } = tags;
259
+ const [collapsed, setCollapsed] = createSignal(false);
260
+
261
+ return div({ class: css('_grid _h[100vh]'),
262
+ style: () => `grid-template-columns:${collapsed() ? '64px' : '240px'} 1fr;grid-template-rows:auto 1fr` },
263
+ aside({ class: css('_flex _col _gap1 _p3 _bgmuted _overflow[auto] _borderR'), style: 'grid-row:1/3' },
264
+ div({ class: css('_flex _aic _jcsb _mb4') },
265
+ cond(() => !collapsed(), () => span({ class: css('_heading5') }, 'App')),
266
+ Button({ variant: 'ghost', size: 'sm', onclick: () => setCollapsed(!collapsed()) }, icon('panel-left'))
267
+ ),
268
+ ...nav.map(item =>
269
+ link({ href: item.href, class: css('_flex _aic _gap2 _p2 _px3 _r2 _trans _fgfg') },
270
+ icon(item.icon), cond(() => !collapsed(), () => text(item.label))
271
+ )
272
+ )
273
+ ),
274
+ header({ class: css('_flex _aic _jcsb _px6 _py3 _borderB') },
275
+ span({ class: css('_heading4') }, 'Page Title')
276
+ ),
277
+ main({ class: css('_flex _col _gap4 _p6 _overflow[auto] _flex1') }, ...children)
278
+ );
279
+ }
280
+ ```
281
+
282
+ ### Children Rules
283
+
284
+ > **Function children = reactive TEXT only.** When an element receives a function as a child, the framework calls `String(fn())` and creates a reactive text node. It does **not** expect DOM elements from that function.
285
+
286
+ Use `cond()` for conditional DOM elements:
287
+
288
+ ```js
289
+ // WRONG — returns "[object HTMLSpanElement]"
290
+ div({}, () => show() ? span({}, 'Hello') : null)
291
+
292
+ // CORRECT — use cond() for conditional DOM
293
+ div({}, cond(() => show(), () => span({}, 'Hello')))
294
+
295
+ // CORRECT — function children work for reactive text
296
+ span({}, () => `Count: ${count()}`)
297
+ ```
298
+
299
+ ### top-nav-main
300
+ ```js
301
+ function TopNavMain({ brand, nav, children }) {
302
+ const { div, header, main, nav: navEl } = tags;
303
+ return div({ class: css('_flex _col _h[100vh]') },
304
+ header({ class: css('_flex _aic _jcsb _px6 _py3 _borderB _bgbg') },
305
+ link({ href: '/', class: css('_heading5 _nounder _fgfg') }, brand),
306
+ navEl({ class: css('_flex _aic _gap6') },
307
+ ...nav.map(item => link({ href: item.href, class: css('_fgmuted _nounder _trans') }, item.label))
308
+ ),
309
+ div({ class: css('_flex _aic _gap2') },
310
+ Button({ variant: 'ghost', size: 'sm' }, icon('search')),
311
+ Button({ variant: 'ghost', size: 'sm' }, icon('user'))
312
+ )
313
+ ),
314
+ main({ class: css('_flex _col _gap4 _p6 _overflow[auto] _flex1') }, ...children)
315
+ );
316
+ }
317
+ ```
318
+
319
+ ### centered
320
+ ```js
321
+ function Centered({ children, width = '400px' }) {
322
+ const { div } = tags;
323
+ return div({ class: css('_flex _center _h[100vh] _bgmuted _p4') },
324
+ Card({ class: css(`_w[${width}] _mw[100%]`) }, ...children)
325
+ );
326
+ }
327
+ ```
328
+
329
+ ### full-bleed
330
+ ```js
331
+ function FullBleed({ children }) {
332
+ const { div, header, main, nav: navEl } = tags;
333
+ return div({ class: css('_flex _col') },
334
+ header({ class: css('_fixed _top0 _left0 _wfull _flex _aic _jcsb _px8 _py4 _z[40]') },
335
+ link({ href: '/', class: css('_heading5 _nounder _fgfg') }, 'Brand'),
336
+ navEl({ class: css('_flex _aic _gap6') })
337
+ ),
338
+ main({ class: css('_flex _col') }, ...children)
339
+ );
340
+ }
341
+ ```
342
+
343
+ ### minimal-header
344
+ ```js
345
+ function MinimalHeader({ brand, children }) {
346
+ const { div, header, main } = tags;
347
+ return div({ class: css('_flex _col _h[100vh]') },
348
+ header({ class: css('_flex _aic _jcc _py3 _borderB') },
349
+ link({ href: '/', class: css('_flex _aic _gap2 _nounder _fgfg') }, icon('arrow-left'), brand)
350
+ ),
351
+ main({ class: css('_flex _col _aic _overflow[auto] _flex1 _py8') },
352
+ div({ class: css('_w[720px] _mw[100%] _px4 _flex _col _gap6') }, ...children)
353
+ )
354
+ );
355
+ }
356
+ ```
357
+
358
+ ---
359
+
360
+ ## 6. Top 15 Pattern Code Snippets
361
+
362
+ > **Spacing note:** Snippets below use comfortable-density defaults (`_gap4`, `_p4`).
363
+ > Actual spacing must match the project's Clarity profile — see `reference/spatial-guidelines.md` §17.
364
+
365
+ ### kpi-grid
366
+ ```js
367
+ function KpiGrid() {
368
+ const { div, h2 } = tags;
369
+ return div({ class: css('_flex _col _gap4') },
370
+ h2({ class: css('_heading4') }, 'Key Metrics'),
371
+ div({ class: css('_grid _gc4 _gap4') },
372
+ Statistic({ label: 'Revenue', value: 1248500, prefix: '$', trend: 'up', trendValue: '+12.5%' }),
373
+ Statistic({ label: 'Users', value: 84230, trend: 'up', trendValue: '+8.1%' }),
374
+ Statistic({ label: 'Orders', value: 6420, trend: 'down', trendValue: '-2.3%' }),
375
+ Statistic({ label: 'Conversion', value: 3.24, suffix: '%', trend: 'up', trendValue: '+0.5%' })
376
+ )
377
+ );
378
+ }
379
+ ```
380
+
381
+ ### data-table
382
+ ```js
383
+ function DataTablePattern({ columns, data }) {
384
+ const { div } = tags;
385
+ const [search, setSearch] = createSignal('');
386
+ return div({ class: css('_flex _col _gap4') },
387
+ div({ class: css('_flex _gap3 _aic _jcsb') },
388
+ Input({ placeholder: 'Search...', value: search, onchange: e => setSearch(e.target.value) }),
389
+ Button({ variant: 'outline' }, 'Export')
390
+ ),
391
+ DataTable({ columns, data, sortable: true, paginate: true, pageSize: 10 })
392
+ );
393
+ }
394
+ ```
395
+
396
+ ### chart-grid
397
+ ```js
398
+ function ChartGrid({ data }) {
399
+ const { div, h2 } = tags;
400
+ return div({ class: css('_flex _col _gap4') },
401
+ h2({ class: css('_heading4') }, 'Analytics'),
402
+ div({ class: css('_grid _gc2 _gap4') },
403
+ Chart({ type: 'line', data: data.revenue, x: 'date', y: 'value', title: 'Revenue', height: 280 }),
404
+ Chart({ type: 'bar', data: data.orders, x: 'month', y: 'count', title: 'Orders', height: 280 }),
405
+ Chart({ type: 'pie', data: data.categories, x: 'name', y: 'value', title: 'Categories', height: 280 }),
406
+ Chart({ type: 'area', data: data.traffic, x: 'date', y: 'visits', title: 'Traffic', height: 280 })
407
+ )
408
+ );
409
+ }
410
+ ```
411
+
412
+ ### hero
413
+ ```js
414
+ function Hero() {
415
+ const { div, h1, p } = tags;
416
+ return div({ class: css('_flex _col _aic _tc _gap6 _py16 _px6') },
417
+ h1({ class: css('_heading1') }, 'Build Faster, Ship Smarter'),
418
+ p({ class: css('_body _fgmuted _mw[640px]') }, 'The AI-first framework that turns ideas into production-ready apps.'),
419
+ div({ class: css('_flex _gap3') },
420
+ Button({ variant: 'primary', size: 'lg' }, 'Get Started'),
421
+ Button({ variant: 'outline', size: 'lg' }, 'View Source')
422
+ )
423
+ );
424
+ }
425
+ ```
426
+
427
+ ### auth-form
428
+ ```js
429
+ function AuthForm() {
430
+ const { div, h2, p, span } = tags;
431
+ const [email, setEmail] = createSignal('');
432
+ const [password, setPassword] = createSignal('');
433
+ return div({ class: css('_flex _col _aic _jcc _minhscreen _p6') },
434
+ Card({ class: css('_w[400px] _mw[100%]') },
435
+ Card.Header({},
436
+ h2({ class: css('_heading4 _tc') }, 'Sign In'),
437
+ p({ class: css('_fgmuted _tc _mt1') }, 'Enter your credentials')
438
+ ),
439
+ Card.Body({ class: css('_flex _col _gap3') },
440
+ Input({ label: 'Email', type: 'email', value: email, onchange: e => setEmail(e.target.value) }),
441
+ Input({ label: 'Password', type: 'password', value: password, onchange: e => setPassword(e.target.value) }),
442
+ Button({ variant: 'primary', class: css('_wfull _mt2') }, 'Sign In')
443
+ ),
444
+ Card.Footer({ class: css('_tc') },
445
+ span({ class: css('_fgmuted _textsm') }, 'No account? ', link({ href: '/register' }, 'Sign Up'))
446
+ )
447
+ )
448
+ );
449
+ }
450
+ ```
451
+
452
+ ### filter-bar
453
+ ```js
454
+ function FilterBar() {
455
+ const { div } = tags;
456
+ const [search, setSearch] = createSignal('');
457
+ return div({ class: css('_flex _gap3 _aic _wrap _py3') },
458
+ Input({ placeholder: 'Search...', value: search, onchange: e => setSearch(e.target.value), class: css('_w[240px]') }),
459
+ Select({ value: 'all', options: [
460
+ { label: 'All', value: 'all' }, { label: 'Active', value: 'active' }
461
+ ] }),
462
+ DatePicker({ placeholder: 'Date range' }),
463
+ Button({ variant: 'ghost', size: 'sm' }, 'Clear')
464
+ );
465
+ }
466
+ ```
467
+
468
+ ### activity-feed
469
+ ```js
470
+ function ActivityFeed({ items }) {
471
+ const { div, span, h3 } = tags;
472
+ return div({ class: css('_flex _col _gap2 _p4') },
473
+ h3({ class: css('_heading5') }, 'Recent Activity'),
474
+ ...items.map(item =>
475
+ div({ class: css('_flex _gap3 _aic _py2 _borderB') },
476
+ Avatar({ src: item.avatar, name: item.user, size: 'sm' }),
477
+ div({ class: css('_flex _col _flex1') },
478
+ span({ class: css('_textsm') }, span({ class: css('_bold') }, item.user), ` ${item.action}`),
479
+ span({ class: css('_textxs _fgmuted') }, item.time)
480
+ ),
481
+ Badge({ variant: 'default' }, item.type)
482
+ )
483
+ )
484
+ );
485
+ }
486
+ ```
487
+
488
+ ### card-grid (preset: product)
489
+ ```js
490
+ function ProductGrid({ products }) {
491
+ const { div, span, h3 } = tags;
492
+ return div({ class: css('_grid _gcaf280 _gap4 _p4') },
493
+ ...products.map(p =>
494
+ Card({},
495
+ Image({ src: p.image, alt: p.name, class: css('_wfull _h[200px] _object[cover]') }),
496
+ Card.Body({ class: css('_flex _col _gap2') },
497
+ h3({ class: css('_heading5') }, p.name),
498
+ span({ class: css('_fgmuted _textsm') }, p.description),
499
+ div({ class: css('_flex _aic _jcsb _mt2') },
500
+ span({ class: css('_heading4') }, `$${p.price}`),
501
+ Button({ variant: 'primary', size: 'sm' }, 'Add to Cart')
502
+ )
503
+ )
504
+ )
505
+ )
506
+ );
507
+ }
508
+ ```
509
+
510
+ ### pricing-table
511
+ ```js
512
+ function PricingTable({ plans }) {
513
+ const { div, span, h3, ul, li } = tags;
514
+ return div({ class: css('_grid _gc3 _gap6 _p4 _aic') },
515
+ ...plans.map(plan =>
516
+ Card({ class: css(plan.featured ? '_b2 _bcprimary' : '') },
517
+ Card.Header({ class: css('_tc') },
518
+ h3({ class: css('_heading4') }, plan.name),
519
+ div({ class: css('_mt2') },
520
+ span({ class: css('_heading1') }, `$${plan.price}`),
521
+ span({ class: css('_fgmuted') }, '/month')
522
+ )
523
+ ),
524
+ Card.Body({},
525
+ ul({ class: css('_flex _col _gap2') },
526
+ ...plan.features.map(f => li({ class: css('_flex _aic _gap2 _textsm') }, icon('check'), f))
527
+ )
528
+ ),
529
+ Card.Footer({},
530
+ Button({ variant: plan.featured ? 'primary' : 'outline', class: css('_wfull') }, 'Get Started')
531
+ )
532
+ )
533
+ )
534
+ );
535
+ }
536
+ ```
537
+
538
+ ### contact-form
539
+ ```js
540
+ function ContactForm() {
541
+ const { div, h2, p } = tags;
542
+ return Card({ class: css('_mw[600px] _mxa') },
543
+ Card.Header({},
544
+ h2({ class: css('_heading4') }, 'Get in Touch'),
545
+ p({ class: css('_fgmuted _mt1') }, 'We\'ll respond within 24 hours.')
546
+ ),
547
+ Card.Body({ class: css('_flex _col _gap4') },
548
+ div({ class: css('_grid _gc2 _gap4') },
549
+ Input({ label: 'First Name' }), Input({ label: 'Last Name' })
550
+ ),
551
+ Input({ label: 'Email', type: 'email' }),
552
+ Textarea({ label: 'Message', rows: 4 }),
553
+ Button({ variant: 'primary', class: css('_wfull') }, 'Send Message')
554
+ )
555
+ );
556
+ }
557
+ ```
558
+
559
+ ### card-grid (preset: content)
560
+ ```js
561
+ function RecipeCardGrid({ recipes }) {
562
+ const { div, span, h3, img } = tags;
563
+ return div({ class: css('_grid _gc3 _gap6 _p4') },
564
+ ...recipes.map(r =>
565
+ Card({},
566
+ img({ src: r.image, alt: r.title, class: css('_wfull _h[200px] _object[cover]') }),
567
+ Card.Body({ class: css('_flex _col _gap2') },
568
+ h3({ class: css('_heading5') }, r.title),
569
+ span({ class: css('_caption _fgmuted') }, r.description),
570
+ div({ class: css('_flex _aic _gap3 _fgmuted _textsm') },
571
+ span({}, icon('clock'), ` ${r.time} min`),
572
+ span({}, icon('users'), ` ${r.servings} servings`)
573
+ ),
574
+ div({ class: css('_flex _gap1 _wrap') },
575
+ ...r.tags.map(t => Chip({ size: 'sm' }, t))
576
+ ),
577
+ div({ class: css('_flex _aic _jcsb _mt2') },
578
+ div({ class: css('_flex _aic _gap2') },
579
+ Avatar({ src: r.author.avatar, size: 'xs' }),
580
+ span({ class: css('_textsm') }, r.author.name)
581
+ ),
582
+ span({ class: css('_textsm _fgmuted') }, icon('git-fork'), ` ${r.forks}`)
583
+ )
584
+ )
585
+ )
586
+ )
587
+ );
588
+ }
589
+ ```
590
+
591
+ ### form-sections (preset: creation)
592
+ ```js
593
+ function RecipeFormSimple() {
594
+ const { div, h3 } = tags;
595
+ const [ingredients, setIngredients] = createSignal(['']);
596
+ const [instructions, setInstructions] = createSignal(['']);
597
+
598
+ return div({ class: css('_flex _col _gap6 _mw[720px] _mxAuto _p4') },
599
+ Upload({ accept: 'image/*', variant: 'dragger' }, 'Upload recipe photo'),
600
+ Card({},
601
+ Card.Header({}, h3({ class: css('_heading5') }, 'Basic Info')),
602
+ Card.Body({ class: css('_flex _col _gap3') },
603
+ Input({ label: 'Title', placeholder: 'Recipe name' }),
604
+ Textarea({ label: 'Description', rows: 3 })
605
+ )
606
+ ),
607
+ Card({},
608
+ Card.Header({}, h3({ class: css('_heading5') }, 'Details')),
609
+ Card.Body({ class: css('_flex _col _gap3') },
610
+ div({ class: css('_grid _gc3 _gap3') },
611
+ InputNumber({ label: 'Prep (min)', min: 0 }),
612
+ InputNumber({ label: 'Cook (min)', min: 0 }),
613
+ InputNumber({ label: 'Servings', min: 1 })
614
+ ),
615
+ Segmented({ label: 'Difficulty', options: ['Easy', 'Medium', 'Hard'] })
616
+ )
617
+ ),
618
+ Card({},
619
+ Card.Header({}, h3({ class: css('_heading5') }, 'Ingredients')),
620
+ Card.Body({ class: css('_flex _col _gap2') },
621
+ ...ingredients().map((_, i) => Input({ placeholder: `Ingredient ${i + 1}` })),
622
+ Button({ variant: 'outline', size: 'sm' }, icon('plus'), 'Add Ingredient')
623
+ )
624
+ ),
625
+ Card({},
626
+ Card.Header({}, h3({ class: css('_heading5') }, 'Instructions')),
627
+ Card.Body({ class: css('_flex _col _gap2') },
628
+ ...instructions().map((_, i) => Textarea({ placeholder: `Step ${i + 1}`, rows: 2 })),
629
+ Button({ variant: 'outline', size: 'sm' }, icon('plus'), 'Add Step')
630
+ )
631
+ ),
632
+ div({ class: css('_flex _jce _gap3') },
633
+ Button({ variant: 'outline' }, 'Save Draft'),
634
+ Button({ variant: 'primary' }, 'Publish Recipe')
635
+ )
636
+ );
637
+ }
638
+ ```
639
+
640
+ ### chat-interface
641
+ ```js
642
+ function ChatInterface() {
643
+ const { div, p } = tags;
644
+ const [input, setInput] = createSignal('');
645
+ const [messages, setMessages] = createSignal([
646
+ { role: 'assistant', text: 'Hi! I\'m your AI chef assistant.' }
647
+ ]);
648
+ const suggestions = ['What can I make with chicken?', 'Suggest a quick dinner'];
649
+
650
+ const send = () => {
651
+ if (!input()) return;
652
+ setMessages([...messages(), { role: 'user', text: input() }]);
653
+ setInput('');
654
+ };
655
+
656
+ return div({ class: css('_flex _col _h[calc(100vh-200px)] _b1 _r4 _overflow[hidden]') },
657
+ ScrollArea({ class: css('_flex1 _p4') },
658
+ div({ class: css('_flex _col _gap3') },
659
+ ...messages().map(m =>
660
+ div({ class: css(m.role === 'user' ? '_flex _jce' : '_flex _gap2') },
661
+ m.role === 'assistant' ? Avatar({ size: 'sm', fallback: 'AI' }) : null,
662
+ div({ class: css(`_p3 _r4 _mw[70%] ${m.role === 'user' ? '_bgprimary' : '_bgmuted'}`) },
663
+ p({}, m.text)
664
+ )
665
+ )
666
+ )
667
+ )
668
+ ),
669
+ div({ class: css('_flex _gap2 _p3 _borderb') },
670
+ ...suggestions.map(s => Chip({ variant: 'outline', onclick: () => setInput(s) }, s))
671
+ ),
672
+ div({ class: css('_flex _gap2 _p3 _bordert') },
673
+ Input({ placeholder: 'Ask your chef assistant...', value: input, class: css('_flex1') }),
674
+ Button({ variant: 'primary', onclick: send }, icon('send'))
675
+ )
676
+ );
677
+ }
678
+ ```
679
+
680
+ ### photo-to-recipe
681
+ ```js
682
+ function PhotoToRecipe() {
683
+ const { div, h3, p } = tags;
684
+ const [analyzing, setAnalyzing] = createSignal(false);
685
+ const [result, setResult] = createSignal(null);
686
+
687
+ return div({ class: css('_grid _gc2 _gap6 _p4') },
688
+ Card({},
689
+ Card.Header({}, h3({ class: css('_heading5') }, icon('camera'), ' Upload Photo')),
690
+ Card.Body({ class: css('_flex _col _gap4 _aic') },
691
+ Upload({ accept: 'image/*', variant: 'dragger', class: css('_wfull') }, 'Drop a food photo here'),
692
+ Button({ variant: 'primary', class: css('_wfull'), onclick: () => setAnalyzing(true) },
693
+ analyzing() ? Spinner({ size: 'sm' }) : icon('sparkles'),
694
+ analyzing() ? ' Analyzing...' : ' Generate Recipe'
695
+ )
696
+ )
697
+ ),
698
+ Card({},
699
+ Card.Header({}, h3({ class: css('_heading5') }, icon('sparkles'), ' AI Generated Recipe')),
700
+ Card.Body({},
701
+ result()
702
+ ? div({ class: css('_flex _col _gap3') }, h3({ class: css('_heading4') }, result().title))
703
+ : p({ class: css('_fgmuted _tc') }, 'Upload a photo to generate a recipe')
704
+ )
705
+ )
706
+ );
707
+ }
708
+ ```
709
+
710
+ ### card-grid (preset: icon)
711
+ ```js
712
+ function FeatureGrid({ features }) {
713
+ const { div, h3, p } = tags;
714
+ return div({ class: css('_grid _gc3 _gap6 _p4') },
715
+ ...features.map(f =>
716
+ Card({},
717
+ Card.Body({ class: css('_flex _col _gap3 _aic _tc') },
718
+ div({ class: css('_w[48px] _h[48px] _r4 _bgmuted _flex _aic _jcc _fgprimary') }, icon(f.icon)),
719
+ h3({ class: css('_heading5') }, f.title),
720
+ p({ class: css('_caption _fgmuted') }, f.description)
721
+ )
722
+ )
723
+ )
724
+ );
725
+ }
726
+ ```
727
+
728
+ ---
729
+
730
+ ## 7. Recipe Application Guide
731
+
732
+ Recipes overlay decorative classes onto standard components. The pattern is always: **standard component + recipe wrapper classes**.
733
+
734
+ ### Without recipe (plain):
735
+ ```js
736
+ div({ class: css('_b1 _r4 _p4') },
737
+ Statistic({ label: 'Revenue', value: 125000, prefix: '$' })
738
+ )
739
+ ```
740
+
741
+ ### With auradecantism recipe:
742
+ ```js
743
+ div({ class: css('d-glass _r4 _p4') }, // glass panel wrapper
744
+ Statistic({ label: 'Revenue', value: 125000, prefix: '$' }),
745
+ span({ class: css('d-gradient-text _textxs') }, 'ALL TIME HIGH') // gradient accent text
746
+ )
747
+ ```
748
+
749
+ ### With command-center recipe:
750
+ ```js
751
+ div({ class: css('cc-frame-sm cc-glow') }, // beveled frame + glow
752
+ Statistic({ label: 'ACTIVE THREATS', value: 42, icon: 'alert-triangle', class: css('_bg[transparent]') })
753
+ )
754
+ ```
755
+
756
+ ### With clean recipe:
757
+ ```js
758
+ div({ class: css('_b1 _r4 _p4') }, // simple border + radius (tokens handle the rest)
759
+ Statistic({ label: 'Revenue', value: 125000, prefix: '$' })
760
+ )
761
+ ```
762
+
763
+ **Key principle:** Recipes don't change which components you use — they change how you wrap and decorate them.
764
+
765
+ ---
766
+
767
+ ## 8. Styles & Modes
768
+
769
+ ```js
770
+ import { setStyle, setMode, registerStyle } from 'decantr/css';
771
+
772
+ // Core style (always available, no import needed)
773
+ setStyle('auradecantism'); // Default: glass, gradients, vibrant (dark)
774
+
775
+ // Add-on styles (require import + registerStyle before use)
776
+ // Available: clean, retro, glassmorphism, command-center,
777
+ // bioluminescent, clay, dopamine, editorial, liquid-glass, prismatic
778
+ import { clean as cleanStyle } from 'decantr/styles/clean';
779
+ registerStyle(cleanStyle);
780
+ setStyle('clean');
781
+
782
+ // Available modes
783
+ setMode('light');
784
+ setMode('dark');
785
+ setMode('auto'); // Follows system preference
786
+ ```
787
+
788
+ **11 styles total:** `auradecantism` (core, default), `clean`, `retro`, `glassmorphism`, `command-center`, `bioluminescent`, `clay`, `dopamine`, `editorial`, `liquid-glass`, `prismatic` (all add-on).
789
+
790
+ ### Interactive State Atoms (Pseudo-Class Prefixes)
791
+
792
+ ```js
793
+ // Hover, focus, focus-visible, active, focus-within — compose with ANY atom
794
+ css('_h:bgprimary') // background on hover
795
+ css('_f:bcprimary') // border-color on focus
796
+ css('_fv:ring2') // ring on keyboard focus
797
+ css('_a:bgmuted') // background on press
798
+ css('_fw:bcprimary') // border when child focused
799
+ css('_sm:h:bgmuted') // responsive + hover
800
+ css('_h:bgprimary/50') // hover + opacity modifier
801
+ ```
802
+
803
+ ### Ring, Transition, Prose, Divide
804
+
805
+ ```js
806
+ css('_ring2 _ringPrimary') // 2px primary ring
807
+ css('_fv:ring2 _ringAccent') // ring on keyboard focus
808
+ css('_transColors') // smooth color transitions
809
+ css('_prose') // rich text typography
810
+ css('_divideY') // borders between stacked children
811
+ css('_textBalance') // balanced line wrapping
812
+ ```
813
+
814
+ ### Opacity Modifiers
815
+ Works on all semantic color atoms: `_bgprimary/50`, `_fgaccent/30`, `_bcborder/80`
816
+
817
+ ---
818
+
819
+ ## 9. State Management Quick Reference
820
+
821
+ ```js
822
+ // Signal (reactive value)
823
+ const [count, setCount] = createSignal(0);
824
+ count(); // Read (triggers tracking)
825
+ setCount(5); // Write (triggers updates)
826
+ setCount(c => c + 1); // Updater function
827
+
828
+ // Effect (runs when dependencies change)
829
+ createEffect(() => {
830
+ console.log('Count is now:', count()); // auto-tracks count
831
+ });
832
+
833
+ // Memo (cached derived value)
834
+ const doubled = createMemo(() => count() * 2);
835
+
836
+ // Store (reactive object)
837
+ const [user, setUser] = createStore({ name: 'Alice', settings: { theme: 'dark' } });
838
+ setUser('name', 'Bob'); // Update field
839
+ setUser('settings', 'theme', 'light'); // Nested update
840
+ ```
841
+
842
+ ---
843
+
844
+ ## 10. Routing Quick Reference
845
+
846
+ ```js
847
+ const router = createRouter({
848
+ mode: 'hash', // or 'history'
849
+ base: '/app', // optional — for subdirectory deployments
850
+ routes: [
851
+ { path: '/', component: HomePage },
852
+ { path: '/products', component: ProductsPage },
853
+ { path: '/product/:id', component: ProductPage },
854
+ { path: '/admin', component: AdminPage, meta: { requiresAuth: true, title: 'Admin' } },
855
+ { path: '/:404', component: NotFoundPage },
856
+ ],
857
+ beforeEach: (to, from) => {
858
+ // Guard can read to.meta (merged parent→child)
859
+ if (to.meta.requiresAuth && !isLoggedIn()) return '/login';
860
+ },
861
+ });
862
+
863
+ // Navigation listener (subscribe/unsubscribe)
864
+ const unsub = router.onNavigate((to, from) => {
865
+ analytics.track('page_view', { path: to.path, from: from.path });
866
+ });
867
+ // Later: unsub();
868
+
869
+ // Standalone variant:
870
+ // import { onNavigate } from 'decantr/router';
871
+ // const unsub = onNavigate((to, from) => { /* track */ });
872
+
873
+ // Navigation
874
+ navigate('/products');
875
+ navigate('/product/42');
876
+ back(); // history.back() — fires guards
877
+ forward(); // history.forward() — fires guards
878
+
879
+ // Loading indicator for lazy routes
880
+ // isNavigating() → reactive boolean, true while lazy components resolve
881
+
882
+ // Route params + meta
883
+ const route = useRoute();
884
+ route.params.id; // '42'
885
+ route.query.sort; // from ?sort=price
886
+ route.meta; // { requiresAuth: true, title: 'Admin' }
887
+
888
+ // Link component
889
+ link({ href: '/products', class: css('_fgprimary') }, 'Products')
890
+ ```
891
+
892
+ ### Complete SPA Entry Point
893
+
894
+ ```js
895
+ import { mount } from 'decantr/core';
896
+ import { createRouter } from 'decantr/router';
897
+
898
+ const router = createRouter({
899
+ mode: 'hash',
900
+ routes: [
901
+ { path: '/', component: HomePage },
902
+ { path: '/about', component: AboutPage },
903
+ ]
904
+ });
905
+
906
+ // mount(target, renderFn) — target is a DOM element, renderFn returns the root node
907
+ mount(document.getElementById('app'), () => router.outlet());
908
+ ```
909
+
910
+ > `createRouter()` returns a plain object with `{ navigate, outlet, current, path, destroy }`. Call `router.outlet()` to get the DOM element that reactively renders the matched route. Do **not** pass the router object itself to `mount()`.
911
+
912
+ ---
913
+
914
+ ## 11. Common Layout Patterns
915
+
916
+ ### Dashboard page (sidebar-main skeleton)
917
+ ```js
918
+ // Grid: 240px sidebar + 1fr main
919
+ div({ class: css('_grid _h[100vh]'), style: 'grid-template-columns:240px 1fr' },
920
+ aside({ class: css('_bgmuted _borderR _p3 _flex _col _gap1') }, /* nav */),
921
+ main({ class: css('_flex _col _overflow[hidden]') },
922
+ header({ class: css('_flex _aic _jcsb _px6 _py3 _borderB') }, /* title + actions */),
923
+ div({ class: css('_flex _col _gap4 _p6 _overflow[auto] _flex1') }, /* content */)
924
+ )
925
+ )
926
+ ```
927
+
928
+ ### Two-column layout (content + sidebar)
929
+ ```js
930
+ div({ class: css('_grid _gap6'), style: 'grid-template-columns:1fr 300px' },
931
+ main(/* primary content */),
932
+ aside(/* sidebar */)
933
+ )
934
+ ```
935
+
936
+ ### Responsive card grid
937
+ ```js
938
+ div({ class: css('_grid _gcaf280 _gap4 _p4') }, // auto-fit columns, min 280px
939
+ ...items.map(item => Card(/* ... */))
940
+ )
941
+ ```
942
+
943
+ ### Form layout
944
+ ```js
945
+ Card({ class: css('_mw[600px] _mxa') },
946
+ Card.Header({}, h2({ class: css('_heading4') }, 'Form Title')),
947
+ Card.Body({ class: css('_flex _col _gap4') },
948
+ div({ class: css('_grid _gc2 _gap4') }, Input({ label: 'First' }), Input({ label: 'Last' })),
949
+ Input({ label: 'Email', type: 'email' }),
950
+ Button({ variant: 'primary', class: css('_wfull') }, 'Submit')
951
+ )
952
+ )
953
+ ```