basalt-ui 1.37.0 → 1.38.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/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ # [1.38.0](https://github.com/jkrumm/basalt-ui/compare/v1.37.1...v1.38.0) (2026-10-04)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * inset the sidebar footer and seat the rail first icon like the expanded row ([7d9e980](https://github.com/jkrumm/basalt-ui/commit/7d9e980b9c1d6cd062e0d8f43a4800f7002b174d))
7
+ * re-date the forms layout deprecations to 1.39.0 ([fe9e785](https://github.com/jkrumm/basalt-ui/commit/fe9e785439c8404dfe09bdcecb774db758123026))
8
+
9
+
10
+ ### Features
11
+
12
+ * add --basalt-shell-lead-inset for a native title bar over the header lead ([980c2ff](https://github.com/jkrumm/basalt-ui/commit/980c2ff707a39ec150b64d27780b3ceecb98b922))
13
+
14
+ ## [1.37.1](https://github.com/jkrumm/basalt-ui/compare/v1.37.0...v1.37.1) (2026-10-02)
15
+
16
+
17
+ ### Bug Fixes
18
+
19
+ * stop the measured fold folding a row that fits to the sub-pixel ([67aebae](https://github.com/jkrumm/basalt-ui/commit/67aebae33ee966acca555fc98cb609df33a81a6c))
20
+
1
21
  # [1.37.0](https://github.com/jkrumm/basalt-ui/compare/v1.36.1...v1.37.0) (2026-10-02)
2
22
 
3
23
 
package/MIGRATING.md CHANGED
@@ -66,18 +66,18 @@ There is one row per SURFACE, not per export, and a surface earns a row once any
66
66
  shipped lefthook preset both changed in 1.30.0 with no row that owned them — which is how a
67
67
  consumer read this index, found nothing, and diffed a KPI row that had silently gone 4-up → 2-up.
68
68
 
69
- | Surface | 1.27.0 | 1.28.0 | 1.29.0 | 1.30.0 | 1.30.1 | 1.30.2 | 1.31.0 | 1.32.0 | 1.32.1 | 1.33.0 | 1.34.0 | 1.34.1 | 1.35.0 | 1.36.0 | 1.36.1 | 1.37.0 | Unreleased |
70
- | ----------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------ | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ---------------------------------- | ------ | ------ | -------------------------------------------------------------------------------------------- | ------ | ------ | ---------- |
71
- | **tokens** (`./tokens`) | — | `--vx-divider` → `rgba()` on both schemes (§ Shell) | `--vx-space-touch-target` added (§ Additions) | eight spacing numbers (§ Chrome) | — | — | `--vx-hit` replaces the touch vars; `SIZE_CLASSES` (§ 1.31.0) | — | `SIZE_CLASSES`/`CONTAINER_CLASSES` actually exported (§ Fixes) | `CONTAINER_GRID_BREAKPOINTS`; `raw-breakpoint` trusts it (§ 1.33.0) | `CONTAINER_KEYS` (§ 1.34.0) | — | — | — | — | — | — |
72
- | **charts** (`./charts`) | — | phone tier, `curve`, formatters, `state`, log axis (§ preamble) | `ChartState.empty: string`, `AxisBottomNumeric` (§ Additions) | responsive `height`, `legend.maxRows` (§ Charts) | `height` reaches the kinds; `maxRows` beats the `fill` fit (§ Charts) | — | container-class tier + `height` keys (§ 1.31.0) | — | `ResponsiveChartHeight` `sm` → key (§ Container recipes) | — | — | — | — | — | — | — | — |
73
- | **shell** (`.`) | — | scrollport, region seams, **the brand left the sidebar** (§§ scrollport, brand, Shell) | `PageTitle`, `BasaltDevDock`, `useBreakpoint` (§ Additions) | More popover, aside pill, the responsive gutter, `useBasaltSpacing` (§ Chrome) | — | — | `theme.breakpoints` derived, `useSizeClass`, `data-basalt-host`, `useBreakpoint` on notice (§ 1.31.0) | `useBreakpoint` removal → 1.33.0; `useSizeClass` is shell-only (§ 1.32.0) | shell-less `shell/` gutter, `AppShell` breakpoints, `useSizeClass` for overlays (§§ Shell-less, `useSizeClass`) | — | `useBreakpoint` removed (§ 1.34.0) | — | — | `useCoarsePointer()` (§ `useCoarsePointer()`) | — | — | — |
74
- | **controls** (`./controls`) | `NumberFilter`, `field.number` (§ Controls) | sheet renders panel rows; `PanelRow`, `SliderControl` (§ `basalt-ui/controls`) | — | — | — | — | — | — | — | `NumberFilter clearable`; `createSearchStore` `resets` (§ State and controls) | — | — | — | `FormSection`/`FormRow`/`FormGroup`/`FormActions` move here (§ The form layout moves) | — | — | — |
75
- | **forms** (`./forms`) | — | `field` → `inputProps`, **`inputProps` drops `key`**, the layout tier (§ `inputProps`) | `field` DELETED — and the `--fix` recipe no longer works (§ Consolidation) | — | — | — | — | — | `FormGroup` row no longer stretches (§ Fixes) | — | — | — | — | the layout + `useForm`/`schemaResolver` deprecated, removed 1.37.0 (§ The form layout moves) | — | — | — |
76
- | **data** (`./data/table`) | — | root is a `<div>`, row selection, `getItemKey` required (§ `basalt-ui/data`) | `./data` + `./query` + `./connectivity` dropped (§ Consolidation) | — | — | — | — | — | `renderCard` documented (§ `renderCard`) | — | — | — | — | — | — | — | — |
77
- | **dashboard** (`.`) | `StatCard`: `unit`, `breakdown`, delta format (§ `StatCard`) | `StatGroup`, `WidgetGrid`, query-aware `StatCard` — additive | — | **the column law keys on the CONTAINER; `cols={4}` is 2-up at `sm`** (§ Dashboard) | — | — | — | — | — | — | — | — | — | — | — | — | — |
78
- | **toolchain** (`./configs/*`) | — | — | the shipped lefthook preset's `check-theme` BROKE — the CLI resolver stopped ascending (§ CLI) | the preset's default bin, and the `root:` recipe (§ Toolchain) | — | — | — | — | — | — | — | — | — | — | — | — | — |
79
- | **CLI** (`basalt-ui`) | — | — | **resolver stops relocating; `doctor` loses 6 of 10 checks** (§ CLI) | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
80
- | **guards** | five rules → `error` (§ Guards) | six new ids at `warn` (§ preamble) | `query-dual-import` retired in 1.29.1 (§ Guards) | four rules → `error` (§ Guards) | — | — | — | `raw-breakpoint`: the container-grid answer, `error` at 1.33.0 (§ 1.32.0) | `FormRow`/`FormGroup` homes in both lanes; `basalt-main` dropped (§ Fixes) | C1 + breakpoint pairs → `error`; `unknown-vx-token` `warn` (§ Guards) | — | — | — | `unknown-vx-token` → `error`; the token factories throw (§ Guards) | — | — | — |
69
+ | Surface | 1.27.0 | 1.28.0 | 1.29.0 | 1.30.0 | 1.30.1 | 1.30.2 | 1.31.0 | 1.32.0 | 1.32.1 | 1.33.0 | 1.34.0 | 1.34.1 | 1.35.0 | 1.36.0 | 1.36.1 | 1.37.0 | 1.37.1 | 1.38.0 | Unreleased |
70
+ | ----------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------ | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ---------------------------------- | ------ | ------ | -------------------------------------------------------------------------------------------- | ------ | ------ | ------ | ------ | ---------- |
71
+ | **tokens** (`./tokens`) | — | `--vx-divider` → `rgba()` on both schemes (§ Shell) | `--vx-space-touch-target` added (§ Additions) | eight spacing numbers (§ Chrome) | — | — | `--vx-hit` replaces the touch vars; `SIZE_CLASSES` (§ 1.31.0) | — | `SIZE_CLASSES`/`CONTAINER_CLASSES` actually exported (§ Fixes) | `CONTAINER_GRID_BREAKPOINTS`; `raw-breakpoint` trusts it (§ 1.33.0) | `CONTAINER_KEYS` (§ 1.34.0) | — | — | — | — | — | — | — | — |
72
+ | **charts** (`./charts`) | — | phone tier, `curve`, formatters, `state`, log axis (§ preamble) | `ChartState.empty: string`, `AxisBottomNumeric` (§ Additions) | responsive `height`, `legend.maxRows` (§ Charts) | `height` reaches the kinds; `maxRows` beats the `fill` fit (§ Charts) | — | container-class tier + `height` keys (§ 1.31.0) | — | `ResponsiveChartHeight` `sm` → key (§ Container recipes) | — | — | — | — | — | — | — | — | — | — |
73
+ | **shell** (`.`) | — | scrollport, region seams, **the brand left the sidebar** (§§ scrollport, brand, Shell) | `PageTitle`, `BasaltDevDock`, `useBreakpoint` (§ Additions) | More popover, aside pill, the responsive gutter, `useBasaltSpacing` (§ Chrome) | — | — | `theme.breakpoints` derived, `useSizeClass`, `data-basalt-host`, `useBreakpoint` on notice (§ 1.31.0) | `useBreakpoint` removal → 1.33.0; `useSizeClass` is shell-only (§ 1.32.0) | shell-less `shell/` gutter, `AppShell` breakpoints, `useSizeClass` for overlays (§§ Shell-less, `useSizeClass`) | — | `useBreakpoint` removed (§ 1.34.0) | — | — | `useCoarsePointer()` (§ `useCoarsePointer()`) | — | — | — | — | — |
74
+ | **controls** (`./controls`) | `NumberFilter`, `field.number` (§ Controls) | sheet renders panel rows; `PanelRow`, `SliderControl` (§ `basalt-ui/controls`) | — | — | — | — | — | — | — | `NumberFilter clearable`; `createSearchStore` `resets` (§ State and controls) | — | — | — | `FormSection`/`FormRow`/`FormGroup`/`FormActions` move here (§ The form layout moves) | — | — | — | — | — |
75
+ | **forms** (`./forms`) | — | `field` → `inputProps`, **`inputProps` drops `key`**, the layout tier (§ `inputProps`) | `field` DELETED — and the `--fix` recipe no longer works (§ Consolidation) | — | — | — | — | — | `FormGroup` row no longer stretches (§ Fixes) | — | — | — | — | the layout + `useForm`/`schemaResolver` deprecated, removed 1.37.0 (§ The form layout moves) | — | — | — | — | — |
76
+ | **data** (`./data/table`) | — | root is a `<div>`, row selection, `getItemKey` required (§ `basalt-ui/data`) | `./data` + `./query` + `./connectivity` dropped (§ Consolidation) | — | — | — | — | — | `renderCard` documented (§ `renderCard`) | — | — | — | — | — | — | — | — | — | — |
77
+ | **dashboard** (`.`) | `StatCard`: `unit`, `breakdown`, delta format (§ `StatCard`) | `StatGroup`, `WidgetGrid`, query-aware `StatCard` — additive | — | **the column law keys on the CONTAINER; `cols={4}` is 2-up at `sm`** (§ Dashboard) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
78
+ | **toolchain** (`./configs/*`) | — | — | the shipped lefthook preset's `check-theme` BROKE — the CLI resolver stopped ascending (§ CLI) | the preset's default bin, and the `root:` recipe (§ Toolchain) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
79
+ | **CLI** (`basalt-ui`) | — | — | **resolver stops relocating; `doctor` loses 6 of 10 checks** (§ CLI) | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — | — |
80
+ | **guards** | five rules → `error` (§ Guards) | six new ids at `warn` (§ preamble) | `query-dual-import` retired in 1.29.1 (§ Guards) | four rules → `error` (§ Guards) | — | — | — | `raw-breakpoint`: the container-grid answer, `error` at 1.33.0 (§ 1.32.0) | `FormRow`/`FormGroup` homes in both lanes; `basalt-main` dropped (§ Fixes) | C1 + breakpoint pairs → `error`; `unknown-vx-token` `warn` (§ Guards) | — | — | — | `unknown-vx-token` → `error`; the token factories throw (§ Guards) | — | — | — | — | — |
81
81
 
82
82
  ---
83
83
 
@@ -86,6 +86,14 @@ consumer read this index, found nothing, and diffed a KPI row that had silently
86
86
  _Nothing yet. Author the next section under this heading as `## Unreleased — <title>`; the
87
87
  release renames it and opens a new one here._
88
88
 
89
+ ## 1.38.0
90
+
91
+ _Nothing to migrate in this release._
92
+
93
+ ## 1.37.1
94
+
95
+ _Nothing to migrate in this release._
96
+
89
97
  ## 1.37.0
90
98
 
91
99
  _Nothing to migrate in this release._
@@ -129,15 +137,15 @@ import. `FormRow`/`FormGroup` stay law C1's form-row home from either subpath.
129
137
 
130
138
  | Was (`basalt-ui/forms`) | Now | Removed in |
131
139
  | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | ---------- |
132
- | `FormSection`, `FormRow`, `FormGroup`, `FormActions` (+ their `*Props`/`*Slot` types) | the same names from `basalt-ui/controls` | 1.38.0 |
133
- | `useForm`, `schemaResolver`, `UseFormReturnType`, `UseFormInput` | `@mantine/form` directly (zero consumers imported them from basalt) | 1.38.0 |
140
+ | `FormSection`, `FormRow`, `FormGroup`, `FormActions` (+ their `*Props`/`*Slot` types) | the same names from `basalt-ui/controls` | 1.39.0 |
141
+ | `useForm`, `schemaResolver`, `UseFormReturnType`, `UseFormInput` | `@mantine/form` directly (zero consumers imported them from basalt) | 1.39.0 |
134
142
 
135
- **Grace:** every old import still resolves in 1.36.x and 1.37.x (re-dated from 1.37.0: consumers still import them). `basalt/deprecated-export` warns on the six
143
+ **Grace:** every old import still resolves in 1.36.x–1.38.x (re-dated twice, from 1.37.0 and again from 1.38.0: consumers still import them). `basalt/deprecated-export` warns on the six
136
144
  values, but the rows are `fix: false` — the autofix rewrites a specifier inside its own import
137
145
  statement and cannot change the module — so move the import by hand
138
146
  (`import { FormRow } from 'basalt-ui/controls'`). Both subpaths are homes for
139
147
  `control-outside-home`/`raw-selection-control` throughout. The `public symbols` budget runs at 401
140
- during the grace minor; 1.38.0 removes six.
148
+ during the grace minor; 1.39.0 removes six.
141
149
 
142
150
  ### `useCoarsePointer()` — the pointer class in JS
143
151
 
@@ -2049,7 +2049,7 @@ const CONTROL_OWNER_NAMES = new Set([
2049
2049
  * `control-outside-home` treating it as a home is the whole of the enforcement a settings page
2050
2050
  * gets, and the two tier rules deliberately do not reach inside it (see {@link SLOT_ATTRS}).
2051
2051
  * `FormRow`/`FormGroup` (`basalt-ui/controls`, `src/controls/form-layout.tsx`; `basalt-ui/forms`
2052
- * re-exports them until 1.38.0 — the set is NAME-matched, so both subpaths are homes) are the SAME
2052
+ * re-exports them until 1.39.0 — the set is NAME-matched, so both subpaths are homes) are the SAME
2053
2053
  * home written for a `<form>` rather than a settings page — a `TextInput` bound to a field is read and typed
2054
2054
  * into at Mantine's own tier, not a 30px chrome affordance — so they are added here rather than
2055
2055
  * given a second walk.
@@ -3671,42 +3671,42 @@ export const DEPRECATED_EXPORTS = [
3671
3671
  subpath: 'basalt-ui/forms',
3672
3672
  name: 'FormSection',
3673
3673
  replacement: "FormSection from 'basalt-ui/controls'",
3674
- removeIn: '1.38.0',
3674
+ removeIn: '1.39.0',
3675
3675
  fix: false,
3676
3676
  },
3677
3677
  {
3678
3678
  subpath: 'basalt-ui/forms',
3679
3679
  name: 'FormRow',
3680
3680
  replacement: "FormRow from 'basalt-ui/controls'",
3681
- removeIn: '1.38.0',
3681
+ removeIn: '1.39.0',
3682
3682
  fix: false,
3683
3683
  },
3684
3684
  {
3685
3685
  subpath: 'basalt-ui/forms',
3686
3686
  name: 'FormGroup',
3687
3687
  replacement: "FormGroup from 'basalt-ui/controls'",
3688
- removeIn: '1.38.0',
3688
+ removeIn: '1.39.0',
3689
3689
  fix: false,
3690
3690
  },
3691
3691
  {
3692
3692
  subpath: 'basalt-ui/forms',
3693
3693
  name: 'FormActions',
3694
3694
  replacement: "FormActions from 'basalt-ui/controls'",
3695
- removeIn: '1.38.0',
3695
+ removeIn: '1.39.0',
3696
3696
  fix: false,
3697
3697
  },
3698
3698
  {
3699
3699
  subpath: 'basalt-ui/forms',
3700
3700
  name: 'useForm',
3701
3701
  replacement: "useForm from '@mantine/form'",
3702
- removeIn: '1.38.0',
3702
+ removeIn: '1.39.0',
3703
3703
  fix: false,
3704
3704
  },
3705
3705
  {
3706
3706
  subpath: 'basalt-ui/forms',
3707
3707
  name: 'schemaResolver',
3708
3708
  replacement: "schemaResolver from '@mantine/form'",
3709
- removeIn: '1.38.0',
3709
+ removeIn: '1.39.0',
3710
3710
  fix: false,
3711
3711
  },
3712
3712
  {
@@ -1923,7 +1923,7 @@ describe('basalt/control-outside-home', () => {
1923
1923
  },
1924
1924
  )
1925
1925
 
1926
- // FormRow/FormGroup (`basalt-ui/controls`, still re-exported from `basalt-ui/forms` until 1.38.0)
1926
+ // FormRow/FormGroup (`basalt-ui/controls`, still re-exported from `basalt-ui/forms` until 1.39.0)
1927
1927
  // are law C1's third home written for a `<form>` rather than a settings page — the same non-home
1928
1928
  // treatment SettingsRow gets, via the same CONTROL_HOST_TAGS ancestry walk, which matches by NAME
1929
1929
  // so either subpath is a home.
@@ -3393,7 +3393,7 @@ describe('basalt/deprecated-export — the form layout move', () => {
3393
3393
  )
3394
3394
  expect(rules).toContain('deprecated-export')
3395
3395
  expect(output).toContain(`'basalt-ui/controls'`)
3396
- expect(output).toContain('removed in 1.38.0')
3396
+ expect(output).toContain('removed in 1.39.0')
3397
3397
  },
3398
3398
  )
3399
3399
 
@@ -1 +1 @@
1
- {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/controls/actions.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/controls/actions.tsx"],"names":[],"mappings":"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"}
@@ -193,6 +193,7 @@ function joinRuns(actions, opts) {
193
193
  }
194
194
  return runs;
195
195
  }
196
+ const SUBPIXEL_SLACK = 1;
196
197
  const MORE_WIDTH = 80;
197
198
  function planBarFold(input) {
198
199
  const { room, fixed, gap, hasMenus, items } = input;
@@ -274,7 +275,13 @@ function useMeasuredFold(input) {
274
275
  const rendered = elements.reduce((sum, el) => sum + el.offsetWidth + BAR_GAP, 0);
275
276
  const moreNow = hasMenus || Object.values(foldsRef.current).includes("overflow");
276
277
  const fixed = used - rendered - (moreNow ? MORE_WIDTH + BAR_GAP : 0);
277
- const next = planBarFold({ room: row.clientWidth, fixed, gap: BAR_GAP, hasMenus, items });
278
+ const next = planBarFold({
279
+ room: row.clientWidth + SUBPIXEL_SLACK,
280
+ fixed,
281
+ gap: BAR_GAP,
282
+ hasMenus,
283
+ items
284
+ });
278
285
  const prev = foldsRef.current;
279
286
  const same = Object.keys(next).length === Object.keys(prev).length && Object.entries(next).every(([key, step]) => prev[key] === step);
280
287
  if (!same) setFolds(next);
@@ -10,7 +10,7 @@
10
10
  * (`useFormSubmit` + `FormStateProvider`), the list field (`useFieldArray`) and the draft
11
11
  * (`useFormDraft`). The layout (`FormSection` / `FormRow` / `FormGroup` / `FormActions`) moved to
12
12
  * `basalt-ui/controls` in 1.36.0 — it needs no `@mantine/form` — and stays here as deprecated
13
- * aliases until 1.38.0, as do the `useForm`/`schemaResolver` re-exports (import from
13
+ * aliases until 1.39.0, as do the `useForm`/`schemaResolver` re-exports (import from
14
14
  * `@mantine/form`).
15
15
  *
16
16
  * Optional peer: @mantine/form ^9.3.0 — install with: bun add @mantine/form
@@ -19,26 +19,26 @@ export { useBasaltForm } from './create-form.js';
19
19
  export type { UseBasaltFormOptions } from './create-form.js';
20
20
  export { inputProps, fieldKey } from './field.js';
21
21
  export {
22
- /** @deprecated Import from 'basalt-ui/controls' — removed in 1.38.0. */
22
+ /** @deprecated Import from 'basalt-ui/controls' — removed in 1.39.0. */
23
23
  FormSection,
24
- /** @deprecated Import from 'basalt-ui/controls' — removed in 1.38.0. */
24
+ /** @deprecated Import from 'basalt-ui/controls' — removed in 1.39.0. */
25
25
  FormRow,
26
- /** @deprecated Import from 'basalt-ui/controls' — removed in 1.38.0. */
26
+ /** @deprecated Import from 'basalt-ui/controls' — removed in 1.39.0. */
27
27
  FormGroup,
28
- /** @deprecated Import from 'basalt-ui/controls' — removed in 1.38.0. */
28
+ /** @deprecated Import from 'basalt-ui/controls' — removed in 1.39.0. */
29
29
  FormActions, } from '../controls/form-layout.js';
30
30
  export type {
31
- /** @deprecated Import from 'basalt-ui/controls' — removed in 1.38.0. */
31
+ /** @deprecated Import from 'basalt-ui/controls' — removed in 1.39.0. */
32
32
  FormSectionProps,
33
- /** @deprecated Import from 'basalt-ui/controls' — removed in 1.38.0. */
33
+ /** @deprecated Import from 'basalt-ui/controls' — removed in 1.39.0. */
34
34
  FormSectionSlot,
35
- /** @deprecated Import from 'basalt-ui/controls' — removed in 1.38.0. */
35
+ /** @deprecated Import from 'basalt-ui/controls' — removed in 1.39.0. */
36
36
  FormRowProps,
37
- /** @deprecated Import from 'basalt-ui/controls' — removed in 1.38.0. */
37
+ /** @deprecated Import from 'basalt-ui/controls' — removed in 1.39.0. */
38
38
  FormRowSlot,
39
- /** @deprecated Import from 'basalt-ui/controls' — removed in 1.38.0. */
39
+ /** @deprecated Import from 'basalt-ui/controls' — removed in 1.39.0. */
40
40
  FormGroupProps,
41
- /** @deprecated Import from 'basalt-ui/controls' — removed in 1.38.0. */
41
+ /** @deprecated Import from 'basalt-ui/controls' — removed in 1.39.0. */
42
42
  FormActionsProps, } from '../controls/form-layout.js';
43
43
  export { useFormSubmit, isFieldErrorEnvelope } from './use-form-submit.js';
44
44
  export type { FormFieldErrors, UseFormSubmitOptions, UseFormSubmitReturn } from './use-form-submit.js';
@@ -51,13 +51,13 @@ export type { FormErrorSummaryProps } from './FormErrorSummary.js';
51
51
  export { useFormDraft, DEFAULT_AUTOSAVE_DEBOUNCE_MS } from './use-form-draft.js';
52
52
  export type { UseFormDraftOptions, UseFormDraftReturn } from './use-form-draft.js';
53
53
  export {
54
- /** @deprecated Import from '@mantine/form' — removed in 1.38.0. */
54
+ /** @deprecated Import from '@mantine/form' — removed in 1.39.0. */
55
55
  useForm,
56
- /** @deprecated Import from '@mantine/form' — removed in 1.38.0. */
56
+ /** @deprecated Import from '@mantine/form' — removed in 1.39.0. */
57
57
  schemaResolver, } from '@mantine/form';
58
58
  export type {
59
- /** @deprecated Import from '@mantine/form' — removed in 1.38.0. */
59
+ /** @deprecated Import from '@mantine/form' — removed in 1.39.0. */
60
60
  UseFormReturnType,
61
- /** @deprecated Import from '@mantine/form' — removed in 1.38.0. */
61
+ /** @deprecated Import from '@mantine/form' — removed in 1.39.0. */
62
62
  UseFormInput, } from '@mantine/form';
63
63
  //# sourceMappingURL=index.d.ts.map
@@ -15,10 +15,27 @@
15
15
  * 10px of `.root` frame inset plus 10px of NavLink inset (`rowInsetX`) = 20. One vertical line
16
16
  * across the seam — a claim this comment used to make while the gutter was 13, and that the
17
17
  * 13 -> 20 move made true.
18
+ *
19
+ * `--basalt-shell-lead-inset` (default 0px, set by the consumer on `:root`) is the opt-in native
20
+ * title-bar contract: a macOS overlay title bar paints its traffic lights ON TOP of this zone, so a
21
+ * consumer publishes their span (~70px) and the zone (a) grows its inline-start padding by it and
22
+ * (b) is never narrower than inset + the collapse toggle, so the 48px rail does not clip the toggle
23
+ * under `overflow: hidden` (the toggle lands right of the lights; the breadcrumb after a widened
24
+ * rail zone shifts right by the same amount — accepted). Unset, every value below resolves to
25
+ * exactly what it was before the property existed: `max(offset, 0px + toggle)` is the offset
26
+ * (256 / 48 are both above the 28px toggle) and the added padding is 0px.
18
27
  */
19
28
  .zone {
20
- flex: 0 0 var(--app-shell-navbar-offset, 0rem);
21
- width: var(--app-shell-navbar-offset, 0rem);
29
+ /* The collapse toggle's box — `app-brand.tsx`'s `size={28}`, which is Mantine's rem-scaled
30
+ * `--ai-size-md` (1.75rem). Mantine defines that var on the ActionIcon, not on `:root`, so the
31
+ * zone restates the same arithmetic; `app-brand.module.css.test.ts` pins the two together. */
32
+ --basalt-shell-toggle-size: calc(1.75rem * var(--mantine-scale, 1));
33
+ --basalt-shell-zone-width: max(
34
+ var(--app-shell-navbar-offset, 0rem),
35
+ calc(var(--basalt-shell-lead-inset, 0px) + var(--basalt-shell-toggle-size))
36
+ );
37
+ flex: 0 0 var(--basalt-shell-zone-width);
38
+ width: var(--basalt-shell-zone-width);
22
39
  min-width: 0;
23
40
  display: flex;
24
41
  align-items: center;
@@ -27,6 +44,7 @@
27
44
  align-self: stretch;
28
45
  margin-inline-start: calc(-1 * var(--app-shell-padding, 0px));
29
46
  padding-inline: var(--app-shell-padding, 0px);
47
+ padding-inline-start: calc(var(--app-shell-padding, 0px) + var(--basalt-shell-lead-inset, 0px));
30
48
  overflow: hidden;
31
49
  /* Mantine's own AppShell timing, so the zone and the navbar under it move as one box. */
32
50
  transition-property: flex-basis, width;
@@ -101,11 +119,12 @@
101
119
  /*
102
120
  * The rail form, mirroring exactly what the sidebar's own brand row did when collapsed: the lead
103
121
  * (logo + name) hides and the toggle centres. The zone drops its inset so the toggle's centre lands
104
- * on the rail's icon column rather than 13px right of it.
122
+ * on the rail's icon column rather than 13px right of it. The lead inset is the one padding the
123
+ * rail keeps: the toggle sits right of the native title-bar controls, not centred under them.
105
124
  */
106
125
  @media (min-width: 52.5em) {
107
126
  .zone[data-collapsed] {
108
- padding-inline: 0;
127
+ padding-inline: var(--basalt-shell-lead-inset, 0px) 0;
109
128
  }
110
129
 
111
130
  .zone[data-collapsed] .brand {
@@ -212,6 +212,11 @@
212
212
  .footer {
213
213
  flex-shrink: 0;
214
214
  padding-top: var(--vx-space-sidebar-region-gap);
215
+ /* The bottom inset equals the column's side inset (`.root`'s `row-inset-x`), so the last row —
216
+ * the settings link, the account row or a bottom progress block — does not sit FLUSH on the
217
+ * navbar's bottom edge against a 10px side gap. Rail and expanded alike: this is the footer's own
218
+ * edge, not a rail rule, so a collapsed sidebar cannot double it. */
219
+ padding-bottom: var(--vx-space-row-inset-x);
215
220
  }
216
221
 
217
222
  .footer > * + * {
@@ -697,6 +702,17 @@
697
702
  display: none;
698
703
  }
699
704
 
705
+ /* The FIRST section's band keeps its label gap in the rail. Expanded, that band's
706
+ * `margin-bottom` is what seats the first nav row `section-label-gap` below the navbar top (an
707
+ * unlabelled band is zero-height); zeroing it above left the rail's first icon flush at 0, a few
708
+ * px higher than the same row expanded. Only the first band: every later band's zeroed margin is
709
+ * what keeps the gap BETWEEN sections at `section-gap` in the rail, where the label it separated
710
+ * from the rows is gone. (A labelled first section expands to label + gap, which the rail cannot
711
+ * match — its label is `display: none`; the equal-offset contract is the unlabelled case.) */
712
+ .root[data-collapsed] .nav > div:first-child > .sectionBand {
713
+ margin-bottom: var(--vx-space-sidebar-section-label-gap);
714
+ }
715
+
700
716
  /* Collapse every nav row to its icon. Targets `.navItem` — the wrapper Box present on EVERY render
701
717
  * path — so it covers the shell's fallback NavLink AND a consumer's `SidebarItem.Anchor` (e.g. a router
702
718
  * <Link>), both of which render a Mantine NavLink inside. Keying off `.link` alone missed consumer
@@ -152,6 +152,13 @@ export type BasaltShellProps = BasaltProps & {
152
152
  * inside it: `PageBarProvider` owns the header portal and the single-kebab claim, `AsideProvider`
153
153
  * owns the aside portal, the region CLAIM and the claiming page's fold state — which `ShellFrame`
154
154
  * has to READ to size `AppShell.Aside`, so it cannot be the component that provides it.
155
+ *
156
+ * **Native title bar (opt-in CSS contract).** A desktop host with a macOS overlay title bar paints
157
+ * its traffic lights ON TOP of the header's leading zone. Set `--basalt-shell-lead-inset` on
158
+ * `:root` to their span (e.g. `html { --basalt-shell-lead-inset: 70px }`; default `0px`, which
159
+ * changes nothing): the zone's inline-start padding grows by it, and the zone is never narrower than
160
+ * the inset plus the collapse toggle, so the 48px rail pushes the toggle to the right of the lights
161
+ * instead of clipping it. The breadcrumb after a widened rail zone shifts right by the same amount.
155
162
  */
156
163
  export declare function BasaltShell(props: BasaltShellProps): import("react").JSX.Element;
157
164
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shell/index.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shell/index.tsx"],"names":[],"mappings":"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"}
package/llms.txt CHANGED
@@ -1,4 +1,4 @@
1
- # basalt-ui v1.37.0
1
+ # basalt-ui v1.38.0
2
2
  # Opinionated framework for Mantine-based React apps — theme, app shell, visx charts, toolchain presets
3
3
  #
4
4
  # basalt-ui is an opinionated framework for Mantine-based React apps.
@@ -19,7 +19,7 @@
19
19
  # ./tokens.css subpath. Guide: docs/FRAMEWORK-FREE.md in the repo.
20
20
 
21
21
  Package: basalt-ui
22
- Version: 1.37.0
22
+ Version: 1.38.0
23
23
 
24
24
  ## basalt-ui
25
25
  Import: import { ... } from 'basalt-ui'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "basalt-ui",
3
- "version": "1.37.0",
3
+ "version": "1.38.0",
4
4
  "description": "Opinionated framework for Mantine-based React apps — theme, app shell, visx charts, toolchain presets",
5
5
  "keywords": [
6
6
  "charts",
@@ -477,6 +477,9 @@ function joinRuns<T extends BarAction>(
477
477
 
478
478
  type FoldStep = 'icon' | 'overflow'
479
479
 
480
+ /** `clientWidth` rounds to a whole pixel; the measured boxes do not — see `useMeasuredFold`. */
481
+ const SUBPIXEL_SLACK = 1
482
+
480
483
  /** The `More` trigger's estimated width — reserved only once something has folded (cf. `FOLD_PILL_WIDTH`). */
481
484
  const MORE_WIDTH = 80
482
485
 
@@ -612,7 +615,16 @@ function useMeasuredFold(input: {
612
615
  const rendered = elements.reduce((sum, el) => sum + el.offsetWidth + BAR_GAP, 0)
613
616
  const moreNow = hasMenus || Object.values(foldsRef.current).includes('overflow')
614
617
  const fixed = used - rendered - (moreNow ? MORE_WIDTH + BAR_GAP : 0)
615
- const next = planBarFold({ room: row.clientWidth, fixed, gap: BAR_GAP, hasMenus, items })
618
+ // `clientWidth` is a rounded integer, the rects above are fractional: a row that is exactly as
619
+ // wide as its content (a `slot` host's shrink-wrapped `Group`, measured against itself) would
620
+ // read 123.4px of content in 123px of room and fold a button that fits.
621
+ const next = planBarFold({
622
+ room: row.clientWidth + SUBPIXEL_SLACK,
623
+ fixed,
624
+ gap: BAR_GAP,
625
+ hasMenus,
626
+ items,
627
+ })
616
628
  const prev = foldsRef.current
617
629
  const same =
618
630
  Object.keys(next).length === Object.keys(prev).length &&
@@ -2,7 +2,7 @@
2
2
  * ./controls — the form layout primitives: `FormSection`, `FormRow`, `FormGroup`, `FormActions`.
3
3
  * Mantine-coupled. They need no `@mantine/form` (layout only), which is why they live here and not
4
4
  * in `./forms`, whose barrel value-imports it: importing a row from there made a consumer install a
5
- * peer it never used. `./forms` re-exports them as deprecated aliases until 1.38.0.
5
+ * peer it never used. `./forms` re-exports them as deprecated aliases until 1.39.0.
6
6
  *
7
7
  * The gap they close (audit B #5 / §4): `./forms` shipped no layout at all, so the only row
8
8
  * primitive in the package was `SettingsRow` over in `./dashboard` — which is the SETTINGS-page
@@ -56,7 +56,7 @@ export { SliderControl, type SliderControlProps } from './slider-control'
56
56
 
57
57
  // ── The form layout (moved from ./forms in 1.36.0) ────────────────────────────────────────────
58
58
  // Layout needs no `@mantine/form`, and `./forms`' barrel value-imports it — so a consumer rendering
59
- // a `FormRow` had to install a peer it never used. `./forms` re-exports these until 1.38.0.
59
+ // a `FormRow` had to install a peer it never used. `./forms` re-exports these until 1.39.0.
60
60
  export {
61
61
  FormSection,
62
62
  FormRow,
@@ -10,7 +10,7 @@
10
10
  * (`useFormSubmit` + `FormStateProvider`), the list field (`useFieldArray`) and the draft
11
11
  * (`useFormDraft`). The layout (`FormSection` / `FormRow` / `FormGroup` / `FormActions`) moved to
12
12
  * `basalt-ui/controls` in 1.36.0 — it needs no `@mantine/form` — and stays here as deprecated
13
- * aliases until 1.38.0, as do the `useForm`/`schemaResolver` re-exports (import from
13
+ * aliases until 1.39.0, as do the `useForm`/`schemaResolver` re-exports (import from
14
14
  * `@mantine/form`).
15
15
  *
16
16
  * Optional peer: @mantine/form ^9.3.0 — install with: bun add @mantine/form
@@ -24,29 +24,29 @@ export type { UseBasaltFormOptions } from './create-form'
24
24
  // Two calls, never one object: `key` inside a spread is a React 19 warning. See field.ts.
25
25
  export { inputProps, fieldKey } from './field'
26
26
 
27
- // ── layout — moved to ./controls (1.36.0), deprecated aliases until 1.38.0 ─────
27
+ // ── layout — moved to ./controls (1.36.0), deprecated aliases until 1.39.0 ─────
28
28
  export {
29
- /** @deprecated Import from 'basalt-ui/controls' — removed in 1.38.0. */
29
+ /** @deprecated Import from 'basalt-ui/controls' — removed in 1.39.0. */
30
30
  FormSection,
31
- /** @deprecated Import from 'basalt-ui/controls' — removed in 1.38.0. */
31
+ /** @deprecated Import from 'basalt-ui/controls' — removed in 1.39.0. */
32
32
  FormRow,
33
- /** @deprecated Import from 'basalt-ui/controls' — removed in 1.38.0. */
33
+ /** @deprecated Import from 'basalt-ui/controls' — removed in 1.39.0. */
34
34
  FormGroup,
35
- /** @deprecated Import from 'basalt-ui/controls' — removed in 1.38.0. */
35
+ /** @deprecated Import from 'basalt-ui/controls' — removed in 1.39.0. */
36
36
  FormActions,
37
37
  } from '../controls/form-layout'
38
38
  export type {
39
- /** @deprecated Import from 'basalt-ui/controls' — removed in 1.38.0. */
39
+ /** @deprecated Import from 'basalt-ui/controls' — removed in 1.39.0. */
40
40
  FormSectionProps,
41
- /** @deprecated Import from 'basalt-ui/controls' — removed in 1.38.0. */
41
+ /** @deprecated Import from 'basalt-ui/controls' — removed in 1.39.0. */
42
42
  FormSectionSlot,
43
- /** @deprecated Import from 'basalt-ui/controls' — removed in 1.38.0. */
43
+ /** @deprecated Import from 'basalt-ui/controls' — removed in 1.39.0. */
44
44
  FormRowProps,
45
- /** @deprecated Import from 'basalt-ui/controls' — removed in 1.38.0. */
45
+ /** @deprecated Import from 'basalt-ui/controls' — removed in 1.39.0. */
46
46
  FormRowSlot,
47
- /** @deprecated Import from 'basalt-ui/controls' — removed in 1.38.0. */
47
+ /** @deprecated Import from 'basalt-ui/controls' — removed in 1.39.0. */
48
48
  FormGroupProps,
49
- /** @deprecated Import from 'basalt-ui/controls' — removed in 1.38.0. */
49
+ /** @deprecated Import from 'basalt-ui/controls' — removed in 1.39.0. */
50
50
  FormActionsProps,
51
51
  } from '../controls/form-layout'
52
52
 
@@ -70,14 +70,14 @@ export type { UseFormDraftOptions, UseFormDraftReturn } from './use-form-draft'
70
70
 
71
71
  // ── @mantine/form re-exports — deprecated, zero consumers import them from here ──
72
72
  export {
73
- /** @deprecated Import from '@mantine/form' — removed in 1.38.0. */
73
+ /** @deprecated Import from '@mantine/form' — removed in 1.39.0. */
74
74
  useForm,
75
- /** @deprecated Import from '@mantine/form' — removed in 1.38.0. */
75
+ /** @deprecated Import from '@mantine/form' — removed in 1.39.0. */
76
76
  schemaResolver,
77
77
  } from '@mantine/form'
78
78
  export type {
79
- /** @deprecated Import from '@mantine/form' — removed in 1.38.0. */
79
+ /** @deprecated Import from '@mantine/form' — removed in 1.39.0. */
80
80
  UseFormReturnType,
81
- /** @deprecated Import from '@mantine/form' — removed in 1.38.0. */
81
+ /** @deprecated Import from '@mantine/form' — removed in 1.39.0. */
82
82
  UseFormInput,
83
83
  } from '@mantine/form'
@@ -15,10 +15,27 @@
15
15
  * 10px of `.root` frame inset plus 10px of NavLink inset (`rowInsetX`) = 20. One vertical line
16
16
  * across the seam — a claim this comment used to make while the gutter was 13, and that the
17
17
  * 13 -> 20 move made true.
18
+ *
19
+ * `--basalt-shell-lead-inset` (default 0px, set by the consumer on `:root`) is the opt-in native
20
+ * title-bar contract: a macOS overlay title bar paints its traffic lights ON TOP of this zone, so a
21
+ * consumer publishes their span (~70px) and the zone (a) grows its inline-start padding by it and
22
+ * (b) is never narrower than inset + the collapse toggle, so the 48px rail does not clip the toggle
23
+ * under `overflow: hidden` (the toggle lands right of the lights; the breadcrumb after a widened
24
+ * rail zone shifts right by the same amount — accepted). Unset, every value below resolves to
25
+ * exactly what it was before the property existed: `max(offset, 0px + toggle)` is the offset
26
+ * (256 / 48 are both above the 28px toggle) and the added padding is 0px.
18
27
  */
19
28
  .zone {
20
- flex: 0 0 var(--app-shell-navbar-offset, 0rem);
21
- width: var(--app-shell-navbar-offset, 0rem);
29
+ /* The collapse toggle's box — `app-brand.tsx`'s `size={28}`, which is Mantine's rem-scaled
30
+ * `--ai-size-md` (1.75rem). Mantine defines that var on the ActionIcon, not on `:root`, so the
31
+ * zone restates the same arithmetic; `app-brand.module.css.test.ts` pins the two together. */
32
+ --basalt-shell-toggle-size: calc(1.75rem * var(--mantine-scale, 1));
33
+ --basalt-shell-zone-width: max(
34
+ var(--app-shell-navbar-offset, 0rem),
35
+ calc(var(--basalt-shell-lead-inset, 0px) + var(--basalt-shell-toggle-size))
36
+ );
37
+ flex: 0 0 var(--basalt-shell-zone-width);
38
+ width: var(--basalt-shell-zone-width);
22
39
  min-width: 0;
23
40
  display: flex;
24
41
  align-items: center;
@@ -27,6 +44,7 @@
27
44
  align-self: stretch;
28
45
  margin-inline-start: calc(-1 * var(--app-shell-padding, 0px));
29
46
  padding-inline: var(--app-shell-padding, 0px);
47
+ padding-inline-start: calc(var(--app-shell-padding, 0px) + var(--basalt-shell-lead-inset, 0px));
30
48
  overflow: hidden;
31
49
  /* Mantine's own AppShell timing, so the zone and the navbar under it move as one box. */
32
50
  transition-property: flex-basis, width;
@@ -101,11 +119,12 @@
101
119
  /*
102
120
  * The rail form, mirroring exactly what the sidebar's own brand row did when collapsed: the lead
103
121
  * (logo + name) hides and the toggle centres. The zone drops its inset so the toggle's centre lands
104
- * on the rail's icon column rather than 13px right of it.
122
+ * on the rail's icon column rather than 13px right of it. The lead inset is the one padding the
123
+ * rail keeps: the toggle sits right of the native title-bar controls, not centred under them.
105
124
  */
106
125
  @media (min-width: 52.5em) {
107
126
  .zone[data-collapsed] {
108
- padding-inline: 0;
127
+ padding-inline: var(--basalt-shell-lead-inset, 0px) 0;
109
128
  }
110
129
 
111
130
  .zone[data-collapsed] .brand {
@@ -212,6 +212,11 @@
212
212
  .footer {
213
213
  flex-shrink: 0;
214
214
  padding-top: var(--vx-space-sidebar-region-gap);
215
+ /* The bottom inset equals the column's side inset (`.root`'s `row-inset-x`), so the last row —
216
+ * the settings link, the account row or a bottom progress block — does not sit FLUSH on the
217
+ * navbar's bottom edge against a 10px side gap. Rail and expanded alike: this is the footer's own
218
+ * edge, not a rail rule, so a collapsed sidebar cannot double it. */
219
+ padding-bottom: var(--vx-space-row-inset-x);
215
220
  }
216
221
 
217
222
  .footer > * + * {
@@ -697,6 +702,17 @@
697
702
  display: none;
698
703
  }
699
704
 
705
+ /* The FIRST section's band keeps its label gap in the rail. Expanded, that band's
706
+ * `margin-bottom` is what seats the first nav row `section-label-gap` below the navbar top (an
707
+ * unlabelled band is zero-height); zeroing it above left the rail's first icon flush at 0, a few
708
+ * px higher than the same row expanded. Only the first band: every later band's zeroed margin is
709
+ * what keeps the gap BETWEEN sections at `section-gap` in the rail, where the label it separated
710
+ * from the rows is gone. (A labelled first section expands to label + gap, which the rail cannot
711
+ * match — its label is `display: none`; the equal-offset contract is the unlabelled case.) */
712
+ .root[data-collapsed] .nav > div:first-child > .sectionBand {
713
+ margin-bottom: var(--vx-space-sidebar-section-label-gap);
714
+ }
715
+
700
716
  /* Collapse every nav row to its icon. Targets `.navItem` — the wrapper Box present on EVERY render
701
717
  * path — so it covers the shell's fallback NavLink AND a consumer's `SidebarItem.Anchor` (e.g. a router
702
718
  * <Link>), both of which render a Mantine NavLink inside. Keying off `.link` alone missed consumer
@@ -319,6 +319,13 @@ function collapseStore(key: string): CollapseStore {
319
319
  * inside it: `PageBarProvider` owns the header portal and the single-kebab claim, `AsideProvider`
320
320
  * owns the aside portal, the region CLAIM and the claiming page's fold state — which `ShellFrame`
321
321
  * has to READ to size `AppShell.Aside`, so it cannot be the component that provides it.
322
+ *
323
+ * **Native title bar (opt-in CSS contract).** A desktop host with a macOS overlay title bar paints
324
+ * its traffic lights ON TOP of the header's leading zone. Set `--basalt-shell-lead-inset` on
325
+ * `:root` to their span (e.g. `html { --basalt-shell-lead-inset: 70px }`; default `0px`, which
326
+ * changes nothing): the zone's inline-start padding grows by it, and the zone is never narrower than
327
+ * the inset plus the collapse toggle, so the 48px rail pushes the toggle to the right of the lights
328
+ * instead of clipping it. The breadcrumb after a widened rail zone shifts right by the same amount.
322
329
  */
323
330
  export function BasaltShell(props: BasaltShellProps) {
324
331
  assertRequiredProps('BasaltShell', props, ['brand', 'sections'])