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.
- package/AGENTS.md +868 -0
- package/CHANGELOG.md +255 -0
- package/CLAUDE.md +178 -0
- package/LICENSE +21 -0
- package/README.md +229 -0
- package/cli/art.js +127 -0
- package/cli/commands/a11y.js +61 -0
- package/cli/commands/audit.js +225 -0
- package/cli/commands/build.js +38 -0
- package/cli/commands/dev.js +18 -0
- package/cli/commands/doctor.js +197 -0
- package/cli/commands/figma-sync.js +48 -0
- package/cli/commands/figma-tokens.js +55 -0
- package/cli/commands/generate.js +26 -0
- package/cli/commands/init.js +116 -0
- package/cli/commands/lint.js +209 -0
- package/cli/commands/mcp.js +530 -0
- package/cli/commands/migrate.js +175 -0
- package/cli/commands/test.js +38 -0
- package/cli/commands/validate.js +354 -0
- package/cli/index.js +113 -0
- package/package.json +95 -0
- package/reference/atoms.md +517 -0
- package/reference/behaviors.md +384 -0
- package/reference/build-tooling.md +275 -0
- package/reference/color-guidelines.md +965 -0
- package/reference/component-lifecycle.md +137 -0
- package/reference/compound-spacing.md +95 -0
- package/reference/decantation-process.md +499 -0
- package/reference/dev-server-routes.md +93 -0
- package/reference/form-system.md +253 -0
- package/reference/i18n.md +336 -0
- package/reference/icons.md +576 -0
- package/reference/llm-primer.md +953 -0
- package/reference/plugins.md +252 -0
- package/reference/registry-consumption.md +76 -0
- package/reference/router.md +217 -0
- package/reference/shells.md +116 -0
- package/reference/spatial-guidelines.md +541 -0
- package/reference/ssr.md +234 -0
- package/reference/state-data.md +215 -0
- package/reference/state-patterns.md +166 -0
- package/reference/state.md +194 -0
- package/reference/style-system.md +110 -0
- package/reference/tokens.md +460 -0
- package/src/app.js +19 -0
- package/src/chart/_animate.js +266 -0
- package/src/chart/_base.js +109 -0
- package/src/chart/_data.js +209 -0
- package/src/chart/_format.js +106 -0
- package/src/chart/_interact.js +364 -0
- package/src/chart/_palette.js +105 -0
- package/src/chart/_renderer.js +52 -0
- package/src/chart/_scene.js +262 -0
- package/src/chart/_shared.js +371 -0
- package/src/chart/index.js +637 -0
- package/src/chart/layouts/_layout-base.js +328 -0
- package/src/chart/layouts/cartesian.js +148 -0
- package/src/chart/layouts/hierarchy.js +562 -0
- package/src/chart/layouts/polar.js +101 -0
- package/src/chart/renderers/canvas.js +179 -0
- package/src/chart/renderers/svg.js +256 -0
- package/src/chart/renderers/webgpu.js +715 -0
- package/src/chart/types/_type-base.js +26 -0
- package/src/chart/types/area.js +134 -0
- package/src/chart/types/bar.js +173 -0
- package/src/chart/types/box-plot.js +125 -0
- package/src/chart/types/bubble.js +63 -0
- package/src/chart/types/candlestick.js +115 -0
- package/src/chart/types/chord.js +85 -0
- package/src/chart/types/combination.js +108 -0
- package/src/chart/types/funnel.js +68 -0
- package/src/chart/types/gauge.js +163 -0
- package/src/chart/types/heatmap.js +98 -0
- package/src/chart/types/histogram.js +71 -0
- package/src/chart/types/line.js +111 -0
- package/src/chart/types/org-chart.js +93 -0
- package/src/chart/types/pie.js +81 -0
- package/src/chart/types/radar.js +96 -0
- package/src/chart/types/radial.js +68 -0
- package/src/chart/types/range-area.js +55 -0
- package/src/chart/types/range-bar.js +61 -0
- package/src/chart/types/sankey.js +73 -0
- package/src/chart/types/scatter.js +66 -0
- package/src/chart/types/sparkline.js +81 -0
- package/src/chart/types/sunburst.js +69 -0
- package/src/chart/types/swimlane.js +88 -0
- package/src/chart/types/treemap.js +62 -0
- package/src/chart/types/waterfall.js +100 -0
- package/src/components/_base.js +1658 -0
- package/src/components/_behaviors.js +1140 -0
- package/src/components/_primitives.js +534 -0
- package/src/components/_qr-encoder.js +539 -0
- package/src/components/accordion.js +207 -0
- package/src/components/affix.js +62 -0
- package/src/components/alert-dialog.js +75 -0
- package/src/components/alert.js +47 -0
- package/src/components/aspect-ratio.js +24 -0
- package/src/components/avatar-group.js +55 -0
- package/src/components/avatar.js +38 -0
- package/src/components/back-top.js +75 -0
- package/src/components/badge.js +74 -0
- package/src/components/banner.js +68 -0
- package/src/components/breadcrumb.js +162 -0
- package/src/components/button.js +115 -0
- package/src/components/calendar.js +131 -0
- package/src/components/card.js +192 -0
- package/src/components/carousel.js +98 -0
- package/src/components/cascader.js +261 -0
- package/src/components/checkbox.js +80 -0
- package/src/components/chip.js +81 -0
- package/src/components/code-block.js +82 -0
- package/src/components/collapsible.js +50 -0
- package/src/components/color-palette.js +438 -0
- package/src/components/color-picker.js +314 -0
- package/src/components/combobox.js +181 -0
- package/src/components/command.js +174 -0
- package/src/components/comment.js +206 -0
- package/src/components/context-menu.js +76 -0
- package/src/components/data-table.js +724 -0
- package/src/components/date-picker.js +217 -0
- package/src/components/date-range-picker.js +244 -0
- package/src/components/datetime-picker.js +271 -0
- package/src/components/descriptions.js +68 -0
- package/src/components/drawer.js +179 -0
- package/src/components/dropdown.js +88 -0
- package/src/components/empty.js +41 -0
- package/src/components/float-button.js +90 -0
- package/src/components/form.js +106 -0
- package/src/components/hover-card.js +49 -0
- package/src/components/icon.js +87 -0
- package/src/components/image.js +97 -0
- package/src/components/index.js +117 -0
- package/src/components/input-group.js +75 -0
- package/src/components/input-number.js +155 -0
- package/src/components/input-otp.js +178 -0
- package/src/components/input.js +91 -0
- package/src/components/kbd.js +36 -0
- package/src/components/label.js +25 -0
- package/src/components/list.js +118 -0
- package/src/components/masked-input.js +236 -0
- package/src/components/mentions.js +165 -0
- package/src/components/menu.js +259 -0
- package/src/components/message.js +80 -0
- package/src/components/modal.js +147 -0
- package/src/components/navigation-menu.js +166 -0
- package/src/components/notification.js +84 -0
- package/src/components/pagination.js +104 -0
- package/src/components/placeholder.js +132 -0
- package/src/components/popconfirm.js +70 -0
- package/src/components/popover.js +58 -0
- package/src/components/progress.js +61 -0
- package/src/components/qrcode.js +251 -0
- package/src/components/radiogroup.js +120 -0
- package/src/components/range-slider.js +176 -0
- package/src/components/rate.js +186 -0
- package/src/components/resizable.js +83 -0
- package/src/components/result.js +57 -0
- package/src/components/scroll-area.js +43 -0
- package/src/components/segmented.js +97 -0
- package/src/components/select.js +165 -0
- package/src/components/separator.js +31 -0
- package/src/components/shell.js +407 -0
- package/src/components/skeleton.js +39 -0
- package/src/components/slider.js +141 -0
- package/src/components/sortable-list.js +176 -0
- package/src/components/space.js +42 -0
- package/src/components/spinner.js +112 -0
- package/src/components/splitter.js +147 -0
- package/src/components/statistic.js +136 -0
- package/src/components/steps.js +99 -0
- package/src/components/switch.js +95 -0
- package/src/components/table.js +44 -0
- package/src/components/tabs.js +216 -0
- package/src/components/tag.js +115 -0
- package/src/components/textarea.js +82 -0
- package/src/components/time-picker.js +153 -0
- package/src/components/time-range-picker.js +170 -0
- package/src/components/timeline.js +226 -0
- package/src/components/toast.js +71 -0
- package/src/components/toggle.js +213 -0
- package/src/components/tooltip.js +57 -0
- package/src/components/tour.js +159 -0
- package/src/components/transfer.js +163 -0
- package/src/components/tree-select.js +274 -0
- package/src/components/tree.js +141 -0
- package/src/components/typography.js +136 -0
- package/src/components/upload.js +118 -0
- package/src/components/visually-hidden.js +20 -0
- package/src/components/watermark.js +124 -0
- package/src/core/index.js +539 -0
- package/src/core/lifecycle.js +69 -0
- package/src/css/atoms.js +651 -0
- package/src/css/components.js +940 -0
- package/src/css/derive.js +1296 -0
- package/src/css/index.js +265 -0
- package/src/css/runtime.js +268 -0
- package/src/css/styles/addons/bioluminescent.js +93 -0
- package/src/css/styles/addons/clay.js +70 -0
- package/src/css/styles/addons/clean.js +57 -0
- package/src/css/styles/addons/command-center.js +143 -0
- package/src/css/styles/addons/dopamine.js +83 -0
- package/src/css/styles/addons/editorial.js +80 -0
- package/src/css/styles/addons/glassmorphism.js +99 -0
- package/src/css/styles/addons/liquid-glass.js +105 -0
- package/src/css/styles/addons/prismatic.js +100 -0
- package/src/css/styles/addons/retro.js +63 -0
- package/src/css/styles/auradecantism.js +96 -0
- package/src/css/theme-registry.js +444 -0
- package/src/data/entity.js +281 -0
- package/src/data/index.js +13 -0
- package/src/data/persist.js +225 -0
- package/src/data/query.js +839 -0
- package/src/data/realtime.js +299 -0
- package/src/data/url.js +177 -0
- package/src/data/worker.js +134 -0
- package/src/explorer/archetypes.js +243 -0
- package/src/explorer/atoms.js +228 -0
- package/src/explorer/charts.js +497 -0
- package/src/explorer/components.js +129 -0
- package/src/explorer/foundations.js +949 -0
- package/src/explorer/icons.js +178 -0
- package/src/explorer/patterns.js +247 -0
- package/src/explorer/recipes.js +194 -0
- package/src/explorer/shared/pattern-examples.js +1337 -0
- package/src/explorer/shared/showcase-renderer.js +958 -0
- package/src/explorer/shared/spec-table.js +41 -0
- package/src/explorer/shared/usage-links.js +87 -0
- package/src/explorer/shell-config.js +10 -0
- package/src/explorer/shells.js +551 -0
- package/src/explorer/styles.js +161 -0
- package/src/explorer/tokens.js +262 -0
- package/src/explorer/tools.js +525 -0
- package/src/form/index.js +804 -0
- package/src/i18n/index.js +251 -0
- package/src/icons/essential.js +479 -0
- package/src/icons/index.js +53 -0
- package/src/plugins/index.js +282 -0
- package/src/registry/archetypes/content-site.json +71 -0
- package/src/registry/archetypes/docs-explorer.json +23 -0
- package/src/registry/archetypes/ecommerce.json +104 -0
- package/src/registry/archetypes/financial-dashboard.json +77 -0
- package/src/registry/archetypes/index.json +41 -0
- package/src/registry/archetypes/portfolio.json +82 -0
- package/src/registry/archetypes/recipe-community.json +159 -0
- package/src/registry/archetypes/saas-dashboard.json +86 -0
- package/src/registry/architect/cross-cutting.json +45 -0
- package/src/registry/architect/domains/ecommerce.json +294 -0
- package/src/registry/architect/domains/financial-services.json +302 -0
- package/src/registry/architect/index.json +26 -0
- package/src/registry/architect/traits.json +379 -0
- package/src/registry/atoms.json +16 -0
- package/src/registry/chart-showcase.json +160 -0
- package/src/registry/chart.json +136 -0
- package/src/registry/components.json +8616 -0
- package/src/registry/core.json +216 -0
- package/src/registry/css.json +319 -0
- package/src/registry/data.json +135 -0
- package/src/registry/foundations.json +11 -0
- package/src/registry/icons.json +463 -0
- package/src/registry/index.json +101 -0
- package/src/registry/patterns/activity-feed.json +37 -0
- package/src/registry/patterns/article-content.json +27 -0
- package/src/registry/patterns/auth-form.json +37 -0
- package/src/registry/patterns/author-card.json +20 -0
- package/src/registry/patterns/card-grid.json +127 -0
- package/src/registry/patterns/category-nav.json +26 -0
- package/src/registry/patterns/chart-grid.json +36 -0
- package/src/registry/patterns/chat-interface.json +37 -0
- package/src/registry/patterns/checklist-card.json +55 -0
- package/src/registry/patterns/comparison-panel.json +27 -0
- package/src/registry/patterns/component-showcase.json +24 -0
- package/src/registry/patterns/contact-form.json +31 -0
- package/src/registry/patterns/cta-section.json +20 -0
- package/src/registry/patterns/data-table.json +37 -0
- package/src/registry/patterns/detail-header.json +83 -0
- package/src/registry/patterns/detail-panel.json +27 -0
- package/src/registry/patterns/explorer-shell.json +22 -0
- package/src/registry/patterns/filter-bar.json +33 -0
- package/src/registry/patterns/filter-sidebar.json +27 -0
- package/src/registry/patterns/form-sections.json +110 -0
- package/src/registry/patterns/goal-tracker.json +27 -0
- package/src/registry/patterns/hero.json +107 -0
- package/src/registry/patterns/index.json +47 -0
- package/src/registry/patterns/kpi-grid.json +36 -0
- package/src/registry/patterns/media-gallery.json +20 -0
- package/src/registry/patterns/order-history.json +20 -0
- package/src/registry/patterns/pagination.json +19 -0
- package/src/registry/patterns/photo-to-recipe.json +36 -0
- package/src/registry/patterns/pipeline-tracker.json +28 -0
- package/src/registry/patterns/post-list.json +27 -0
- package/src/registry/patterns/pricing-table.json +32 -0
- package/src/registry/patterns/scorecard.json +28 -0
- package/src/registry/patterns/search-bar.json +20 -0
- package/src/registry/patterns/specimen-grid.json +19 -0
- package/src/registry/patterns/stat-card.json +55 -0
- package/src/registry/patterns/stats-bar.json +55 -0
- package/src/registry/patterns/steps-card.json +55 -0
- package/src/registry/patterns/table-of-contents.json +19 -0
- package/src/registry/patterns/testimonials.json +21 -0
- package/src/registry/patterns/timeline.json +27 -0
- package/src/registry/patterns/token-inspector.json +21 -0
- package/src/registry/patterns/wizard.json +27 -0
- package/src/registry/recipe-auradecantism.json +69 -0
- package/src/registry/recipe-clean.json +65 -0
- package/src/registry/recipe-command-center.json +78 -0
- package/src/registry/router.json +73 -0
- package/src/registry/schema/README.md +197 -0
- package/src/registry/skeletons.json +259 -0
- package/src/registry/state.json +137 -0
- package/src/registry/tokens.json +40 -0
- package/src/router/hash.js +17 -0
- package/src/router/history.js +18 -0
- package/src/router/index.js +598 -0
- package/src/ssr/index.js +922 -0
- package/src/state/arrays.js +181 -0
- package/src/state/devtools.js +647 -0
- package/src/state/index.js +498 -0
- package/src/state/middleware.js +288 -0
- package/src/state/scheduler.js +206 -0
- package/src/state/store.js +300 -0
- package/src/tags/index.js +19 -0
- package/src/tannins/auth.js +396 -0
- package/src/test/dom.js +352 -0
- package/src/test/index.js +62 -0
- package/src/test/state.js +306 -0
- package/tools/a11y-audit.js +487 -0
- package/tools/analyzer.js +315 -0
- package/tools/audit.js +706 -0
- package/tools/builder.js +1422 -0
- package/tools/css-extract.js +188 -0
- package/tools/dev-server.js +316 -0
- package/tools/dts-gen.js +1260 -0
- package/tools/figma-components.js +329 -0
- package/tools/figma-patterns.js +516 -0
- package/tools/figma-plugin/code.js +453 -0
- package/tools/figma-plugin/manifest.json +14 -0
- package/tools/figma-plugin/ui.html +268 -0
- package/tools/figma-render.js +293 -0
- package/tools/figma-tokens.js +712 -0
- package/tools/figma-upload.js +318 -0
- package/tools/generate.js +738 -0
- package/tools/icons.js +133 -0
- package/tools/init-templates.js +265 -0
- package/tools/install-hooks.sh +5 -0
- package/tools/migrations/0.5.0.js +53 -0
- package/tools/migrations/0.6.0.js +95 -0
- package/tools/minify.js +170 -0
- package/tools/pre-commit +4 -0
- package/tools/registry.js +662 -0
- package/tools/reset-playground.js +61 -0
- package/tools/starter-templates/content-site/app.js +49 -0
- package/tools/starter-templates/content-site/essence.js +19 -0
- package/tools/starter-templates/content-site/pages.js +31 -0
- package/tools/starter-templates/ecommerce/app.js +50 -0
- package/tools/starter-templates/ecommerce/essence.js +19 -0
- package/tools/starter-templates/ecommerce/pages.js +31 -0
- package/tools/starter-templates/landing-page/app.js +38 -0
- package/tools/starter-templates/landing-page/essence.js +18 -0
- package/tools/starter-templates/landing-page/pages.js +21 -0
- package/tools/starter-templates/portfolio/app.js +45 -0
- package/tools/starter-templates/portfolio/essence.js +19 -0
- package/tools/starter-templates/portfolio/pages.js +33 -0
- package/tools/starter-templates/saas-dashboard/app.js +70 -0
- package/tools/starter-templates/saas-dashboard/essence.js +19 -0
- package/tools/starter-templates/saas-dashboard/pages.js +31 -0
- package/tools/verify-pack.js +203 -0
- package/types/chart.d.ts +77 -0
- package/types/components.d.ts +587 -0
- package/types/core.d.ts +89 -0
- package/types/css.d.ts +149 -0
- package/types/data.d.ts +238 -0
- package/types/form.d.ts +164 -0
- package/types/i18n.d.ts +51 -0
- package/types/icons.d.ts +27 -0
- package/types/index.d.ts +13 -0
- package/types/router.d.ts +116 -0
- package/types/ssr.d.ts +102 -0
- package/types/state.d.ts +83 -0
- package/types/tags.d.ts +62 -0
- package/types/tannins.d.ts +63 -0
- 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`
|