@nocoo/basalt 3.2.0 → 3.2.4

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 (225) hide show
  1. package/README.md +25 -19
  2. package/ai/COMPATIBILITY.md +41 -36
  3. package/ai/DESIGN.md +73 -56
  4. package/ai/INTEGRATION.md +819 -825
  5. package/ai/RECIPES.md +334 -135
  6. package/ai/USAGE.md +2 -2
  7. package/ai/registry.json +454 -97
  8. package/ai/sources.json +2 -2
  9. package/dist/assets/brand-mark.js +1 -2
  10. package/dist/charts/area.js +1 -2
  11. package/dist/charts/bar.js +1 -2
  12. package/dist/charts/bullet.js +1 -2
  13. package/dist/charts/chart-colors.js +1 -2
  14. package/dist/charts/charts.js +1 -2
  15. package/dist/charts/config.js +1 -2
  16. package/dist/charts/custom-chart.js +1 -2
  17. package/dist/charts/date-navigation.js +1 -2
  18. package/dist/charts/donut.js +1 -2
  19. package/dist/charts/frame.js +3 -4
  20. package/dist/charts/frame.js.map +1 -1
  21. package/dist/charts/funnel.js +1 -2
  22. package/dist/charts/gauge.js +1 -2
  23. package/dist/charts/grouped-bar.js +1 -2
  24. package/dist/charts/heatmap-calendar.js +7 -11
  25. package/dist/charts/heatmap-calendar.js.map +1 -1
  26. package/dist/charts/heatmap-matrix.js +4 -5
  27. package/dist/charts/heatmap-matrix.js.map +1 -1
  28. package/dist/charts/item-list.js +1 -2
  29. package/dist/charts/legend.js +1 -2
  30. package/dist/charts/line.js +1 -2
  31. package/dist/charts/palette.js +1 -2
  32. package/dist/charts/radar.js +1 -2
  33. package/dist/charts/sample.js +1 -2
  34. package/dist/charts/sankey.js +1 -2
  35. package/dist/charts/series.js +1 -2
  36. package/dist/charts/slot-bar.js +1 -2
  37. package/dist/charts/sparkline.js +1 -2
  38. package/dist/charts/stacked-bar.js +1 -2
  39. package/dist/charts/stat-card.js +1 -2
  40. package/dist/charts/timeline.js +1 -2
  41. package/dist/charts/timeseries.js +1 -2
  42. package/dist/charts/tooltip.js +31 -27
  43. package/dist/charts/tooltip.js.map +1 -1
  44. package/dist/components/accordion.js +1 -2
  45. package/dist/components/alert-dialog.js +1 -2
  46. package/dist/components/app-header.js +2 -3
  47. package/dist/components/app-header.js.map +1 -1
  48. package/dist/components/app-shell.js +2 -3
  49. package/dist/components/app-shell.js.map +1 -1
  50. package/dist/components/approval-card.js +1 -2
  51. package/dist/components/autocomplete.js +1 -2
  52. package/dist/components/avatar.js +1 -2
  53. package/dist/components/badge.js +1 -2
  54. package/dist/components/banner.js +1 -2
  55. package/dist/components/basalt-mark.js +1 -2
  56. package/dist/components/battery-meter.js +1 -2
  57. package/dist/components/breadcrumbs.js +1 -2
  58. package/dist/components/button.d.ts +1 -1
  59. package/dist/components/button.js +3 -3
  60. package/dist/components/button.js.map +1 -1
  61. package/dist/components/chat-bubble.js +1 -2
  62. package/dist/components/chat-composer.js +2 -3
  63. package/dist/components/chat-composer.js.map +1 -1
  64. package/dist/components/chat-header.js +1 -2
  65. package/dist/components/chat-inbox.js +1 -2
  66. package/dist/components/chat-markdown.js +1 -2
  67. package/dist/components/chat-message.js +2 -3
  68. package/dist/components/chat-message.js.map +1 -1
  69. package/dist/components/checkbox.js +1 -2
  70. package/dist/components/clipboard-text.js +1 -2
  71. package/dist/components/code.js +2 -3
  72. package/dist/components/code.js.map +1 -1
  73. package/dist/components/collapsible.js +1 -2
  74. package/dist/components/collapsible.js.map +1 -1
  75. package/dist/components/combobox.js +1 -2
  76. package/dist/components/command-palette.js +3 -4
  77. package/dist/components/command-palette.js.map +1 -1
  78. package/dist/components/confirm-dialog.js +3 -12
  79. package/dist/components/confirm-dialog.js.map +1 -1
  80. package/dist/components/context-cards.js +2 -3
  81. package/dist/components/context-cards.js.map +1 -1
  82. package/dist/components/context-menu.js +2 -3
  83. package/dist/components/context-menu.js.map +1 -1
  84. package/dist/components/data-table.js +129 -131
  85. package/dist/components/data-table.js.map +1 -1
  86. package/dist/components/date-picker.js +7 -18
  87. package/dist/components/date-picker.js.map +1 -1
  88. package/dist/components/delete-resource.js +1 -2
  89. package/dist/components/description-list.js +1 -2
  90. package/dist/components/dialog.js +1 -2
  91. package/dist/components/diff-table.js +2 -3
  92. package/dist/components/diff-table.js.map +1 -1
  93. package/dist/components/dock.js +1 -2
  94. package/dist/components/dock.js.map +1 -1
  95. package/dist/components/dropdown-menu.js +2 -3
  96. package/dist/components/dropdown-menu.js.map +1 -1
  97. package/dist/components/editable-nav-item.js +3 -4
  98. package/dist/components/editable-nav-item.js.map +1 -1
  99. package/dist/components/empty.js +1 -2
  100. package/dist/components/fab.js +1 -2
  101. package/dist/components/field.js +1 -2
  102. package/dist/components/file-dropzone.js +2 -3
  103. package/dist/components/file-dropzone.js.map +1 -1
  104. package/dist/components/filter-bar.js +2 -3
  105. package/dist/components/filter-bar.js.map +1 -1
  106. package/dist/components/flow.js +1 -2
  107. package/dist/components/grid.js +1 -2
  108. package/dist/components/hover-card.js +1 -2
  109. package/dist/components/icon-picker.js +4 -5
  110. package/dist/components/icon-picker.js.map +1 -1
  111. package/dist/components/inline-editable.js +2 -3
  112. package/dist/components/inline-editable.js.map +1 -1
  113. package/dist/components/input-area.js +1 -2
  114. package/dist/components/input-group.js +4 -5
  115. package/dist/components/input-group.js.map +1 -1
  116. package/dist/components/input.js +1 -2
  117. package/dist/components/label.js +1 -2
  118. package/dist/components/layer-card.js +3 -3
  119. package/dist/components/layer-card.js.map +1 -1
  120. package/dist/components/link.js +2 -3
  121. package/dist/components/link.js.map +1 -1
  122. package/dist/components/loader.js +1 -2
  123. package/dist/components/loading-screen.js +1 -2
  124. package/dist/components/menu-bar.js +3 -4
  125. package/dist/components/menu-bar.js.map +1 -1
  126. package/dist/components/meter.js +1 -2
  127. package/dist/components/multi-select.js +2 -3
  128. package/dist/components/multi-select.js.map +1 -1
  129. package/dist/components/navigation-menu.js +1 -2
  130. package/dist/components/overlay.d.ts +3 -6
  131. package/dist/components/overlay.js +4 -5
  132. package/dist/components/overlay.js.map +1 -1
  133. package/dist/components/page-header.js +1 -2
  134. package/dist/components/pagination.js +1 -2
  135. package/dist/components/popover.js +1 -2
  136. package/dist/components/prompt-bar.js +1 -2
  137. package/dist/components/radio.js +2 -7
  138. package/dist/components/radio.js.map +1 -1
  139. package/dist/components/recommendation-card.js +1 -2
  140. package/dist/components/resource-list.js +1 -2
  141. package/dist/components/responsive-master-detail.js +3 -4
  142. package/dist/components/responsive-master-detail.js.map +1 -1
  143. package/dist/components/scroll-area.js +2 -3
  144. package/dist/components/scroll-area.js.map +1 -1
  145. package/dist/components/section-rule.js +1 -2
  146. package/dist/components/segment-control.js +2 -3
  147. package/dist/components/segment-control.js.map +1 -1
  148. package/dist/components/select.js +2 -3
  149. package/dist/components/select.js.map +1 -1
  150. package/dist/components/sensitive-input.js +2 -3
  151. package/dist/components/sensitive-input.js.map +1 -1
  152. package/dist/components/separator.js +1 -2
  153. package/dist/components/sheet.js +1 -2
  154. package/dist/components/sidebar.d.ts +53 -19
  155. package/dist/components/sidebar.js +201 -100
  156. package/dist/components/sidebar.js.map +1 -1
  157. package/dist/components/skeleton-line.js +1 -2
  158. package/dist/components/slider.js +1 -2
  159. package/dist/components/stat-strip.js +1 -2
  160. package/dist/components/switch.js +1 -2
  161. package/dist/components/table-of-contents.js +1 -2
  162. package/dist/components/table-pager.js +1 -2
  163. package/dist/components/table.js +2 -3
  164. package/dist/components/table.js.map +1 -1
  165. package/dist/components/tabs.js +2 -3
  166. package/dist/components/tabs.js.map +1 -1
  167. package/dist/components/tag-badge.js +1 -2
  168. package/dist/components/tag-color-picker.js +3 -4
  169. package/dist/components/tag-color-picker.js.map +1 -1
  170. package/dist/components/text.js +1 -2
  171. package/dist/components/theme-toggle.js +1 -2
  172. package/dist/components/thinking.js +1 -2
  173. package/dist/components/toast.js +2 -3
  174. package/dist/components/toast.js.map +1 -1
  175. package/dist/components/toggle-group.js +1 -2
  176. package/dist/components/toggle.js +1 -2
  177. package/dist/components/tool-chips.js +1 -2
  178. package/dist/components/toolbar.js +9 -10
  179. package/dist/components/toolbar.js.map +1 -1
  180. package/dist/components/tooltip.js +1 -2
  181. package/dist/components/typeahead-field.js +6 -21
  182. package/dist/components/typeahead-field.js.map +1 -1
  183. package/dist/components/upload-queue.js +1 -2
  184. package/dist/index.d.ts +1 -1
  185. package/dist/index.js +11 -11
  186. package/dist/models/agent-feedback.js +1 -2
  187. package/dist/models/avatar.js +1 -2
  188. package/dist/models/code.js +1 -2
  189. package/dist/models/context-cards.js +1 -2
  190. package/dist/models/diff-table.js +1 -2
  191. package/dist/models/recommendation.js +1 -2
  192. package/dist/providers/accent.js +2 -7
  193. package/dist/providers/accent.js.map +1 -1
  194. package/dist/providers/link.js +1 -2
  195. package/dist/providers/theme.js +2 -7
  196. package/dist/providers/theme.js.map +1 -1
  197. package/dist/styles/base.css +71 -0
  198. package/dist/styles/standalone.css +127 -98
  199. package/dist/styles/tailwind.css +10 -2
  200. package/dist/styles/tokens.css +55 -7
  201. package/dist/utils/cn.js +1 -2
  202. package/dist/utils/control-surface.js +1 -2
  203. package/dist/utils/navigation.d.ts +2 -2
  204. package/dist/utils/navigation.js +2 -3
  205. package/dist/utils/navigation.js.map +1 -1
  206. package/dist/utils/preference-store.js +1 -2
  207. package/dist/utils/selection-indicator.js +1 -2
  208. package/dist/utils/use-checkable-group.js +1 -2
  209. package/dist/utils/use-hover-highlight.js +2 -3
  210. package/dist/utils/use-hover-highlight.js.map +1 -1
  211. package/dist/viewmodels/use-approval-card.js +11 -13
  212. package/dist/viewmodels/use-approval-card.js.map +1 -1
  213. package/dist/viewmodels/use-chat-composer.js +1 -2
  214. package/dist/viewmodels/use-chat-message.js +1 -2
  215. package/dist/viewmodels/use-code-block.js +1 -2
  216. package/dist/viewmodels/use-code-block.js.map +1 -1
  217. package/dist/viewmodels/use-context-cards.js +1 -2
  218. package/dist/viewmodels/use-diff-table.js +1 -2
  219. package/dist/viewmodels/use-diff-table.js.map +1 -1
  220. package/dist/viewmodels/use-loader.js +1 -2
  221. package/dist/viewmodels/use-recommendation-card.js +1 -2
  222. package/dist/viewmodels/use-recommendation-card.js.map +1 -1
  223. package/dist/viewmodels/use-thinking.js +1 -2
  224. package/dist/viewmodels/use-tool-chips.js +1 -2
  225. package/package.json +1 -1
package/README.md CHANGED
@@ -52,12 +52,14 @@ Import order is strict. Register Basalt tokens before the Tailwind framework imp
52
52
  @import "tailwindcss";
53
53
 
54
54
  @layer base {
55
- html, body, #root {
56
- height: 100%;
57
- }
58
- body {
59
- @apply bg-basalt-background text-basalt-foreground antialiased;
60
- }
55
+ html,
56
+ body,
57
+ #root {
58
+ height: 100%;
59
+ }
60
+ body {
61
+ @apply bg-basalt-background text-basalt-foreground antialiased;
62
+ }
61
63
  }
62
64
  ```
63
65
 
@@ -79,21 +81,23 @@ Standalone CSS contains compiled design tokens, scoped control classes, keyframe
79
81
  ## Import Architecture
80
82
 
81
83
  ### 1. Root Barrel (`@nocoo/basalt`)
84
+
82
85
  Contains lightweight base components, inputs, layout surfaces, and providers:
83
86
  ```tsx compile:pkg-readme-root-import
84
87
  import {
85
- Button,
86
- Input,
87
- LayerCard,
88
- ThemeProvider,
89
- ThemeToggle,
90
- Tooltip,
91
- Toast,
92
- Sidebar
88
+ Button,
89
+ Input,
90
+ LayerCard,
91
+ ThemeProvider,
92
+ ThemeToggle,
93
+ Tooltip,
94
+ Toast,
95
+ Sidebar,
93
96
  } from "@nocoo/basalt";
94
97
  ```
95
98
 
96
99
  ### 2. Granular Subpaths (`@nocoo/basalt/components/*`, `@nocoo/basalt/charts/*`, `@nocoo/basalt/providers/*`)
100
+
97
101
  Keeps initial bundle size small by isolating complex or specialized dependencies:
98
102
  ```tsx compile:pkg-readme-granular-import
99
103
  import { DatePicker } from "@nocoo/basalt/components/date-picker";
@@ -107,6 +111,7 @@ import { useTheme } from "@nocoo/basalt/providers/theme";
107
111
  ## Framework Integration Highlights
108
112
 
109
113
  ### React 19 & Next.js Client Boundaries
114
+
110
115
  Basalt controls require browser event listeners and React context. When using Next.js App Router, render Basalt components within a client module:
111
116
 
112
117
  ```tsx compile:pkg-readme-client-app
@@ -116,15 +121,16 @@ Basalt controls require browser event listeners and React context. When using Ne
116
121
  import { Button, ThemeProvider } from "@nocoo/basalt";
117
122
 
118
123
  export function BasaltApp() {
119
- return (
120
- <ThemeProvider>
121
- <Button variant="default">Client Control</Button>
122
- </ThemeProvider>
123
- );
124
+ return (
125
+ <ThemeProvider>
126
+ <Button variant="default">Client Control</Button>
127
+ </ThemeProvider>
128
+ );
124
129
  }
125
130
  ```
126
131
 
127
132
  ### Server-Side Theme Pre-Hydration
133
+
128
134
  To eliminate theme flashing (FOUC), inject the theme class before React renders:
129
135
 
130
136
  ```html
@@ -30,6 +30,7 @@ The initial public baseline is locked to version **`2.0.3`** (commit `e61efc1`).
30
30
  - `@nocoo/basalt/styles/standalone` (Zero-dependency compiled tokens and utility CSS)
31
31
 
32
32
  ### Machine Verification
33
+
33
34
  All 110 entrypoints, their export targets, 3 CSS paths, and all 572 exported symbols (with value/type fidelity) are cataloged in `packages/basalt/public-api-baseline.json` and verified via `packages/basalt/scripts/verify-public-api.ts`. No entrypoint or public symbol present in this baseline may be removed or have its value/type identity degraded without a major version bump.
34
35
 
35
36
  ---
@@ -37,8 +38,9 @@ All 110 entrypoints, their export targets, 3 CSS paths, and all 572 exported sym
37
38
  ## 2. Export Architecture & Root vs. Granular Principles
38
39
 
39
40
  ### Root Barrel (`@nocoo/basalt`)
41
+
40
42
  The root barrel is reserved for lightweight, frequently-used leaves, standard inputs, core surfaces, and global providers.
41
- - **Included on root:** `Button`, `Input`, `Checkbox`, `Switch`, `Badge`, `Text`, `Label`, `Separator`, `LayerCard`, `DescriptionList`, `Sidebar`, `Dialog`, `Sheet`, `Popover`, `Tooltip`, `Toast`, `ThemeProvider`, `ThemeToggle`, `LinkProvider`.
43
+ - **Included on root:** `Button`, `Input`, `Checkbox`, `Switch`, `Badge`, `Text`, `Label`, `Separator`, `LayerCard`, `DescriptionList`, `Sidebar`, `SidebarBrand`, `SidebarToggle`, `Dialog`, `Sheet`, `Popover`, `Tooltip`, `Toast`, `ThemeProvider`, `ThemeToggle`, `LinkProvider`.
42
44
  - **Excluded from root:**
43
45
  - Radio inputs (`@nocoo/basalt/components/radio`)
44
46
  - Heavy visualization libraries (`@nocoo/basalt/charts/*`)
@@ -47,6 +49,7 @@ The root barrel is reserved for lightweight, frequently-used leaves, standard in
47
49
  - Application frame layout chrome (`@nocoo/basalt/components/app-shell`, `app-header`, `page-header`, `section-rule`)
48
50
 
49
51
  ### Wildcard Subpaths & Legacy Internal Helpers
52
+
50
53
  <a id="wildcard-subpaths"></a>
51
54
  In `v2.0.3`, wildcard exports (`./components/*`, `./charts/*`, `./providers/*`) exposed all non-test modules in the package, including internal building blocks:
52
55
  - **`@nocoo/basalt/components/typeahead-field`**: Internal composite input controller for autocomplete and combobox (`TypeaheadField`, `TypeaheadItem`). Provides popup list and keyboard navigation wiring.
@@ -64,13 +67,14 @@ In `v2.0.3`, wildcard exports (`./components/*`, `./charts/*`, `./providers/*`)
64
67
 
65
68
  Basalt strictly follows [SemVer 2.0.0](https://semver.org/):
66
69
 
67
- | Change Level | Permitted Changes | Examples |
68
- |---|---|---|
69
- | **PATCH** (`2.0.x`) | Backward-compatible bug fixes, internal optimizations, visual refinements that do not alter layout bounding boxes or break existing snapshots. | Fixing event propagation in `Button`, correcting border contrast ratio, fixing broken types. |
70
- | **MINOR** (`2.x.0`) | Additive, backward-compatible features; new components, new optional props, new granular export paths. | Adding a new `FilterBar` component, introducing an optional `variant` prop to `Badge`. |
71
- | **MAJOR** (`3.0.0`) | Breaking changes to existing public component signatures, DOM/ARIA structures, or removal of deprecated export paths. | Renaming required props, removing legacy wildcard subpaths, updating peer dependency minimums to React 20. |
70
+ | Change Level | Permitted Changes | Examples |
71
+ | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
72
+ | **PATCH** (`2.0.x`) | Backward-compatible bug fixes, internal optimizations, visual refinements that do not alter layout bounding boxes or break existing snapshots. | Fixing event propagation in `Button`, correcting border contrast ratio, fixing broken types. |
73
+ | **MINOR** (`2.x.0`) | Additive, backward-compatible features; new components, new optional props, new granular export paths. | Adding a new `FilterBar` component, introducing an optional `variant` prop to `Badge`. |
74
+ | **MAJOR** (`3.0.0`) | Breaking changes to existing public component signatures, DOM/ARIA structures, or removal of deprecated export paths. | Renaming required props, removing legacy wildcard subpaths, updating peer dependency minimums to React 20. |
72
75
 
73
76
  ### Layout & Default Geometry Policy
77
+
74
78
  Changes to default geometry (such as outer padding, default margins, or intrinsic dimensions) alter the visual balance of consuming applications.
75
79
  - **Bug Fixes vs Breaking Changes:** Correcting an unintended layout collapse or overflow bug (e.g., popup clipping in cards or chart height collapsing to 0px) is considered a patch/minor fix. Altering default container padding (e.g. changing `LayerCard` padding from `none` to `md`) affects caller assumptions.
76
80
  - If default geometry must evolve, before/after migration notes must be published in `CHANGELOG.md`, and backward-compatible props or opt-ins must be offered.
@@ -93,6 +97,7 @@ Basalt chart components build upon modular configuration tokens and series utili
93
97
 
94
98
  <a id="chart-config"></a>
95
99
  ### Chart Configuration (`@nocoo/basalt/charts/config`)
100
+
96
101
  Defines shared tokens, styles, and props for cartesian charts:
97
102
  - **Constants**:
98
103
  - `CHART_TYPE`: Font sizes (`axisFontSize: 11`, `legendFontSize: 12`, `tooltipTitleSize: 12`, `tooltipBodySize: 12`), `tooltipDot: 8`, stroke width (`strokeWidth: 2`), `gridDash: "3 3"`, `gridOpacity: 0.15`, and `areaFillAlpha: 0.2`.
@@ -117,6 +122,7 @@ Defines shared tokens, styles, and props for cartesian charts:
117
122
 
118
123
  <a id="chart-series"></a>
119
124
  ### Chart Series Utilities (`@nocoo/basalt/charts/series`)
125
+
120
126
  Manages series descriptor shapes, point definitions, and data key fallbacks:
121
127
  - **Types**:
122
128
  - `ChartSeriesDescriptor`: `{ key: string, label?: string, color?: string }`.
@@ -133,7 +139,6 @@ Manages series descriptor shapes, point definitions, and data key fallbacks:
133
139
  - `applyLeadColor(items: ChartSeriesDescriptor[], color?: string): ChartSeriesDescriptor[]`: If `color` is provided and the first item does not specify a `color` (`item.color ?? color`), sets the first item's color. Preserves an explicit first-item color.
134
140
  - `xyFallbackKeys(data: Array<{ y2?: number; y3?: number }>): string[]`: Scans data array and dynamically includes `"y2"` and `"y3"` if present, returning `["y", ...]` keys. Compatible with legacy XY series fallback.
135
141
 
136
-
137
142
  ---
138
143
 
139
144
  <a id="6-unreleased-migration-notes-p1p10"></a>
@@ -143,14 +148,14 @@ These notes cover the components, documentation, examples and palette changes in
143
148
 
144
149
  ### Palettes and production URL
145
150
 
146
- | Before | Current behavior | Consumer action |
147
- |---|---|---|
148
- | 24 control accents sharing chart tokens | 12 iMac / iPhone 5C-inspired accents on `--basalt-accent-1…12` | Build pickers from `useAccent().swatches`; do not assume the former count, order, labels, or token values. |
149
- | 24 distinct `CHART_COLORS` entries | Fixed five-color Blue / Pink / Green / Yellow / Gray cycle; the first four match classic control swatches exactly | Replace direct indexing beyond 4 with `getChartColor(index)` or `index % CHART_COLORS.length`. This is an intentional visual and runtime-value change, not a claim that all prior palette behavior is unchanged. |
150
- | Numbered chart tokens and 24 `chart` keys | Existing names retained as aliases of five chart colors | Keep imports; expect repeated colors. Use explicit per-series colors and descriptive legends where five categories are insufficient. |
151
- | Presets only | Optional `AccentProvider.paletteOverrides` with light/dark HSL pairs | Application owns validation and persistence. Overrides affect control swatches and semantic primary, never chart tokens. |
152
- | Ring remainder tied to a series color | Theme-aware neutral `chartMuted` track | No prop migration. Gauge is static under reduced motion and displays 0, partial, and full values without a black remainder. |
153
- | `https://basalt.hexly.ai` | `https://basaltui.com` | Update bookmarks and production links. Browser storage is origin-scoped, so old-domain theme/palette preferences cannot transfer automatically. Local `basalt.dev.hexly.ai` is unchanged. |
151
+ | Before | Current behavior | Consumer action |
152
+ | ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
153
+ | 24 control accents sharing chart tokens | 12 iMac / iPhone 5C-inspired accents on `--basalt-accent-1…12` | Build pickers from `useAccent().swatches`; do not assume the former count, order, labels, or token values. |
154
+ | 24 distinct `CHART_COLORS` entries | Fixed five-color Blue / Pink / Green / Yellow / Gray cycle; the first four match classic control swatches exactly | Replace direct indexing beyond 4 with `getChartColor(index)` or `index % CHART_COLORS.length`. This is an intentional visual and runtime-value change, not a claim that all prior palette behavior is unchanged. |
155
+ | Numbered chart tokens and 24 `chart` keys | Existing names retained as aliases of five chart colors | Keep imports; expect repeated colors. Use explicit per-series colors and descriptive legends where five categories are insufficient. |
156
+ | Presets only | Optional `AccentProvider.paletteOverrides` with light/dark HSL pairs | Application owns validation and persistence. Overrides affect control swatches and semantic primary, never chart tokens. |
157
+ | Ring remainder tied to a series color | Theme-aware neutral `chartMuted` track | No prop migration. Gauge is static under reduced motion and displays 0, partial, and full values without a black remainder. |
158
+ | `https://basalt.hexly.ai` | `https://basaltui.com` | Update bookmarks and production links. Browser storage is origin-scoped, so old-domain theme/palette preferences cannot transfer automatically. Local `basalt.dev.hexly.ai` is unchanged. |
154
159
 
155
160
  Chart gray retains its prior light/dark values. Marks use solid candy colors without contrasting outlines or shadows; their fills are not darkened. Raw candy fills can fall below 3:1 on light surfaces; use descriptive labels and `summary` / `dataAlternative` alongside them. Custom control palettes remain independent.
156
161
 
@@ -160,31 +165,31 @@ v2.1.0 includes the palette count and value changes above as an explicit excepti
160
165
 
161
166
  Persisted accent IDs are normalized on reads and selections:
162
167
 
163
- | Old ID | Retained ID |
164
- |---|---|
165
- | `jade`, `seafoam` | `teal` |
166
- | `vermilion`, `crimson` | `red` |
167
- | `magenta` | `rose` |
168
- | `orchid` | `purple` |
169
- | `cobalt` | `indigo` |
170
- | `steel` | `primary` |
171
- | `cadet` | `sky` |
172
- | `olive` | `lime` |
173
- | `gold` | `amber` |
174
- | `tangerine` | `orange` |
168
+ | Old ID | Retained ID |
169
+ | ---------------------- | ----------- |
170
+ | `jade`, `seafoam` | `teal` |
171
+ | `vermilion`, `crimson` | `red` |
172
+ | `magenta` | `rose` |
173
+ | `orchid` | `purple` |
174
+ | `cobalt` | `indigo` |
175
+ | `steel` | `primary` |
176
+ | `cadet` | `sky` |
177
+ | `olive` | `lime` |
178
+ | `gold` | `amber` |
179
+ | `tangerine` | `orange` |
175
180
 
176
181
  Other retained IDs keep their identity with the new palette values. Unknown IDs fall back to `primary`. Use IDs rather than array positions for saved selections.
177
182
 
178
183
  ### Components and application recipes
179
184
 
180
- | Area | Additions and compatibility | Application responsibility |
181
- |---|---|---|
182
- | Resource screens | Additive DataTable sorting, manual processing/pagination, header and retry options; ResourceList state/toolbar/bulk/footer slots; granular BatteryMeter | Queries, authorization, selection policy, total counts and backend requests |
183
- | Filtering | MultiSelect, FilterBar and FilterChip, including controlled search and native form/reset support | Search results and business filter state |
184
- | Uploads | FileDropzone plus UploadQueue/UploadItem, validation, progress and action callbacks | Transport, retry/cancel, object URL lifetime and server validation |
185
- | Editing and navigation | InlineEditable, EditableNavItem/FolderNavItem, IconPicker, TagBadge/TagColorPicker, ResponsiveMasterDetail | Persistence, routing, allowed icons, stable item IDs and tree policy |
186
- | App templates | Complete AppFrame, Login and Resources modules in `ai/RECIPES.md`, compiled from the installed package | Router adapters, authentication, permissions, API calls and real data |
187
- | Chart details | Values heatmap and Timeline rails use fixed chart tokens; optional `TimelineEvent.textColor` pairs custom event backgrounds with readable titles and subtitles | Choose text color for custom backgrounds; omitted `textColor` keeps the legacy white treatment for explicit event colors |
188
- | Documentation | Generated props retain generics, callback/constructor union precedence, readonly arrays and tuples | Use the declared type parameters; generated text no longer substitutes `unknown` for caller data types |
185
+ | Area | Additions and compatibility | Application responsibility |
186
+ | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
187
+ | Resource screens | Additive DataTable sorting, manual processing/pagination, header and retry options; ResourceList state/toolbar/bulk/footer slots; granular BatteryMeter | Queries, authorization, selection policy, total counts and backend requests |
188
+ | Filtering | MultiSelect, FilterBar and FilterChip, including controlled search and native form/reset support | Search results and business filter state |
189
+ | Uploads | FileDropzone plus UploadQueue/UploadItem, validation, progress and action callbacks | Transport, retry/cancel, object URL lifetime and server validation |
190
+ | Editing and navigation | InlineEditable, EditableNavItem/FolderNavItem, IconPicker, TagBadge/TagColorPicker, ResponsiveMasterDetail | Persistence, routing, allowed icons, stable item IDs and tree policy |
191
+ | App templates | Complete AppFrame, Login and Resources modules in `ai/RECIPES.md`, compiled from the installed package | Router adapters, authentication, permissions, API calls and real data |
192
+ | Chart details | Values heatmap and Timeline rails use fixed chart tokens; optional `TimelineEvent.textColor` pairs custom event backgrounds with readable titles and subtitles | Choose text color for custom backgrounds; omitted `textColor` keeps the legacy white treatment for explicit event colors |
193
+ | Documentation | Generated props retain generics, callback/constructor union precedence, readonly arrays and tuples | Use the declared type parameters; generated text no longer substitutes `unknown` for caller data types |
189
194
 
190
195
  New workflow modules stay on granular subpaths. Existing string table headers, legacy chart `y`/`y2`/`y3` inputs, required ResourceList data, and explicit series colors remain supported. No automatic downstream migration is performed. A full tree adapter, draggable SplitPane, and maps remain outside this release scope.
package/ai/DESIGN.md CHANGED
@@ -28,23 +28,23 @@ different density; opening the page layout must not enlarge compact controls.
28
28
 
29
29
  ## Spacing
30
30
 
31
- | Step | Token | Value | Reference |
32
- | --- | --- | --- | --- |
33
- | Extra small | `--basalt-space-xs` | `.125rem` | 2px |
34
- | Small | `--basalt-space-sm` | `.25rem` | 4px |
35
- | Medium | `--basalt-space-md` | `.375rem` | 6px |
36
- | Large / default | `--basalt-space-lg`, `--basalt-space-default` | `.5rem` | 8px |
31
+ | Step | Token | Value | Reference |
32
+ | --------------- | --------------------------------------------- | --------- | --------- |
33
+ | Extra small | `--basalt-space-xs` | `.125rem` | 2px |
34
+ | Small | `--basalt-space-sm` | `.25rem` | 4px |
35
+ | Medium | `--basalt-space-md` | `.375rem` | 6px |
36
+ | Large / default | `--basalt-space-lg`, `--basalt-space-default` | `.5rem` | 8px |
37
37
 
38
38
  These four steps belong to control content: labels, icons, fields, actions,
39
39
  selectable rows, menus and banner content. They are not the maximum spacing for a page
40
40
  or content surface. Card and layout boundaries use their own four-step roles:
41
41
 
42
- | Step | Card token | Layout token | Value | Reference |
43
- | --- | --- | --- | --- | --- |
44
- | Small | `--basalt-space-card-sm` | `--basalt-space-layout-sm` | `.75rem` | 12px |
45
- | Medium | `--basalt-space-card` | `--basalt-space-layout` | `1rem` | 16px |
46
- | Large | `--basalt-space-card-lg` | `--basalt-space-layout-lg` | `1.5rem` | 24px |
47
- | Extra large | `--basalt-space-card-xl` | `--basalt-space-layout-xl` | `2rem` | 32px |
42
+ | Step | Card token | Layout token | Value | Reference |
43
+ | ----------- | ------------------------ | -------------------------- | -------- | --------- |
44
+ | Small | `--basalt-space-card-sm` | `--basalt-space-layout-sm` | `.75rem` | 12px |
45
+ | Medium | `--basalt-space-card` | `--basalt-space-layout` | `1rem` | 16px |
46
+ | Large | `--basalt-space-card-lg` | `--basalt-space-layout-lg` | `1.5rem` | 24px |
47
+ | Extra large | `--basalt-space-card-xl` | `--basalt-space-layout-xl` | `2rem` | 32px |
48
48
 
49
49
  Card padding defaults to medium. LayerCard headers flow into Body by default:
50
50
  16px horizontal/top insets, no bottom inset and an 8px internal gap, without a divider.
@@ -64,10 +64,12 @@ Wrapping and enlarged text may grow the header; do not impose a fixed height.
64
64
 
65
65
  Layout grids default to medium (16px). ContentIsland owns 16px insets on mobile,
66
66
  24px on tablets and 24px vertical / 32px horizontal from 1024px. The catalog shell
67
- keeps 8px outside the island on mobile and 12px from 768px. Ordinary application
68
- sections use 24px gaps, including catalog documentation. SectionRule
69
- uses 12px content separation for compact labels and 16px for reading headings. `Grid gap="sm|md|lg|xl"`
70
- and semantic utilities such as `gap-basalt-layout-xl` select these tiers.
67
+ keeps 8px outside the island on mobile and 12px from 768px; AppHeader uses the
68
+ same 8px / 12px horizontal inset to align with that outer island boundary, not its
69
+ inner content edge. Ordinary application sections use 24px gaps, including catalog
70
+ documentation. SectionRule uses 12px content separation for compact labels and
71
+ 16px for reading headings. `Grid gap="sm|md|lg|xl"` and semantic utilities such as
72
+ `gap-basalt-layout-xl` select these tiers.
71
73
  There is no universal height for either category.
72
74
 
73
75
  Dialog and AlertDialog panels use 16px outer insets on mobile and 24px from
@@ -100,8 +102,9 @@ TagBadge uses 6px horizontal padding at `sm` and 8px at `md`.
100
102
  All nonzero padding, margin and gaps use the appropriate category scale or aliases.
101
103
  Zero, auto margins and hairline seams are structural exceptions, not new density
102
104
  levels. Negative margins may cancel an owned boundary, not invent a new gap.
103
- Use `p-basalt-card`, `gap-basalt-layout`, `gap-basalt-space-sm`, etc. Default nav
104
- insets and field gaps remain 8px. Selectable rows use 8px horizontal / 6px vertical
105
+ Container spacing uses 4px multiples (4/8/12/16/24/32px); 2px and 6px
106
+ control-internal spacing remains unchanged. Use `p-basalt-card`, `gap-basalt-layout`,
107
+ `gap-basalt-space-sm`, etc. Default nav insets and field gaps remain 8px. Selectable rows use 8px horizontal / 6px vertical
105
108
  insets and an 8px icon gap. Sidebar destinations alone use 8px vertical padding;
106
109
  menus, editable navigation and chat rows retain their compact role. One component
107
110
  owns each boundary: nested sections must not accidentally double the same inset.
@@ -117,15 +120,15 @@ media and shell widths. They are not an alternative margin/padding scale.
117
120
  Choose a category before implementing a component. A composite's root and its
118
121
  children can have different categories. Root module inventory:
119
122
 
120
- | Category | Reference | Component modules |
121
- | --- | --- | --- |
122
- | Inline | 22px single line | `badge`, `tag-badge` |
123
- | Action | 34px default | `button`, `input`, `select`, `toggle`, `toggle-group`, `segment-control`, `autocomplete`, `combobox`, `typeahead-field`, `sensitive-input`, `input-group`, `input-area`, `clipboard-text`, `inline-editable`, `multi-select`, `date-picker`, `icon-picker`, `tag-color-picker`, `theme-toggle`, `pagination`, `table-pager` |
124
- | Banner | 38px single line | `banner`, `toast` |
125
- | Card / content surface | Natural | `layer-card`, `approval-card`, `context-cards`, `recommendation-card`, `chat-bubble`, `code`, `empty`, `file-dropzone`, `resource-list`, `upload-queue` |
126
- | Layout / composite | Natural | `accordion`, `collapsible`, `app-header`, `app-shell`, `sidebar`, `navigation-menu`, `editable-nav-item`, `breadcrumbs`, `page-header`, `section-rule`, `filter-bar`, `toolbar`, `grid`, `flow`, `field`, `description-list`, `table`, `data-table`, `diff-table`, `tabs`, `table-of-contents`, `responsive-master-detail`, `scroll-area`, `separator`, `stat-strip`, `chat-header`, `chat-inbox`, `chat-message`, `chat-markdown`, `chat-composer`, `prompt-bar`, `thinking`, `tool-chips`, `loading-screen` |
127
- | Overlay composite | Natural; triggers/actions follow Action | `dialog`, `alert-dialog`, `confirm-dialog`, `delete-resource`, `sheet`, `dock`, `popover`, `hover-card`, `tooltip`, `dropdown-menu`, `context-menu`, `menu-bar`, `command-palette` |
128
- | Graphic / text primitive | Content-specific | `avatar`, `basalt-mark`, `battery-meter`, `meter`, `loader`, `skeleton-line`, `text`, `label`, `link`, `checkbox`, `radio`, `switch`, `slider`, `fab` |
123
+ | Category | Reference | Component modules |
124
+ | ------------------------ | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
125
+ | Inline | 22px single line | `badge`, `tag-badge` |
126
+ | Action | 34px default | `button`, `input`, `select`, `toggle`, `toggle-group`, `segment-control`, `autocomplete`, `combobox`, `typeahead-field`, `sensitive-input`, `input-group`, `input-area`, `clipboard-text`, `inline-editable`, `multi-select`, `date-picker`, `icon-picker`, `tag-color-picker`, `theme-toggle`, `pagination`, `table-pager` |
127
+ | Banner | 38px single line | `banner`, `toast` |
128
+ | Card / content surface | Natural | `layer-card`, `approval-card`, `context-cards`, `recommendation-card`, `chat-bubble`, `code`, `empty`, `file-dropzone`, `resource-list`, `upload-queue` |
129
+ | Layout / composite | Natural | `accordion`, `collapsible`, `app-header`, `app-shell`, `sidebar`, `navigation-menu`, `editable-nav-item`, `breadcrumbs`, `page-header`, `section-rule`, `filter-bar`, `toolbar`, `grid`, `flow`, `field`, `description-list`, `table`, `data-table`, `diff-table`, `tabs`, `table-of-contents`, `responsive-master-detail`, `scroll-area`, `separator`, `stat-strip`, `chat-header`, `chat-inbox`, `chat-message`, `chat-markdown`, `chat-composer`, `prompt-bar`, `thinking`, `tool-chips`, `loading-screen` |
130
+ | Overlay composite | Natural; triggers/actions follow Action | `dialog`, `alert-dialog`, `confirm-dialog`, `delete-resource`, `sheet`, `dock`, `popover`, `hover-card`, `tooltip`, `dropdown-menu`, `context-menu`, `menu-bar`, `command-palette` |
131
+ | Graphic / text primitive | Content-specific | `avatar`, `basalt-mark`, `battery-meter`, `meter`, `loader`, `skeleton-line`, `text`, `label`, `link`, `checkbox`, `radio`, `switch`, `slider`, `fab` |
129
132
 
130
133
  Graphic primitives preserve their intrinsic shape: a checkbox mark is not a
131
134
  34px square and an avatar is not a text input. Their containing action rows use
@@ -150,7 +153,8 @@ The shared classes calculate natural size as follows:
150
153
  - `basalt-action`: 14px font, 24px line box + 4px vertical padding each side +
151
154
  1px border each side = 34px. Explicit `sm` (28px) and `lg` (40px) are shared
152
155
  size variants, not ad-hoc height utilities. Icon-only actions use a minimum
153
- derived from `1lh`, not a fixed height; icons are normally 14px.
156
+ derived from `1lh`; `icon-sm` is an actual 28px by 28px control, while `icon`
157
+ retains the default 34px contract. Icons are normally 14px.
154
158
  - `basalt-banner`: 14px font, 22px line box + 12px vertical padding = 46px.
155
159
  The outer boundary uses 16px horizontal / 12px vertical panel insets; internal
156
160
  icons and actions retain compact control spacing.
@@ -158,7 +162,9 @@ The shared classes calculate natural size as follows:
158
162
  - Action rows: 22px text line (`--basalt-line-row`) + 6px vertical padding each
159
163
  side = 34px. Body prose/code retain the 20px line box. Compound inputs own one
160
164
  outer border; inset children do not add another. Segmented tracks keep 14px text
161
- on a 20px line box: 30px items + 2px track inset per side = 34px.
165
+ on a 20px line box: 30px items + 2px track inset per side = 34px. A horizontally
166
+ scrolling SegmentControl viewport adds 4px clearance on all four edges and uses
167
+ matching scroll-padding; this does not enlarge the ToggleGroup track.
162
168
  - Sidebar destination and disclosure rows use the same 22px line box with 8px
163
169
  vertical padding, yielding 38px reference rows without changing shared controls.
164
170
  - Cards, dialogs and layout containers have no mandatory height. Textareas and
@@ -186,12 +192,12 @@ interaction contracts; do not change their containment as a global workaround.
186
192
  tokens below, with `--basalt-leading-*` for line boxes. Do not shrink text to
187
193
  fit a fixed box or introduce responsive intermediate sizes.
188
194
 
189
- | Tier | Reference | Token / utility | Roles |
190
- | --- | --- | --- | --- |
191
- | Auxiliary | 12px / `.75rem` | `--basalt-text-sm` / `text-basalt-sm` | Badges, table column headers, field hints/errors, sidebar uppercase group/partition labels, secondary metadata, chart axes/legends/tooltips, inline code and code block source (`font-mono`) |
192
- | Body | 14px / `.875rem` | `--basalt-text-base` / `text-basalt-base` | Body prose, all interactive controls and option rows (including ApprovalCard, RecommendationCard, Table cells and ContextCards), card/panel titles (LayerCard.Header, CodeBlock title, ContextCards header, StatCard title, Popover title and Empty title), PageHeader descriptions and modal/sheet descriptions (DialogDescription, AlertDialogDescription and SheetDescription) |
193
- | Section | 18px / `1.125rem` | `--basalt-text-xl` / `text-basalt-xl` | Section/category h2/h3 headings, SectionRule variant="heading" h2, modal/drawer titles (DialogTitle, AlertDialogTitle and SheetTitle) |
194
- | Page / metric | 24px / `1.5rem` | `--basalt-text-3xl` / `text-basalt-3xl` | Page main title (PageHeader h1) and primary KPI numerals (StatCard value, StatStrip value and dashboard metrics) |
195
+ | Tier | Reference | Token / utility | Roles |
196
+ | ------------- | ----------------- | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
197
+ | Auxiliary | 12px / `.75rem` | `--basalt-text-sm` / `text-basalt-sm` | Badges, table column headers, field hints/errors, sidebar uppercase group/partition labels, secondary metadata, chart axes/legends/tooltips, inline code and code block source (`font-mono`) |
198
+ | Body | 14px / `.875rem` | `--basalt-text-base` / `text-basalt-base` | Body prose, all interactive controls and option rows (including ApprovalCard, RecommendationCard, Table cells and ContextCards), card/panel titles (LayerCard.Header, CodeBlock title, ContextCards header, StatCard title, Popover title and Empty title), PageHeader descriptions and modal/sheet descriptions (DialogDescription, AlertDialogDescription and SheetDescription) |
199
+ | Section | 18px / `1.125rem` | `--basalt-text-xl` / `text-basalt-xl` | Section/category h2/h3 headings, SectionRule variant="heading" h2, modal/drawer titles (DialogTitle, AlertDialogTitle and SheetTitle) |
200
+ | Page / metric | 24px / `1.5rem` | `--basalt-text-3xl` / `text-basalt-3xl` | Page main title (PageHeader h1) and primary KPI numerals (StatCard value, StatStrip value and dashboard metrics) |
195
201
 
196
202
  The only independent display-scale exemptions are LandingPage
197
203
  (`src/styles/landing.css`), LoginPage (`src/pages/LoginPage.tsx`) and the 404
@@ -235,18 +241,29 @@ interaction contracts; do not change their containment as a global workaround.
235
241
  expose that state instead of switching to a gray secondary variant alone.
236
242
  Checkbox/radio/switch glyphs, primary/destructive actions and semantic diff
237
243
  colors keep their semantic fills. Color swatches retain their actual palette.
238
- - Keyboard focus is separate from selection. Navigation uses an inset focus ring
239
- so overflow and collapsed groups cannot clip it. Disabled controls retain
244
+ - Keyboard focus is separate from selection. Ordinary focus uses a 1px theme
245
+ `--basalt-ring` stroke with a 2px outer offset; flush structural targets use the
246
+ same stroke inset with a 2px negative offset (`basalt-focus-inset`). External
247
+ focus targets need 4px clearance inside their nearest clipping container;
248
+ existing padding of at least 4px counts and is not stacked. Forced-colors mode
249
+ may use a 2px system outline. Focus causes no layout shift, and error indicators
250
+ remain independent. Navigation uses inset focus so overflow and collapsed groups
251
+ cannot clip it. Disabled controls retain
240
252
  subdued opacity and cannot become moving-hover targets; selection never lowers
241
253
  opacity or mutes its primary label.
242
254
  - SidebarPartition is a major section label (12px / `text-basalt-sm`, semibold,
243
255
  uppercase, 16px top spacing). SidebarGroup is a compact disclosure label
244
256
  (12px / `text-basalt-sm`, semibold, uppercase, 22px line box, 8px inset, 12px
245
257
  group separation), not a destination or hover-highlight target. Ordinary sidebar
246
- destinations use 14px (`text-basalt-base`) text and 8px row insets. Groups preserve
247
- keyboard expansion and chevron motion; collapsed rails omit labels. Icons stay centered on the 68px collapsed rail, and the brand
248
- mark keeps the same center when expanded. Do not trade that axis for a global
249
- left-edge alignment.
258
+ destinations use 14px (`text-basalt-base`) text and 8px row insets. Sidebar owns
259
+ a private 24px content column: its 16px region inset composes with the row's
260
+ 8px inset, without changing the shared 8px navigation inset used by other
261
+ components. Header, footer, search, group and destination content align to that
262
+ column. `SidebarBrand` reserves a fixed 20px logo slot at 24px, keeping the logo
263
+ center on the 68px rail axis during the width transition. Its action remains
264
+ reachable below the header when collapsed. Groups preserve keyboard expansion
265
+ and chevron motion; collapsed rails omit labels. Icons stay centered on the 68px
266
+ collapsed rail. Do not trade the rail axis for a global left-edge alignment.
250
267
  - Content wells use the actual surface hierarchy, not selected tokens or muted
251
268
  fills. Skeletons, progress tracks and decorative code marks may remain subdued.
252
269
  Skeleton shimmer uses foreground at 10% opacity so it remains visible in both themes.
@@ -354,12 +371,12 @@ islands rather than wrapping the whole page in another card.
354
371
  Alignment is local to a meaningful scope, not a requirement that every text node
355
372
  start at the page edge. Establish the outer rail before composing the inner one:
356
373
 
357
- | Scope | Alignment rule |
358
- | --- | --- |
359
- | Page | PageHeader and region edges share the ContentIsland rail |
360
- | Surface | Header, body and footer share their panel inset |
361
- | Field or column | Labels, controls and values follow that group's start or end rail |
362
- | Semantic slot | Checkboxes, row numbers and status glyphs reserve their own consistent space |
374
+ | Scope | Alignment rule |
375
+ | --------------- | ---------------------------------------------------------------------------- |
376
+ | Page | PageHeader and region edges share the ContentIsland rail |
377
+ | Surface | Header, body and footer share their panel inset |
378
+ | Field or column | Labels, controls and values follow that group's start or end rail |
379
+ | Semantic slot | Checkboxes, row numbers and status glyphs reserve their own consistent space |
363
380
 
364
381
  Related text keeps its rail across sorting, selection and missing optional icons.
365
382
  Numeric headings and values share the end rail; a sort glyph sits inward of that
@@ -395,15 +412,15 @@ content width, not uniformly loose controls or one global density multiplier.
395
412
  Choose the boundary before choosing a spacing token. A valid token on the wrong
396
413
  owner is still a design error:
397
414
 
398
- | Boundary | Owner | Composition |
399
- | --- | --- | --- |
400
- | Page/region separation | `ShowcasePage` / `SectionRule` | Layout gaps, no second page inset |
401
- | Painted content or navigation panel | `LayerCard` | Header and Body slots, never an unpadded painted div |
402
- | Full-width disclosure heading | `LayerCard.Header asChild` | Slot onto `CollapsibleTrigger`; one hit target, 16/12px insets |
403
- | Expanded prose or fields | `LayerCard.Body` | Inside `CollapsibleContent unstyled`; one 16px inset |
404
- | Attached code | `CodeBlock attached` / `CodeHighlighted attached` | Direct child of unstyled disclosure; code owns its 16/12px insets |
405
- | Edge-to-edge table/list | Its row/cell components | Direct card child; panel inset on edge cells, no Body with `p-0` or compensating margins |
406
- | Compact status/action strip | `Banner` | Control spacing, not a hand-drawn card |
415
+ | Boundary | Owner | Composition |
416
+ | ----------------------------------- | ------------------------------------------------- | ---------------------------------------------------------------------------------------- |
417
+ | Page/region separation | `ShowcasePage` / `SectionRule` | Layout gaps, no second page inset |
418
+ | Painted content or navigation panel | `LayerCard` | Header and Body slots, never an unpadded painted div |
419
+ | Full-width disclosure heading | `LayerCard.Header asChild` | Slot onto `CollapsibleTrigger`; one hit target, 16/12px insets |
420
+ | Expanded prose or fields | `LayerCard.Body` | Inside `CollapsibleContent unstyled`; one 16px inset |
421
+ | Attached code | `CodeBlock attached` / `CodeHighlighted attached` | Direct child of unstyled disclosure; code owns its 16/12px insets |
422
+ | Edge-to-edge table/list | Its row/cell components | Direct card child; panel inset on edge cells, no Body with `p-0` or compensating margins |
423
+ | Compact status/action strip | `Banner` | Control spacing, not a hand-drawn card |
407
424
 
408
425
  Collapsible owns visibility, focus semantics and motion; it does not add another
409
426
  surface. Its default inset is for plain standalone text. Use `unstyled` when a