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,384 @@
1
+ # Behavioral Primitives Reference
2
+
3
+ `src/components/_behaviors.js` provides composable behavioral systems used by 70+ components. Each primitive wires up event listeners, ARIA state, and keyboard interactions so components stay thin — import the behavior, call it, and wire `destroy()` to `onDestroy()`.
4
+
5
+ ## Cleanup Contract
6
+
7
+ **All primitives returning `destroy()` (or `deactivate()`/`disconnect()`) MUST be wired to `onDestroy()`.** Failure to do so leaks document listeners, timers, and observers when components are removed from the DOM. See `reference/component-lifecycle.md` for patterns.
8
+
9
+ ```javascript
10
+ const overlay = createOverlay(trigger, panel, { trigger: 'click' });
11
+ const listbox = createListbox(panel, { onSelect: handleSelect });
12
+ onDestroy(() => { overlay.destroy(); listbox.destroy(); });
13
+ ```
14
+
15
+ ---
16
+
17
+ ## Overlay
18
+
19
+ ### `createOverlay(triggerEl, contentEl, opts?)`
20
+
21
+ Managed floating layer with show/hide, click-outside, escape-to-close, and ARIA state.
22
+
23
+ **Used by:** Tooltip, Popover, HoverCard, Dropdown, Select, Combobox, DatePicker, TimePicker, ColorPicker, Cascader, TreeSelect, Mentions, Command, NavigationMenu, ContextMenu, Popconfirm, Tour
24
+
25
+ | Param | Type | Default | Description |
26
+ |-------|------|---------|-------------|
27
+ | `triggerEl` | `HTMLElement` | — | Element that triggers the overlay |
28
+ | `contentEl` | `HTMLElement` | — | Floating content element |
29
+ | `opts.trigger` | `'click'\|'hover'\|'manual'` | `'click'` | Activation mode |
30
+ | `opts.closeOnEscape` | `boolean` | `true` | Escape key closes overlay |
31
+ | `opts.closeOnOutside` | `boolean` | `true` | Click outside closes overlay |
32
+ | `opts.hoverDelay` | `number` | `200` | ms delay before showing (hover mode) |
33
+ | `opts.hoverCloseDelay` | `number` | `150` | ms delay before hiding (hover mode) |
34
+ | `opts.onOpen` | `Function` | — | Called when overlay opens |
35
+ | `opts.onClose` | `Function` | — | Called when overlay closes |
36
+ | `opts.usePopover` | `boolean` | `false` | Use Popover API instead of display toggle |
37
+
38
+ **Returns:** `{ open(), close(), toggle(), isOpen(): boolean, destroy() }`
39
+
40
+ **ARIA managed:** `aria-expanded` on `triggerEl` (`'true'`/`'false'`)
41
+
42
+ ```javascript
43
+ const overlay = createOverlay(btn, panel, { trigger: 'hover', hoverDelay: 300 });
44
+ onDestroy(() => overlay.destroy());
45
+ ```
46
+
47
+ ---
48
+
49
+ ## Navigation
50
+
51
+ ### `createListbox(containerEl, opts?)`
52
+
53
+ Keyboard navigation + selection for option lists. Manages highlight, arrow keys, enter/space selection, type-ahead search, and scroll-into-view.
54
+
55
+ **Used by:** Select, Combobox, Command, Cascader, TreeSelect, Transfer, Mentions, AutoComplete, ContextMenu, Dropdown
56
+
57
+ | Param | Type | Default | Description |
58
+ |-------|------|---------|-------------|
59
+ | `containerEl` | `HTMLElement` | — | Listbox container |
60
+ | `opts.itemSelector` | `string` | `'.d-option'` | CSS selector for option elements |
61
+ | `opts.activeClass` | `string` | `'d-option-active'` | Class for highlighted item |
62
+ | `opts.disabledSelector` | `string` | `'.d-option-disabled'` | Selector for disabled items |
63
+ | `opts.loop` | `boolean` | `true` | Loop at list boundaries |
64
+ | `opts.orientation` | `'vertical'\|'horizontal'` | `'vertical'` | Arrow key axis |
65
+ | `opts.multiSelect` | `boolean` | `false` | Allow multi-selection |
66
+ | `opts.typeAhead` | `boolean` | `false` | Type-to-search (500ms buffer) |
67
+ | `opts.onSelect` | `Function` | — | Called with `(element, index)` |
68
+ | `opts.onHighlight` | `Function` | — | Called with `(element, index)` |
69
+
70
+ **Returns:** `{ highlight(index), highlightNext(), highlightPrev(), selectCurrent(), getActiveIndex(): number, reset(), handleKeydown(e), destroy() }`
71
+
72
+ **ARIA managed:** `aria-selected` on each option (`'true'`/`'false'`)
73
+
74
+ **Keys handled:** ArrowDown/ArrowUp (or ArrowRight/ArrowLeft for horizontal), Home, End, Enter, Space, type-ahead characters
75
+
76
+ ```javascript
77
+ const listbox = createListbox(panel, { typeAhead: true, onSelect: (el, i) => pick(i) });
78
+ onDestroy(() => listbox.destroy());
79
+ ```
80
+
81
+ ### `createRovingTabindex(containerEl, opts?)`
82
+
83
+ Roving tabindex pattern for groups. One element has `tabindex=0`, rest have `tabindex=-1`. Arrow keys move focus.
84
+
85
+ **Used by:** Tabs, RadioGroup, ToggleGroup, Segmented, Menu, Menubar, ButtonGroup, Toolbar
86
+
87
+ | Param | Type | Default | Description |
88
+ |-------|------|---------|-------------|
89
+ | `containerEl` | `HTMLElement` | — | Container element |
90
+ | `opts.itemSelector` | `string` | `'[role="tab"]'` | Selector for navigable items |
91
+ | `opts.orientation` | `'horizontal'\|'vertical'\|'both'` | `'horizontal'` | Arrow key axes |
92
+ | `opts.loop` | `boolean` | `true` | Loop at boundaries |
93
+ | `opts.onFocus` | `Function` | — | Called with `(element, index)` |
94
+
95
+ **Returns:** `{ focus(index), setActive(index), getActive(): number, destroy() }`
96
+
97
+ **ARIA managed:** `tabindex` on each item (`'0'` or `'-1'`)
98
+
99
+ **Keys handled:** ArrowRight/ArrowLeft (horizontal), ArrowDown/ArrowUp (vertical), Home, End
100
+
101
+ ```javascript
102
+ const roving = createRovingTabindex(tabList, { itemSelector: '[role="tab"]' });
103
+ onDestroy(() => roving.destroy());
104
+ ```
105
+
106
+ ### `createDisclosure(triggerEl, contentEl, opts?)`
107
+
108
+ Expand/collapse with smooth height animation. Does not add document-level listeners.
109
+
110
+ **Used by:** Accordion, Collapsible, Tree, NavigationMenu sections
111
+
112
+ | Param | Type | Default | Description |
113
+ |-------|------|---------|-------------|
114
+ | `triggerEl` | `HTMLElement` | — | Toggle trigger |
115
+ | `contentEl` | `HTMLElement` | — | Collapsible content |
116
+ | `opts.defaultOpen` | `boolean` | `false` | Initial state |
117
+ | `opts.animate` | `boolean` | `true` | Smooth height transition |
118
+ | `opts.onToggle` | `Function` | — | Called with `(isOpen)` |
119
+
120
+ **Returns:** `{ open(), close(), toggle(), isOpen(): boolean }`
121
+
122
+ **ARIA managed:** `aria-expanded` on `triggerEl`
123
+
124
+ **Keys handled:** Enter, Space on trigger
125
+
126
+ **Note:** No `destroy()` — uses only element-level listeners (cleaned up with the element).
127
+
128
+ ```javascript
129
+ const disc = createDisclosure(header, body, { defaultOpen: true });
130
+ ```
131
+
132
+ ---
133
+
134
+ ## Focus
135
+
136
+ ### `createFocusTrap(containerEl)`
137
+
138
+ Traps Tab/Shift+Tab cycling within focusable elements. Focuses first focusable on activate.
139
+
140
+ **Used by:** Modal, Drawer, AlertDialog, Command
141
+
142
+ | Param | Type | Description |
143
+ |-------|------|-------------|
144
+ | `containerEl` | `HTMLElement` | Container to trap focus within |
145
+
146
+ **Returns:** `{ activate(), deactivate() }`
147
+
148
+ **Focusable selector:** `a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])`
149
+
150
+ **Keys handled:** Tab, Shift+Tab
151
+
152
+ **Cleanup:** Call `deactivate()` via `onDestroy()`.
153
+
154
+ ```javascript
155
+ const trap = createFocusTrap(panel);
156
+ trap.activate();
157
+ onDestroy(() => trap.deactivate());
158
+ ```
159
+
160
+ ---
161
+
162
+ ## Interaction
163
+
164
+ ### `createDrag(el, opts)`
165
+
166
+ Lightweight pointer-based drag handler. Tracks pointer movement with delta from start position.
167
+
168
+ **Used by:** Slider, Resizable, Transfer, DnD sorting
169
+
170
+ | Param | Type | Description |
171
+ |-------|------|-------------|
172
+ | `el` | `HTMLElement` | Draggable element |
173
+ | `opts.onMove` | `Function` | Called with `(x, y, dx, dy, event)` |
174
+ | `opts.onStart` | `Function` | Called with `(x, y, event)` |
175
+ | `opts.onEnd` | `Function` | Called with `(x, y, event)` |
176
+
177
+ **Returns:** `{ destroy() }`
178
+
179
+ ```javascript
180
+ const drag = createDrag(handle, { onMove: (x, y, dx, dy) => update(dx) });
181
+ onDestroy(() => drag.destroy());
182
+ ```
183
+
184
+ ### `createHotkey(el, bindings)`
185
+
186
+ Keyboard shortcut registration with modifier normalization, chord sequences, and Mac meta-key handling.
187
+
188
+ **Used by:** Command, Modal, custom app shortcuts
189
+
190
+ | Param | Type | Description |
191
+ |-------|------|-------------|
192
+ | `el` | `HTMLElement\|Document` | Scope element for key events |
193
+ | `bindings` | `Object<string, Function>` | Map of shortcut string to handler |
194
+
195
+ **Shortcut format:** `'ctrl+k'`, `'shift+alt+n'`, `'meta+enter'`, `'g g'` (chord). Modifiers: `ctrl`, `shift`, `alt`, `meta`/`cmd`/`command`. On Mac, `ctrl` matches both Ctrl and Meta.
196
+
197
+ **Returns:** `{ destroy(), update(newBindings) }`
198
+
199
+ ```javascript
200
+ const hk = createHotkey(document, { 'ctrl+k': openSearch, 'g g': goTop });
201
+ onDestroy(() => hk.destroy());
202
+ ```
203
+
204
+ ---
205
+
206
+ ## Scroll
207
+
208
+ ### `createVirtualScroll(containerEl, opts)`
209
+
210
+ Renders only visible items + buffer for large lists. Fixed item height.
211
+
212
+ **Used by:** DataTable, Tree (large), Transfer, Select (many options)
213
+
214
+ | Param | Type | Default | Description |
215
+ |-------|------|---------|-------------|
216
+ | `containerEl` | `HTMLElement` | — | Scrollable container |
217
+ | `opts.itemHeight` | `number` | — | Fixed item height (px) |
218
+ | `opts.totalItems` | `number` | — | Total item count |
219
+ | `opts.buffer` | `number` | `5` | Extra items above/below viewport |
220
+ | `opts.renderItem` | `Function` | — | `(index) => HTMLElement` |
221
+
222
+ **Returns:** `{ refresh(), setTotal(n), destroy() }`
223
+
224
+ ```javascript
225
+ const vs = createVirtualScroll(container, { itemHeight: 36, totalItems: 10000, renderItem: renderRow });
226
+ onDestroy(() => vs.destroy());
227
+ ```
228
+
229
+ ### `createInfiniteScroll(containerEl, opts)`
230
+
231
+ Triggers load-more when a sentinel element enters the viewport via IntersectionObserver.
232
+
233
+ **Used by:** List (infinite mode), feeds, search results
234
+
235
+ | Param | Type | Default | Description |
236
+ |-------|------|---------|-------------|
237
+ | `containerEl` | `HTMLElement` | — | Scrollable container |
238
+ | `opts.loadMore` | `Function` | — | Async callback to load more data |
239
+ | `opts.threshold` | `number` | `200` | Distance (px) from bottom to trigger |
240
+ | `opts.sentinel` | `HTMLElement` | auto-created | Custom sentinel element |
241
+
242
+ **Returns:** `{ destroy(), loading(): boolean }`
243
+
244
+ ```javascript
245
+ const inf = createInfiniteScroll(list, { loadMore: fetchNextPage, threshold: 300 });
246
+ onDestroy(() => inf.destroy());
247
+ ```
248
+
249
+ ### `createScrollSpy(root, opts?)`
250
+
251
+ Tracks which observed section is visible. Calls callback when active section changes.
252
+
253
+ **Used by:** TableOfContents, workbench navigation, documentation layouts
254
+
255
+ | Param | Type | Default | Description |
256
+ |-------|------|---------|-------------|
257
+ | `root` | `HTMLElement\|null` | — | Scroll container (`null` = viewport) |
258
+ | `opts.rootMargin` | `string` | `'-20% 0px -60% 0px'` | IntersectionObserver margin |
259
+ | `opts.threshold` | `number` | `0` | Visibility threshold |
260
+ | `opts.onActiveChange` | `Function` | — | Called with `(element)` |
261
+
262
+ **Returns:** `{ observe(el), unobserve(el), disconnect() }`
263
+
264
+ **Cleanup:** Call `disconnect()` via `onDestroy()`.
265
+
266
+ ```javascript
267
+ const spy = createScrollSpy(null, { onActiveChange: (el) => highlight(el.id) });
268
+ sections.forEach(s => spy.observe(s));
269
+ onDestroy(() => spy.disconnect());
270
+ ```
271
+
272
+ ---
273
+
274
+ ## Layout
275
+
276
+ ### `createMasonry(containerEl, opts?)`
277
+
278
+ Pinterest-style layout via shortest-column placement. Auto-recalculates on resize via ResizeObserver.
279
+
280
+ **Used by:** Image galleries, card grids
281
+
282
+ | Param | Type | Default | Description |
283
+ |-------|------|---------|-------------|
284
+ | `containerEl` | `HTMLElement` | — | Container whose children are laid out |
285
+ | `opts.columns` | `number` | `3` | Number of columns |
286
+ | `opts.gap` | `number` | `16` | Gap between items (px) |
287
+
288
+ **Returns:** `{ refresh(), setColumns(n), destroy() }`
289
+
290
+ ```javascript
291
+ const masonry = createMasonry(grid, { columns: 4, gap: 24 });
292
+ onDestroy(() => masonry.destroy());
293
+ ```
294
+
295
+ ---
296
+
297
+ ## Form
298
+
299
+ ### `createFormField(controlEl, opts?)`
300
+
301
+ Wraps a form control with label, help text, error message, and required indicator. Manages ARIA attributes reactively.
302
+
303
+ **Used by:** All form inputs (Input, Select, Checkbox, Switch, etc.)
304
+
305
+ | Param | Type | Description |
306
+ |-------|------|-------------|
307
+ | `controlEl` | `HTMLElement` | The input/select/textarea element |
308
+ | `opts.label` | `string` | Label text |
309
+ | `opts.error` | `string\|Function` | Static or reactive (getter) error message |
310
+ | `opts.help` | `string` | Help text |
311
+ | `opts.required` | `boolean` | Show required indicator |
312
+ | `opts.class` | `string` | Additional CSS class on wrapper |
313
+
314
+ **Returns:** `HTMLElement` (the `d-field` wrapper)
315
+
316
+ **ARIA managed:** `aria-describedby` (links to help text), `aria-invalid`, `aria-errormessage` (links to error), `aria-hidden` on required indicator
317
+
318
+ **Note:** No `destroy()` — uses `createEffect` for reactive error tracking (cleaned up by reactivity system).
319
+
320
+ ```javascript
321
+ const input = h('input', { type: 'text', class: 'd-input' });
322
+ const field = createFormField(input, { label: 'Email', error: () => emailErr(), required: true });
323
+ ```
324
+
325
+ ---
326
+
327
+ ## Utilities
328
+
329
+ ### `caret(direction?, opts?)`
330
+
331
+ Shared chevron icon for dropdowns, accordions, and other disclosure components.
332
+
333
+ | Param | Type | Default | Description |
334
+ |-------|------|---------|-------------|
335
+ | `direction` | `'down'\|'up'\|'right'\|'left'` | `'down'` | Arrow direction |
336
+ | `opts` | `Object` | `{}` | Passed to `icon()`, plus optional `class` |
337
+
338
+ **Returns:** `HTMLElement` (SVG icon with class `d-caret`)
339
+
340
+ ```javascript
341
+ trigger.appendChild(caret('down'));
342
+ ```
343
+
344
+ ### `createCheckControl(opts?)`
345
+
346
+ Styled checkbox for compound components. Returns same structure as Checkbox component.
347
+
348
+ **Used by:** Transfer, Tree, TreeSelect, DataTable
349
+
350
+ | Param | Type | Description |
351
+ |-------|------|-------------|
352
+ | `opts` | `Object` | Attributes for `<input type="checkbox">` |
353
+
354
+ **Returns:** `{ wrap: HTMLElement, input: HTMLInputElement }`
355
+
356
+ ```javascript
357
+ const { wrap, input } = createCheckControl({ checked: true });
358
+ row.appendChild(wrap);
359
+ ```
360
+
361
+ ---
362
+
363
+ ## Quick Reference
364
+
365
+ | Primitive | Category | Has `destroy()`? | Document listeners? |
366
+ |-----------|----------|-------------------|---------------------|
367
+ | `createOverlay` | Overlay | Yes | Yes (keydown, mousedown) |
368
+ | `createListbox` | Navigation | Yes | No (container only) |
369
+ | `createRovingTabindex` | Navigation | Yes | No (container only) |
370
+ | `createDisclosure` | Navigation | No | No |
371
+ | `createFocusTrap` | Focus | `deactivate()` | No (container only) |
372
+ | `createDrag` | Interaction | Yes | Yes (pointermove, pointerup during drag) |
373
+ | `createHotkey` | Keyboard | Yes | Yes (keydown) |
374
+ | `createVirtualScroll` | Scroll | Yes | No (container scroll) |
375
+ | `createInfiniteScroll` | Scroll | Yes | No (IntersectionObserver) |
376
+ | `createScrollSpy` | Scroll | `disconnect()` | No (IntersectionObserver) |
377
+ | `createMasonry` | Layout | Yes | No (ResizeObserver) |
378
+ | `createFormField` | Form | No | No |
379
+ | `createCheckControl` | Utility | No | No |
380
+ | `caret` | Utility | No | No |
381
+
382
+ ---
383
+
384
+ **See also:** `reference/component-lifecycle.md`, `reference/compound-spacing.md`
@@ -0,0 +1,275 @@
1
+ # Build Tooling Reference
2
+
3
+ Production build pipeline. Entry: `decantr build` or `node cli/commands/build.js`. Output: `dist/`.
4
+
5
+ ## Build Pipeline
6
+
7
+ | Step | Description | File |
8
+ |------|-------------|------|
9
+ | 1. Config | Load `decantr.config.json`, merge defaults, apply CLI flag overrides | `cli/commands/build.js` |
10
+ | 2. Resolve | BFS from `src/app.js` — resolve all static imports (decantr/* + relative) into module map | `tools/builder.js` `resolveModules()` |
11
+ | 3. Incremental check | MD5 hash all source files + HTML, compare to cache — skip rebuild if unchanged | `tools/builder.js` |
12
+ | 4. Bundle | Topological sort modules, rewrite imports/exports into IIFE with module variables (`_m0`, `_m1`, ...) | `tools/builder.js` `bundle()` |
13
+ | 5. Tree shake | Remove unused exports per-module via usage graph analysis | `tools/builder.js` `treeShakeModule()` |
14
+ | 6. Icon tree shake | Scan for `icon('name')` calls, prune unreferenced entries from `ESSENTIAL`/`EXTENDED` objects | `tools/builder.js` `treeShakeIcons()` |
15
+ | 7. Style elimination | Detect used addon styles via `setStyle()`/`setTheme()` + Essence, remove unused addon modules | `tools/builder.js` `detectUsedStyles()` + `eliminateUnusedAddonStyles()` |
16
+ | 8. Component CSS pruning | Map dependency graph to CSS sections, remove unused `componentCSSMap` entries | `tools/builder.js` `detectUsedComponents()` + `pruneComponentCSS()` |
17
+ | 9. Code split | Detect `import()` calls, resolve each as separate chunk, inject runtime loader | `tools/builder.js` `resolveChunks()` |
18
+ | 10. CSS extract | Scan all source for `css('...')` and `class: '...'` patterns, generate `@layer d.atoms{...}` | `tools/css-extract.js` |
19
+ | 11. Static CSS extraction | If safe (`canStaticExtract()`), replace `css/index.js` with passthrough, remove `atoms.js` + `runtime.js` | `tools/builder.js` `staticExtractTransform()` |
20
+ | 12. CSS purge | Scan bundled JS for referenced atom class names, strip unreferenced rules | `tools/builder.js` `purgeCSS()` |
21
+ | 13. Minify | Strip comments, collapse whitespace, remove unnecessary semicolons | `tools/minify.js` |
22
+ | 14. Write | Content-hashed filenames (`app.{hash}.js`, `app.{hash}.css`), rewrite HTML script/link tags | `tools/builder.js` |
23
+ | 15. Copy public | Copy `public/` to `dist/`, transform all `.html` files with asset references | `tools/builder.js` |
24
+ | 16. Report | Print per-asset sizes (raw, gzip, brotli), compression ratios, module breakdown (top 15) | `tools/builder.js` |
25
+ | 17. Cache save | Write MD5 hashes to `node_modules/.decantr-cache/build-cache.json` | `tools/builder.js` |
26
+
27
+ ## Build-Time Style Elimination
28
+
29
+ Non-core styles live in `src/css/styles/addons/` and are imported explicitly by user code (e.g., `import { clean } from 'decantr/styles/clean'`). Only `auradecantism` is a built-in. Unused addon style modules are detected and removed at build time.
30
+
31
+ **Detection** (`detectUsedStyles()`):
32
+ 1. Scans all user source files for `setStyle('...')` and `setTheme('...')` calls
33
+ 2. Checks `decantr.essence.json` for `vintage.style` declarations (including sectioned essences)
34
+ 3. Checks `decantr.config.json` for style references
35
+ 4. Collects the set of actually-used style IDs
36
+
37
+ **Elimination** (`eliminateUnusedAddonStyles()`):
38
+ - Removes unused addon style module files (`src/css/styles/addons/*.js`) from the module map
39
+ - Since addon styles are no longer imported by `theme-registry.js`, there are no import lines to remove from the registry
40
+
41
+ The default style (auradecantism) is always kept as a built-in. Only addon styles explicitly referenced via `setStyle()`, `setTheme()`, or the Essence are shipped.
42
+
43
+ ## Component CSS Pruning
44
+
45
+ Component CSS (`src/css/components.js`) is structured as a keyed object (`componentCSSMap`) with 78 sections. At build time, unused sections are pruned based on which components appear in the dependency graph.
46
+
47
+ **Detection** (`detectUsedComponents()`):
48
+ - Maps component source files found in the dependency graph to their corresponding CSS section keys via `COMPONENT_CSS_MAP`
49
+ - The `global` section is always retained
50
+
51
+ **Pruning** (`pruneComponentCSS()`):
52
+ - Removes unused sections from `componentCSSMap` in the bundled output
53
+ - A baseline app using no components prunes 77/78 sections (everything except `global`), saving ~65 KB raw
54
+
55
+ ## Static CSS Extraction
56
+
57
+ When safe, the entire atom resolution runtime (`atoms.js` + `runtime.js`) is eliminated and replaced with a pre-generated static CSS file.
58
+
59
+ **Safety valve** (`canStaticExtract()`):
60
+ - Checks for `define()` calls in user code (custom atoms require runtime)
61
+ - Checks for dynamic `css()` patterns (template literals, variable concatenation)
62
+ - If either is detected, falls back to full runtime — no static extraction
63
+
64
+ **Transform** (`staticExtractTransform()`):
65
+ - Replaces `css/index.js` with a ~673 byte passthrough that converts `_group` to `d-group`, `_peer` to `d-peer`, and joins class names
66
+ - Removes `atoms.js` and `runtime.js` from the module map entirely
67
+ - All atom CSS rules are pre-generated into the static CSS file by `tools/css-extract.js`
68
+ - Saves ~31 KB raw (atoms.js + runtime.js eliminated)
69
+
70
+ **CSS generation** (`tools/css-extract.js` `generateCSS()`):
71
+ - Handles all atom types: standard, responsive (`_sm:gc3`), container queries (`_cq640:gc3`), group/peer (`_gh:fgprimary`), opacity modifiers (`_bgprimary/50`), and arbitrary values (`_w[512px]`)
72
+ - Output is a `@layer d.atoms{...}` block with all referenced atoms pre-resolved
73
+
74
+ ## Tree Shaking
75
+
76
+ **Usage graph analysis** (`buildUsageGraph()`):
77
+ 1. For each module, collect which named exports are imported by other modules
78
+ 2. Side-effect imports (`import './foo'`) or namespace imports mark all exports as used (`*`)
79
+ 3. Per-module, exports not in the used set are candidates for removal
80
+
81
+ **Removal logic** (`treeShakeModule()`):
82
+ - Skip if wildcard (`*`) — module has side-effect consumers
83
+ - For each unused export, count references within the module itself (`\bname\b` regex)
84
+ - If `refCount >= 2`, the export is used internally — keep it
85
+ - If `refCount < 2`, remove the full declaration (function body via brace counting, const/let via semicolon-at-depth-0)
86
+
87
+ **Icon tree shaking** (`treeShakeIcons()`):
88
+ - Scans bundled output for `icon('name')` calls
89
+ - Rewrites `ESSENTIAL = {...}` and `EXTENDED = {...}` objects to include only referenced icon entries
90
+ - Runs after main tree shake, before code splitting
91
+
92
+ ## Code Splitting
93
+
94
+ **Detection**: `findDynamicImports()` scans all modules for `import('specifier')` patterns (relative or `decantr/*`).
95
+
96
+ **Chunk generation** (`resolveChunks()`):
97
+ 1. For each dynamic import target not already in the main bundle, resolve its full dependency tree
98
+ 2. Remove modules already in main bundle (shared code stays in main)
99
+ 3. Bundle each chunk independently with tree shaking
100
+ 4. Wrap chunk as `window.__decantrChunk = (function(){...})();`
101
+ 5. Minify, content-hash filename: `{chunk-name}.{hash}.js`
102
+
103
+ **Runtime loader** (injected into main bundle):
104
+ ```js
105
+ function __decantrLoadChunk(url) {
106
+ return new Promise(function(r, e) {
107
+ var s = document.createElement('script');
108
+ s.src = url;
109
+ s.onload = function() { r(window.__decantrChunk) };
110
+ s.onerror = e;
111
+ document.head.appendChild(s);
112
+ })
113
+ }
114
+ ```
115
+
116
+ Dynamic `import()` calls rewritten to `__decantrLoadChunk('./assets/{chunk-file}')`.
117
+
118
+ ## Source Maps
119
+
120
+ | Property | Value |
121
+ |----------|-------|
122
+ | Spec | V3 |
123
+ | Encoding | VLQ (Base64) |
124
+ | `sourcesContent` | Embedded (original source inlined) |
125
+ | Output file | `assets/app.{hash}.js.map` |
126
+ | Reference | `//# sourceMappingURL=./app.{hash}.js.map` appended to JS |
127
+
128
+ Mappings track per-module: output line range mapped to source file + proportional source line. Column-level mapping is output-col-0 only.
129
+
130
+ ## Incremental Builds
131
+
132
+ | Item | Detail |
133
+ |------|--------|
134
+ | Cache location | `{project}/node_modules/.decantr-cache/build-cache.json` |
135
+ | Hash algorithm | MD5 |
136
+ | Scope | All resolved module sources + `public/index.html` |
137
+ | Combined hash | MD5 of `JSON.stringify(fileHashes) + htmlHash` |
138
+ | Skip condition | Combined hash matches cache AND `dist/` directory exists |
139
+ | Cache write | After successful build completes |
140
+
141
+ ## CSS Purging
142
+
143
+ **Class name detection** — three scan passes over bundled JS:
144
+
145
+ | Pass | Pattern | Extracts |
146
+ |------|---------|----------|
147
+ | String literals | `['"\`]([^'"\`]*?)['"\` ]` | Tokens containing `_`, split by whitespace, keep `_`-prefixed |
148
+ | `css()` calls | `css\s*\(\s*['"\`](...)['"` ]\s*\)` | All whitespace-separated tokens |
149
+ | `class:` props | `class:\s*['"](...)['"` ] | All whitespace-separated tokens |
150
+
151
+ **Rule removal**: Parses CSS within `@layer d.atoms{...}`, extracts class name from each `.class{...}` rule, removes rules whose class name is not in the referenced set.
152
+
153
+ ## Minification
154
+
155
+ `tools/minify.js` — zero-dependency JS minifier.
156
+
157
+ | Step | Action |
158
+ |------|--------|
159
+ | 1 | Stash template literals (protect multi-line content) |
160
+ | 2 | Strip JSDoc (`/** */`), block comments (`/* */`), single-line comments (`//`) |
161
+ | 3 | Trim lines, skip empty lines |
162
+ | 4 | Collapse whitespace outside strings (keep space only between `\w` characters) |
163
+ | 5 | Remove semicolons before `}` |
164
+ | 6 | Collapse newlines around braces |
165
+ | 7 | Join statements — newline after `}`, space before `else`/`catch`/`finally`/`while` |
166
+ | 8 | Restore stashed template literals |
167
+
168
+ No variable renaming or advanced optimizations — structural minification only.
169
+
170
+ ## Bundle Analysis
171
+
172
+ **Embeddable API** (`tools/analyzer.js`):
173
+ ```js
174
+ import { analyzeBundle } from './tools/analyzer.js';
175
+ const { report, stats } = analyzeBundle(bundledJS, cssOutput, { html });
176
+ ```
177
+
178
+ **Standalone**:
179
+ ```
180
+ node tools/analyzer.js [dist-dir]
181
+ ```
182
+
183
+ **Output**: asset table (raw/gzip/brotli per file), module breakdown (top 10 by size with bar chart), compression ratios.
184
+
185
+ **Built-in analysis** (in builder): when `analyze` is enabled, prints module breakdown (top 15) sorted by size descending, with percentage of total JS.
186
+
187
+ **Compression**: gzip (default zlib) + Brotli quality 11 reported for all assets.
188
+
189
+ ## CLI Flags
190
+
191
+ | Flag | Config Key | Default | Effect |
192
+ |------|-----------|---------|--------|
193
+ | `--no-sourcemap` | `sourcemap` | `true` | Skip source map generation |
194
+ | `--no-analyze` | `analyze` | `true` | Skip module breakdown report |
195
+ | `--no-incremental` | `incremental` | `true` | Force full rebuild (ignore cache) |
196
+ | `--no-code-split` | `codeSplit` | `true` | Bundle all code into single file |
197
+ | `--no-purge` | `purgeCSS` | `true` | Keep all CSS atoms (no dead CSS elimination) |
198
+ | `--no-tree-shake` | `treeShake` | `true` | Keep all exports (no dead JS elimination) |
199
+
200
+ All features enabled by default. CLI flags override config file values.
201
+
202
+ ## Config File
203
+
204
+ `decantr.config.json` in project root. The `build` section:
205
+
206
+ ```json
207
+ {
208
+ "build": {
209
+ "outDir": "dist",
210
+ "inline": false,
211
+ "sourcemap": true,
212
+ "analyze": true,
213
+ "incremental": true,
214
+ "codeSplit": true,
215
+ "purgeCSS": true,
216
+ "treeShake": true
217
+ }
218
+ }
219
+ ```
220
+
221
+ | Key | Type | Default | Description |
222
+ |-----|------|---------|-------------|
223
+ | `outDir` | `string` | `"dist"` | Output directory (relative to project root) |
224
+ | `inline` | `boolean` | `false` | Reserved — inline JS/CSS into HTML |
225
+ | `sourcemap` | `boolean` | `true` | Generate V3 source maps |
226
+ | `analyze` | `boolean` | `true` | Print module breakdown after build |
227
+ | `incremental` | `boolean` | `true` | Enable MD5 hash cache for skip-if-unchanged |
228
+ | `codeSplit` | `boolean` | `true` | Split dynamic imports into separate chunks |
229
+ | `purgeCSS` | `boolean` | `true` | Remove unreferenced CSS atom rules |
230
+ | `treeShake` | `boolean` | `true` | Remove unused JS exports |
231
+
232
+ Config is loaded first, then CLI `--no-*` flags override to `false`.
233
+
234
+ ## Output Structure
235
+
236
+ ```
237
+ dist/
238
+ index.html # Transformed HTML with asset references
239
+ assets/
240
+ app.{hash}.js # Bundled + minified JS
241
+ app.{hash}.js.map # Source map (if enabled)
242
+ app.{hash}.css # Extracted + purged atomic CSS
243
+ {chunk}.{hash}.js # Code-split chunks (if any)
244
+ ... # Copied public/ assets (non-HTML copied as-is, HTML transformed)
245
+ ```
246
+
247
+ ## Key Files
248
+
249
+ | File | Role |
250
+ |------|------|
251
+ | `cli/commands/build.js` | CLI entry — config loading, flag parsing, invokes `build()` |
252
+ | `tools/builder.js` | Core build pipeline — resolve, bundle, tree shake, code split, purge, report |
253
+ | `tools/css-extract.js` | `extractClassNames()` + `generateCSS()` — scan source for atoms (including responsive, container query, group/peer, opacity, arbitrary), emit `@layer d.atoms` |
254
+ | `tools/minify.js` | `minify()` — structural JS minification |
255
+ | `tools/analyzer.js` | `analyzeBundle()` — standalone or embeddable bundle size analysis |
256
+
257
+ ## Bundle Optimization Results (Baseline App)
258
+
259
+ | Metric | Before | After | Reduction |
260
+ |--------|--------|-------|-----------|
261
+ | Raw JS | 133.8 KB | 52.6 KB | 60.7% |
262
+ | Gzip JS | 29.3 KB | 14.6 KB | 50.2% |
263
+ | Brotli JS | 24.8 KB | 12.9 KB | 48.0% |
264
+
265
+ Breakdown by optimization phase:
266
+
267
+ | Phase | Savings (raw) | What is eliminated |
268
+ |-------|---------------|-------------------|
269
+ | Style elimination | ~17 KB | 4 unused style modules (clean, retro, glassmorphism, command-center) |
270
+ | Component CSS pruning | ~65 KB | 77/78 unused `componentCSSMap` sections |
271
+ | Static CSS extraction | ~31 KB | `atoms.js` + `runtime.js` (css() becomes passthrough) |
272
+
273
+ ---
274
+
275
+ **See also:** `reference/dev-server-routes.md`, `reference/atoms.md`