basalt-ui 1.39.0 → 1.40.2
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 +26 -0
- package/MIGRATING.md +24 -12
- package/dist/provider/index.d.ts +1 -1
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +14 -4
- package/dist/shell/app-sidebar-account.d.ts.map +1 -1
- package/dist/shell/app-sidebar-account.js +4 -3
- package/dist/shell/app-sidebar.module.css +17 -7
- package/dist/styles.css +6 -2
- package/dist/theme/index.d.ts +15 -0
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +23 -4
- package/dist/theme/segmented-control.module.css +16 -0
- package/dist/theme/segmented-control.module.css.d.ts +1 -0
- package/llms.txt +2 -2
- package/package.json +1 -1
- package/src/provider/index.tsx +15 -3
- package/src/shell/app-sidebar-account.tsx +8 -3
- package/src/shell/app-sidebar.module.css +17 -7
- package/src/styles.css +6 -2
- package/src/theme/index.ts +41 -3
- package/src/theme/segmented-control.module.css +16 -0
- package/src/theme/segmented-control.module.css.d.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
## [1.40.2](https://github.com/jkrumm/basalt-ui/compare/v1.40.1...v1.40.2) (2026-10-05)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* sidebar footer rows sit on the nav row's height and 1px pitch ([bbd5c66](https://github.com/jkrumm/basalt-ui/commit/bbd5c66a223af61888df28cca837b89ef95181b1))
|
|
7
|
+
|
|
8
|
+
## [1.40.1](https://github.com/jkrumm/basalt-ui/compare/v1.40.0...v1.40.1) (2026-10-05)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* lift the selected segment above the track in dark mode ([a8315a9](https://github.com/jkrumm/basalt-ui/commit/a8315a90b9b067470f9d6ae3a0005117e713934b))
|
|
14
|
+
|
|
15
|
+
# [1.40.0](https://github.com/jkrumm/basalt-ui/compare/v1.39.0...v1.40.0) (2026-10-05)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* raised segmented indicator, nav-row geometry for the sidebar account row ([d800ea1](https://github.com/jkrumm/basalt-ui/commit/d800ea1dad17091aa558c05d33117adc7cf31310))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Features
|
|
24
|
+
|
|
25
|
+
* createBasaltTheme inputs option for desktop-sized inputs ([51a3d7f](https://github.com/jkrumm/basalt-ui/commit/51a3d7f023a5379c9c55f226c1671d83e732ea59))
|
|
26
|
+
|
|
1
27
|
# [1.39.0](https://github.com/jkrumm/basalt-ui/compare/v1.38.1...v1.39.0) (2026-10-04)
|
|
2
28
|
|
|
3
29
|
|
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 | 1.37.1 | 1.38.0 | 1.38.1 | 1.39.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) | — | — | — | — | — | — | — | | `appShellInset` 20 → 16 (§ 1.38.1) | — | — |
|
|
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()`) | — | — | — | — | | content gutter 16, block 8 from `sm` (§ 1.38.1) | — | — |
|
|
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 | 1.38.1 | 1.39.0 | 1.40.0 | 1.40.1 | 1.40.2 | 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) | — | — | — | — | — | — | — | | `appShellInset` 20 → 16 (§ 1.38.1) | — | — | — | — | — |
|
|
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()`) | — | — | — | — | | content gutter 16, block 8 from `sm` (§ 1.38.1) | — | — | — | — | — |
|
|
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,18 @@ 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.40.2
|
|
90
|
+
|
|
91
|
+
_Nothing to migrate in this release._
|
|
92
|
+
|
|
93
|
+
## 1.40.1
|
|
94
|
+
|
|
95
|
+
_Nothing to migrate in this release._
|
|
96
|
+
|
|
97
|
+
## 1.40.0
|
|
98
|
+
|
|
99
|
+
_Nothing to migrate in this release._
|
|
100
|
+
|
|
89
101
|
## 1.39.0 — the forms aliases leave
|
|
90
102
|
|
|
91
103
|
**Added: `BasaltShell headerHeight?: number`** (px, default unchanged — the density-tracking
|
package/dist/provider/index.d.ts
CHANGED
|
@@ -142,7 +142,7 @@ export declare class BasaltErrorBoundary extends Component<BasaltErrorBoundaryPr
|
|
|
142
142
|
* `BasaltBridge`'s `useMemo`) purely so `provider/index.test.ts` can assert the ORDER holds without
|
|
143
143
|
* rendering a React tree.
|
|
144
144
|
*/
|
|
145
|
-
export declare function composeInjectedCss(base: string, fontsCss: string, radiusCss: string, densityCss: string): string;
|
|
145
|
+
export declare function composeInjectedCss(base: string, fontsCss: string, radiusCss: string, densityCss: string, inputsCss?: string): string;
|
|
146
146
|
/**
|
|
147
147
|
* `data-basalt-host` on `<html>` — the one place a `display-mode` query is read; CSS keys off it.
|
|
148
148
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/provider/index.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AACzD,OAAO,EAAE,SAAS,EAAsB,MAAM,OAAO,CAAA;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAIjD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAA;AAIrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAQjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAExD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAA;AAEnD,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,SAAS,CAAA;IACnB,oFAAoF;IACpF,KAAK,CAAC,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAA;IACrC;;;;;;;;;;;;;;;;;;OAkBG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;OAGG;IACH,cAAc,CAAC,EAAE,gBAAgB,CAAA;IACjC,8CAA8C;IAC9C,kBAAkB,CAAC,EAAE,oBAAoB,CAAC,oBAAoB,CAAC,CAAA;IAC/D;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC3D;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;OAKG;IACH,YAAY,CAAC,EAAE,IAAI,CAAC,yBAAyB,EAAE,UAAU,CAAC,CAAA;IAC1D;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,SAAS,CAAA;CAC1B,GAAG,IAAI,CAAC,oBAAoB,EAAE,UAAU,GAAG,OAAO,GAAG,oBAAoB,GAAG,sBAAsB,CAAC,CAAA;AAcpG;;;GAGG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,EAAE,SAAS,CAAA;IACnB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC3D;;;;OAIG;IACH,QAAQ,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,SAAS,CAAC,CAAA;CACvD,CAAA;AAED,KAAK,aAAa,GAAG;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAA;AA2B1D;;;;;;;;;GASG;AACH,qBAAa,mBAAoB,SAAQ,SAAS,CAAC,wBAAwB,EAAE,aAAa,CAAC;gBAC7E,KAAK,EAAE,wBAAwB;IAK3C,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa;IAIrD,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAKxD,MAAM,IAAI,SAAS;CAS7B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/provider/index.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AACzD,OAAO,EAAE,SAAS,EAAsB,MAAM,OAAO,CAAA;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAIjD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAA;AAIrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAQjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAExD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAA;AAEnD,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,SAAS,CAAA;IACnB,oFAAoF;IACpF,KAAK,CAAC,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAA;IACrC;;;;;;;;;;;;;;;;;;OAkBG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;OAGG;IACH,cAAc,CAAC,EAAE,gBAAgB,CAAA;IACjC,8CAA8C;IAC9C,kBAAkB,CAAC,EAAE,oBAAoB,CAAC,oBAAoB,CAAC,CAAA;IAC/D;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC3D;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;OAKG;IACH,YAAY,CAAC,EAAE,IAAI,CAAC,yBAAyB,EAAE,UAAU,CAAC,CAAA;IAC1D;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,SAAS,CAAA;CAC1B,GAAG,IAAI,CAAC,oBAAoB,EAAE,UAAU,GAAG,OAAO,GAAG,oBAAoB,GAAG,sBAAsB,CAAC,CAAA;AAcpG;;;GAGG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,EAAE,SAAS,CAAA;IACnB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC3D;;;;OAIG;IACH,QAAQ,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,SAAS,CAAC,CAAA;CACvD,CAAA;AAED,KAAK,aAAa,GAAG;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAA;AA2B1D;;;;;;;;;GASG;AACH,qBAAa,mBAAoB,SAAQ,SAAS,CAAC,wBAAwB,EAAE,aAAa,CAAC;gBAC7E,KAAK,EAAE,wBAAwB;IAK3C,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa;IAIrD,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAKxD,MAAM,IAAI,SAAS;CAS7B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,SAAS,SAAK,GACb,MAAM,CAER;AAYD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAmBvC;AAgID,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,KAAK,EACL,aAAoB,EACpB,cAAc,EACd,kBAA2B,EAC3B,OAAO,EACP,KAAK,EACL,YAAY,EACZ,aAAyB,EACzB,GAAG,IAAI,EACR,EAAE,mBAAmB,+BAsCrB"}
|
package/dist/provider/index.js
CHANGED
|
@@ -66,8 +66,8 @@ class BasaltErrorBoundary extends Component {
|
|
|
66
66
|
return this.props.children;
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
|
-
function composeInjectedCss(base, fontsCss, radiusCss, densityCss) {
|
|
70
|
-
return [base, fontsCss, radiusCss, densityCss].filter(Boolean).join("\n");
|
|
69
|
+
function composeInjectedCss(base, fontsCss, radiusCss, densityCss, inputsCss = "") {
|
|
70
|
+
return [base, fontsCss, radiusCss, densityCss, inputsCss].filter(Boolean).join("\n");
|
|
71
71
|
}
|
|
72
72
|
function createMountCounter() {
|
|
73
73
|
let count = 0;
|
|
@@ -106,6 +106,7 @@ function BasaltBridge({
|
|
|
106
106
|
const fontsConfig = theme.other?.basaltFonts;
|
|
107
107
|
const radiusConfig = theme.other?.basaltRadius;
|
|
108
108
|
const densityConfig = theme.other?.basaltDensity;
|
|
109
|
+
const desktopInputs = theme.other?.["basaltInputs"] === "desktop";
|
|
109
110
|
const paletteCss = useMemo(() => {
|
|
110
111
|
if (!injectPalette) return "";
|
|
111
112
|
const base = deriveConfig === void 0 || isDefaultDeriveConfig(deriveConfig) ? buildPaletteCss(paletteOptions) : (
|
|
@@ -116,8 +117,17 @@ function BasaltBridge({
|
|
|
116
117
|
const fontsCss = buildFontsCss(fontsConfig);
|
|
117
118
|
const radiusCss = buildRadiusCss(radiusConfig);
|
|
118
119
|
const densityCss = buildDensityCss(densityConfig);
|
|
119
|
-
|
|
120
|
-
|
|
120
|
+
const inputsCss = desktopInputs ? ":root {\n--basalt-input-fz-floor: 0px;\n}" : "";
|
|
121
|
+
return composeInjectedCss(base, fontsCss, radiusCss, densityCss, inputsCss);
|
|
122
|
+
}, [
|
|
123
|
+
injectPalette,
|
|
124
|
+
deriveConfig,
|
|
125
|
+
fontsConfig,
|
|
126
|
+
radiusConfig,
|
|
127
|
+
densityConfig,
|
|
128
|
+
desktopInputs,
|
|
129
|
+
paletteOptions
|
|
130
|
+
]);
|
|
121
131
|
useEffect(() => {
|
|
122
132
|
if (typeof window === "undefined") return;
|
|
123
133
|
const handleError = (e) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-sidebar-account.d.ts","sourceRoot":"","sources":["../../src/shell/app-sidebar-account.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"app-sidebar-account.d.ts","sourceRoot":"","sources":["../../src/shell/app-sidebar-account.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAElD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AA0DzD,wBAAgB,cAAc,CAAC,KAAK,EAAE,kBAAkB,GAAG,WAAW,+BAiGrE"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Group, Menu, Skeleton, Stack, Text, UnstyledButton } from "@mantine/core";
|
|
3
3
|
import { useBasaltSpacing } from "../theme/index.js";
|
|
4
|
+
import { VX } from "../tokens/index.js";
|
|
4
5
|
import { cx } from "../common/props.js";
|
|
5
6
|
import { assertRequiredProps } from "../common/validate.js";
|
|
6
7
|
import classes from "./app-sidebar.module.css";
|
|
@@ -68,13 +69,13 @@ function SidebarAccount(props) {
|
|
|
68
69
|
return /* @__PURE__ */ jsxs(
|
|
69
70
|
UnstyledButton,
|
|
70
71
|
{
|
|
71
|
-
className: cx(classes.
|
|
72
|
+
className: cx(classes.footerBtn, className),
|
|
72
73
|
"aria-label": "Sign in",
|
|
73
74
|
onClick: actions?.onSignIn,
|
|
74
75
|
...style !== void 0 && { style },
|
|
75
76
|
children: [
|
|
76
|
-
/* @__PURE__ */ jsx(IconUser, {}),
|
|
77
|
-
/* @__PURE__ */ jsx(Text, { className: classes.accountText,
|
|
77
|
+
/* @__PURE__ */ jsx("span", { className: classes.footerIconSlot, children: /* @__PURE__ */ jsx(IconUser, {}) }),
|
|
78
|
+
/* @__PURE__ */ jsx(Text, { className: classes.accountText, fz: VX.text.md, children: "Sign in" })
|
|
78
79
|
]
|
|
79
80
|
}
|
|
80
81
|
);
|
|
@@ -205,9 +205,8 @@
|
|
|
205
205
|
* bottom-placed block (a `kind: 'progress'` block defaults to `'bottom'`) stacked FLUSH on the
|
|
206
206
|
* settings and account rows and on the scroll region above, which is the "cramped" footer.
|
|
207
207
|
*
|
|
208
|
-
* `region-gap` (12) above the cluster, `stack-sm` (8) between
|
|
209
|
-
*
|
|
210
|
-
* not a list of peer sections.
|
|
208
|
+
* `region-gap` (12) above the cluster, `stack-sm` (8) between BLOCKS, and the settings/account rows
|
|
209
|
+
* on the nav's own 1px pitch (see `.footerLinks + *` below): the footer rows are one list with the nav.
|
|
211
210
|
*/
|
|
212
211
|
.footer {
|
|
213
212
|
flex-shrink: 0;
|
|
@@ -223,6 +222,12 @@
|
|
|
223
222
|
margin-top: var(--vx-space-stack-sm);
|
|
224
223
|
}
|
|
225
224
|
|
|
225
|
+
/* Footer rows ARE a list like the nav's (`Stack gap={1}`): the settings rows and the account row
|
|
226
|
+
* sit on the nav's 1px pitch, not the cluster gap that separates blocks from rows. */
|
|
227
|
+
.footer > .footerLinks + * {
|
|
228
|
+
margin-top: 1px;
|
|
229
|
+
}
|
|
230
|
+
|
|
226
231
|
/*
|
|
227
232
|
* Footer settings row — IDENTICAL geometry to a nav row (padding 6px 10px, radius 6, gap 10,
|
|
228
233
|
* 15px): muted text + faint icon at rest, ink-6% tint + ink on hover, exactly like an inactive
|
|
@@ -237,6 +242,9 @@
|
|
|
237
242
|
padding: var(--vx-space-row-inset-y) var(--vx-space-row-inset-x);
|
|
238
243
|
border-radius: var(--vx-radius-ctrl);
|
|
239
244
|
font-size: var(--vx-text-md);
|
|
245
|
+
/* The nav row's line-height, so a footer row is exactly as tall as a nav row (Mantine's `Text`
|
|
246
|
+
* would otherwise bring its own 1.55 and add ~3px). */
|
|
247
|
+
line-height: var(--vx-space-row-line-height);
|
|
240
248
|
color: var(--vx-muted);
|
|
241
249
|
}
|
|
242
250
|
|
|
@@ -272,11 +280,12 @@
|
|
|
272
280
|
.footerText {
|
|
273
281
|
flex: 1;
|
|
274
282
|
text-align: left;
|
|
283
|
+
line-height: var(--vx-space-row-line-height);
|
|
275
284
|
}
|
|
276
285
|
|
|
277
286
|
/*
|
|
278
|
-
* Sidebar account row (SidebarAccount) —
|
|
279
|
-
* border; hover = the same ink-6% tint as every other row
|
|
287
|
+
* Sidebar account row (SidebarAccount, authenticated) — a nav row's padding and gap, so its leading
|
|
288
|
+
* block starts on the nav icon column; NO border; hover = the same ink-6% tint as every other row.
|
|
280
289
|
*/
|
|
281
290
|
.accountRow {
|
|
282
291
|
flex: 1;
|
|
@@ -284,8 +293,7 @@
|
|
|
284
293
|
display: flex;
|
|
285
294
|
align-items: center;
|
|
286
295
|
gap: var(--vx-space-nav-icon-gap);
|
|
287
|
-
padding: var(--vx-space-
|
|
288
|
-
var(--vx-space-sidebar-account-inset-bottom);
|
|
296
|
+
padding: var(--vx-space-row-inset-y) var(--vx-space-row-inset-x);
|
|
289
297
|
border-radius: var(--vx-radius-ctrl);
|
|
290
298
|
}
|
|
291
299
|
|
|
@@ -297,6 +305,7 @@
|
|
|
297
305
|
flex: 1;
|
|
298
306
|
min-width: 0;
|
|
299
307
|
text-align: left;
|
|
308
|
+
line-height: var(--vx-space-row-line-height);
|
|
300
309
|
}
|
|
301
310
|
|
|
302
311
|
/*
|
|
@@ -585,6 +594,7 @@
|
|
|
585
594
|
.footerLinks {
|
|
586
595
|
display: flex;
|
|
587
596
|
flex-direction: column;
|
|
597
|
+
gap: 1px;
|
|
588
598
|
}
|
|
589
599
|
|
|
590
600
|
/* Fixed 18px icon column for a flat footer row — an icon-less row keeps the label aligned with
|
package/dist/styles.css
CHANGED
|
@@ -97,11 +97,15 @@
|
|
|
97
97
|
and re-opens the zoom bug — a floor a `style` prop can override is not a floor, it's a
|
|
98
98
|
suggestion. `check-theme`'s `raw-form-control` + `sub-16-input-font` guard kinds catch the
|
|
99
99
|
authoring mistake at build time; this `!important` is the runtime backstop that makes it
|
|
100
|
-
un-defeatable even when a violation slips through.
|
|
100
|
+
un-defeatable even when a violation slips through.
|
|
101
|
+
|
|
102
|
+
`--basalt-input-fz-floor` is the one knob: `createBasaltTheme(…, { inputs: 'desktop' })` has the
|
|
103
|
+
provider set it to 0px for an app that never runs on iOS Safari (a Tauri/desktop shell), where
|
|
104
|
+
the 16px floor is only visual weight. Default stays 16px. */
|
|
101
105
|
input,
|
|
102
106
|
select,
|
|
103
107
|
textarea {
|
|
104
|
-
font-size: max(16px, var(--input-fz, 1rem)) !important;
|
|
108
|
+
font-size: max(var(--basalt-input-fz-floor, 16px), var(--input-fz, 1rem)) !important;
|
|
105
109
|
}
|
|
106
110
|
|
|
107
111
|
/* `data-numeric` — one typographic law, every element. A number read as a MEASUREMENT (a preset
|
package/dist/theme/index.d.ts
CHANGED
|
@@ -99,6 +99,21 @@ export type CreateBasaltThemeOptions = {
|
|
|
99
99
|
* createBasaltTheme(undefined, { density: -2 })
|
|
100
100
|
*/
|
|
101
101
|
density?: number;
|
|
102
|
+
/**
|
|
103
|
+
* Input sizing for the platform the app runs on. `'touch'` (default) keeps the shipped identity:
|
|
104
|
+
* `md` inputs (42px) with the 16px font floor that stops iOS Safari's focus-zoom. `'desktop'` is
|
|
105
|
+
* for an app that never runs in a mobile browser (a Tauri/Electron shell): the Input family
|
|
106
|
+
* (`TextInput`, `NumberInput`, `PasswordInput`, `Select`, `Textarea`, `Input`) defaults to
|
|
107
|
+
* Mantine's `sm` (36px, `--vx-text-sm` font) and the provider drops the 16px floor, so every
|
|
108
|
+
* input Mantine ships — `MultiSelect`/`TagsInput`/pills included, they already default to `sm` —
|
|
109
|
+
* renders at its own size. An explicit `size` prop still wins. Stashed on
|
|
110
|
+
* `theme.other.basaltInputs` (only when non-default); `BasaltProvider` emits the floor override,
|
|
111
|
+
* so `injectPalette={false}` also opts it out.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* createBasaltTheme(undefined, { inputs: 'desktop' })
|
|
115
|
+
*/
|
|
116
|
+
inputs?: 'touch' | 'desktop';
|
|
102
117
|
};
|
|
103
118
|
/**
|
|
104
119
|
* Build the Basalt theme, optionally merged with consumer overrides. Overrides win on conflict
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AAiEA,OAAO,KAAK,EACV,oBAAoB,EAGpB,oBAAoB,EAErB,MAAM,eAAe,CAAA;AAStB,OAAO,KAAK,EAAe,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAE/E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAGpD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAQlD;;;;;GAKG;AACH,YAAY,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAIlD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAMvD,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;AAgBnE,OAAO,QAAQ,eAAe,CAAC;IAC7B,UAAU,iBAAiB;QACzB,YAAY,CAAC,EAAE,YAAY,CAAA;QAC3B,WAAW,CAAC,EAAE,iBAAiB,CAAA;QAC/B,YAAY,CAAC,EAAE,YAAY,CAAA;QAC3B,aAAa,CAAC,EAAE,WAAW,CAAA;KAC5B;CACF;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AAiEA,OAAO,KAAK,EACV,oBAAoB,EAGpB,oBAAoB,EAErB,MAAM,eAAe,CAAA;AAStB,OAAO,KAAK,EAAe,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAE/E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAGpD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAQlD;;;;;GAKG;AACH,YAAY,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAIlD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAMvD,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;AAgBnE,OAAO,QAAQ,eAAe,CAAC;IAC7B,UAAU,iBAAiB;QACzB,YAAY,CAAC,EAAE,YAAY,CAAA;QAC3B,WAAW,CAAC,EAAE,iBAAiB,CAAA;QAC/B,YAAY,CAAC,EAAE,YAAY,CAAA;QAC3B,aAAa,CAAC,EAAE,WAAW,CAAA;KAC5B;CACF;AAgiCD;+EAC+E;AAC/E,eAAO,MAAM,SAAS,EAAE,oBAAuD,CAAA;AAyT/E;;8FAE8F;AAC9F,eAAO,MAAM,oBAAoB,EAAE,oBACc,CAAA;AAEjD,kEAAkE;AAClE,MAAM,MAAM,wBAAwB,GAAG;IACrC;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;IAE9B;;;;;;;;;;;;;;;;OAgBG;IACH,KAAK,CAAC,EAAE,iBAAiB,CAAA;IAEzB;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IAEf;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAEhB;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAC7B,CAAA;AA0BD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,CAAC,EAAE,IAAI,CAAC,oBAAoB,EAAE,aAAa,CAAC,EACrD,OAAO,CAAC,EAAE,wBAAwB,GACjC,oBAAoB,CAmCtB"}
|
package/dist/theme/index.js
CHANGED
|
@@ -530,7 +530,8 @@ function buildTheme(data, options = {}) {
|
|
|
530
530
|
defaultProps: { radius: radius.card },
|
|
531
531
|
classNames: {
|
|
532
532
|
label: segmentedControlClasses.label,
|
|
533
|
-
control: segmentedControlClasses.control
|
|
533
|
+
control: segmentedControlClasses.control,
|
|
534
|
+
indicator: segmentedControlClasses.indicator
|
|
534
535
|
},
|
|
535
536
|
// `label` gets a `ctl`-only min-height (function-form `styles`, same `props.size === …` gate
|
|
536
537
|
// as Button/ActionIcon/Input's `vars` overrides above) so a `size="ctl"` SegmentedControl
|
|
@@ -539,7 +540,7 @@ function buildTheme(data, options = {}) {
|
|
|
539
540
|
// density. `sm`/`md` SegmentedControls (forms) are untouched, keeping Mantine's own sizing.
|
|
540
541
|
styles: (_theme, props) => ({
|
|
541
542
|
root: {
|
|
542
|
-
backgroundColor: "color-mix(in srgb, var(--vx-ink)
|
|
543
|
+
backgroundColor: "color-mix(in srgb, var(--vx-ink) 8%, transparent)",
|
|
543
544
|
padding: `${SPACE_FIXED.segmentedTrackInset}px`,
|
|
544
545
|
// The OUTER safety net a `.control`'s own `min-width: 0` (`segmented-control.module.css`)
|
|
545
546
|
// cannot provide by itself: that rule only lets each SEGMENT shrink below its own label's
|
|
@@ -556,8 +557,13 @@ function buildTheme(data, options = {}) {
|
|
|
556
557
|
minWidth: "max-content"
|
|
557
558
|
},
|
|
558
559
|
indicator: {
|
|
559
|
-
|
|
560
|
-
|
|
560
|
+
// A RAISED pill, not a darker one: its fill (`segmented-control.module.css`, per scheme) is
|
|
561
|
+
// lighter than the ink-tinted track in both schemes, + the shared raised shadow + a 1px ink outline.
|
|
562
|
+
// Same model as Mantine's default (white indicator, shadow, on a grey track) and
|
|
563
|
+
// shadcn's Tabs/ToggleGroup (`bg-background` + shadow on a `bg-muted` track). The old
|
|
564
|
+
// ink-12% fill sat one shade from the track and read as a hover state.
|
|
565
|
+
boxShadow: "var(--vx-shadow-raised)",
|
|
566
|
+
outline: "1px solid color-mix(in srgb, var(--vx-ink) 14%, transparent)",
|
|
561
567
|
borderRadius: "var(--vx-radius-tight)"
|
|
562
568
|
},
|
|
563
569
|
// Conditionally spread the WHOLE key (never a present `label: undefined`) — the package
|
|
@@ -1037,6 +1043,17 @@ const cssVariablesResolver = buildCssVariablesResolver(DEFAULT_PALETTE_DATA);
|
|
|
1037
1043
|
function hasFontsConfig(fonts) {
|
|
1038
1044
|
return fonts !== void 0 && Object.values(fonts).some((value) => value !== void 0);
|
|
1039
1045
|
}
|
|
1046
|
+
const DESKTOP_INPUTS_THEME = {
|
|
1047
|
+
components: {
|
|
1048
|
+
Input: Input.extend({ defaultProps: { size: "sm" } }),
|
|
1049
|
+
TextInput: TextInput.extend({ defaultProps: { size: "sm" } }),
|
|
1050
|
+
NumberInput: NumberInput.extend({ defaultProps: { size: "sm" } }),
|
|
1051
|
+
PasswordInput: PasswordInput.extend({ defaultProps: { size: "sm" } }),
|
|
1052
|
+
Select: Select.extend({ defaultProps: { size: "sm" } }),
|
|
1053
|
+
Textarea: Textarea.extend({ defaultProps: { size: "sm" } })
|
|
1054
|
+
},
|
|
1055
|
+
other: { basaltInputs: "desktop" }
|
|
1056
|
+
};
|
|
1040
1057
|
function createBasaltTheme(overrides, options) {
|
|
1041
1058
|
const resolvedConfig = resolveDeriveConfig(options?.derive);
|
|
1042
1059
|
const radiusLevel = options?.radius ?? 0;
|
|
@@ -1046,6 +1063,7 @@ function createBasaltTheme(overrides, options) {
|
|
|
1046
1063
|
const spaceValues = deriveSpacing(densityLevel);
|
|
1047
1064
|
const isDefaultDensity = densityLevel === 0;
|
|
1048
1065
|
const isDefaultDerive = isDefaultDeriveConfig(resolvedConfig);
|
|
1066
|
+
const desktopInputs = options?.inputs === "desktop";
|
|
1049
1067
|
let theme;
|
|
1050
1068
|
if (isDefaultDerive && isDefaultRadius && isDefaultDensity) {
|
|
1051
1069
|
theme = baseTheme;
|
|
@@ -1066,6 +1084,7 @@ function createBasaltTheme(overrides, options) {
|
|
|
1066
1084
|
if (hasFontsConfig(fontsConfig)) {
|
|
1067
1085
|
theme = mergeThemeOverrides(theme, { other: { basaltFonts: fontsConfig } });
|
|
1068
1086
|
}
|
|
1087
|
+
if (desktopInputs) theme = mergeThemeOverrides(theme, DESKTOP_INPUTS_THEME);
|
|
1069
1088
|
if (!overrides) return theme;
|
|
1070
1089
|
return { ...mergeThemeOverrides(theme, overrides), breakpoints: SIZE_CLASS_BREAKPOINTS };
|
|
1071
1090
|
}
|
|
@@ -54,3 +54,19 @@
|
|
|
54
54
|
font-family: var(--basalt-font-mono);
|
|
55
55
|
font-size: var(--vx-text-xs);
|
|
56
56
|
}
|
|
57
|
+
|
|
58
|
+
/*
|
|
59
|
+
* Selected-segment fill — LIGHTER than the ink-8% track in both schemes, which is what reads as
|
|
60
|
+
* raised. The two schemes need different recipes: the track is an ink tint, so on light it is
|
|
61
|
+
* darker than the page (the panel surface sits above it) while on dark it is LIGHTER than the page,
|
|
62
|
+
* and the same panel surface would then sink below it. Dark therefore lifts the elevated surface
|
|
63
|
+
* with ink. In the module (not the theme's inline `styles`) so the scheme can be selected on —
|
|
64
|
+
* same ancestor-attribute pattern as `controls.module.css`.
|
|
65
|
+
*/
|
|
66
|
+
.indicator {
|
|
67
|
+
background-color: var(--vx-surface-elevated);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
html[data-mantine-color-scheme='dark'] .indicator {
|
|
71
|
+
background-color: color-mix(in srgb, var(--vx-ink) 14%, var(--vx-surface-elevated));
|
|
72
|
+
}
|
package/llms.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# basalt-ui v1.
|
|
1
|
+
# basalt-ui v1.40.2
|
|
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.
|
|
22
|
+
Version: 1.40.2
|
|
23
23
|
|
|
24
24
|
## basalt-ui
|
|
25
25
|
Import: import { ... } from 'basalt-ui'
|
package/package.json
CHANGED
package/src/provider/index.tsx
CHANGED
|
@@ -236,8 +236,9 @@ export function composeInjectedCss(
|
|
|
236
236
|
fontsCss: string,
|
|
237
237
|
radiusCss: string,
|
|
238
238
|
densityCss: string,
|
|
239
|
+
inputsCss = '',
|
|
239
240
|
): string {
|
|
240
|
-
return [base, fontsCss, radiusCss, densityCss].filter(Boolean).join('\n')
|
|
241
|
+
return [base, fontsCss, radiusCss, densityCss, inputsCss].filter(Boolean).join('\n')
|
|
241
242
|
}
|
|
242
243
|
|
|
243
244
|
// ── Host attribute ────────────────────────────────────────────────────────────────────────────────
|
|
@@ -325,6 +326,7 @@ function BasaltBridge({
|
|
|
325
326
|
const fontsConfig = theme.other?.basaltFonts
|
|
326
327
|
const radiusConfig = theme.other?.basaltRadius
|
|
327
328
|
const densityConfig = theme.other?.basaltDensity
|
|
329
|
+
const desktopInputs = theme.other?.['basaltInputs'] === 'desktop'
|
|
328
330
|
const paletteCss = useMemo(() => {
|
|
329
331
|
if (!injectPalette) return ''
|
|
330
332
|
const base =
|
|
@@ -336,8 +338,18 @@ function BasaltBridge({
|
|
|
336
338
|
const fontsCss = buildFontsCss(fontsConfig)
|
|
337
339
|
const radiusCss = buildRadiusCss(radiusConfig)
|
|
338
340
|
const densityCss = buildDensityCss(densityConfig)
|
|
339
|
-
|
|
340
|
-
|
|
341
|
+
// `createBasaltTheme({ inputs: 'desktop' })` — lifts the iOS 16px input-font floor (`styles.css`).
|
|
342
|
+
const inputsCss = desktopInputs ? ':root {\n--basalt-input-fz-floor: 0px;\n}' : ''
|
|
343
|
+
return composeInjectedCss(base, fontsCss, radiusCss, densityCss, inputsCss)
|
|
344
|
+
}, [
|
|
345
|
+
injectPalette,
|
|
346
|
+
deriveConfig,
|
|
347
|
+
fontsConfig,
|
|
348
|
+
radiusConfig,
|
|
349
|
+
densityConfig,
|
|
350
|
+
desktopInputs,
|
|
351
|
+
paletteOptions,
|
|
352
|
+
])
|
|
341
353
|
|
|
342
354
|
useEffect(() => {
|
|
343
355
|
// SSR guard — window is not available in server contexts
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { Group, Menu, Skeleton, Stack, Text, UnstyledButton } from '@mantine/core'
|
|
20
20
|
import { useBasaltSpacing } from '../theme'
|
|
21
|
+
import { VX } from '../tokens'
|
|
21
22
|
import { cx } from '../common/props'
|
|
22
23
|
import type { BasaltProps } from '../common/props'
|
|
23
24
|
import { assertRequiredProps } from '../common/validate'
|
|
@@ -100,15 +101,19 @@ export function SidebarAccount(props: BasaltAccountProps & BasaltProps) {
|
|
|
100
101
|
if (state.status === 'unauthenticated') {
|
|
101
102
|
return (
|
|
102
103
|
<UnstyledButton
|
|
103
|
-
|
|
104
|
+
// A footer row IS a nav row's geometry (`.footerBtn`) — same padding, icon column, muted
|
|
105
|
+
// rest state and hover as the Settings row above it, so the footer reads as one cluster.
|
|
106
|
+
className={cx(classes.footerBtn, className)}
|
|
104
107
|
// The collapsed rail hides `.accountText` (display:none), which drops "Sign in" from the
|
|
105
108
|
// accessibility tree — the label keeps the button nameable with only the glyph showing.
|
|
106
109
|
aria-label="Sign in"
|
|
107
110
|
onClick={actions?.onSignIn}
|
|
108
111
|
{...(style !== undefined && { style })}
|
|
109
112
|
>
|
|
110
|
-
<
|
|
111
|
-
|
|
113
|
+
<span className={classes.footerIconSlot}>
|
|
114
|
+
<IconUser />
|
|
115
|
+
</span>
|
|
116
|
+
<Text className={classes.accountText} fz={VX.text.md}>
|
|
112
117
|
Sign in
|
|
113
118
|
</Text>
|
|
114
119
|
</UnstyledButton>
|
|
@@ -205,9 +205,8 @@
|
|
|
205
205
|
* bottom-placed block (a `kind: 'progress'` block defaults to `'bottom'`) stacked FLUSH on the
|
|
206
206
|
* settings and account rows and on the scroll region above, which is the "cramped" footer.
|
|
207
207
|
*
|
|
208
|
-
* `region-gap` (12) above the cluster, `stack-sm` (8) between
|
|
209
|
-
*
|
|
210
|
-
* not a list of peer sections.
|
|
208
|
+
* `region-gap` (12) above the cluster, `stack-sm` (8) between BLOCKS, and the settings/account rows
|
|
209
|
+
* on the nav's own 1px pitch (see `.footerLinks + *` below): the footer rows are one list with the nav.
|
|
211
210
|
*/
|
|
212
211
|
.footer {
|
|
213
212
|
flex-shrink: 0;
|
|
@@ -223,6 +222,12 @@
|
|
|
223
222
|
margin-top: var(--vx-space-stack-sm);
|
|
224
223
|
}
|
|
225
224
|
|
|
225
|
+
/* Footer rows ARE a list like the nav's (`Stack gap={1}`): the settings rows and the account row
|
|
226
|
+
* sit on the nav's 1px pitch, not the cluster gap that separates blocks from rows. */
|
|
227
|
+
.footer > .footerLinks + * {
|
|
228
|
+
margin-top: 1px;
|
|
229
|
+
}
|
|
230
|
+
|
|
226
231
|
/*
|
|
227
232
|
* Footer settings row — IDENTICAL geometry to a nav row (padding 6px 10px, radius 6, gap 10,
|
|
228
233
|
* 15px): muted text + faint icon at rest, ink-6% tint + ink on hover, exactly like an inactive
|
|
@@ -237,6 +242,9 @@
|
|
|
237
242
|
padding: var(--vx-space-row-inset-y) var(--vx-space-row-inset-x);
|
|
238
243
|
border-radius: var(--vx-radius-ctrl);
|
|
239
244
|
font-size: var(--vx-text-md);
|
|
245
|
+
/* The nav row's line-height, so a footer row is exactly as tall as a nav row (Mantine's `Text`
|
|
246
|
+
* would otherwise bring its own 1.55 and add ~3px). */
|
|
247
|
+
line-height: var(--vx-space-row-line-height);
|
|
240
248
|
color: var(--vx-muted);
|
|
241
249
|
}
|
|
242
250
|
|
|
@@ -272,11 +280,12 @@
|
|
|
272
280
|
.footerText {
|
|
273
281
|
flex: 1;
|
|
274
282
|
text-align: left;
|
|
283
|
+
line-height: var(--vx-space-row-line-height);
|
|
275
284
|
}
|
|
276
285
|
|
|
277
286
|
/*
|
|
278
|
-
* Sidebar account row (SidebarAccount) —
|
|
279
|
-
* border; hover = the same ink-6% tint as every other row
|
|
287
|
+
* Sidebar account row (SidebarAccount, authenticated) — a nav row's padding and gap, so its leading
|
|
288
|
+
* block starts on the nav icon column; NO border; hover = the same ink-6% tint as every other row.
|
|
280
289
|
*/
|
|
281
290
|
.accountRow {
|
|
282
291
|
flex: 1;
|
|
@@ -284,8 +293,7 @@
|
|
|
284
293
|
display: flex;
|
|
285
294
|
align-items: center;
|
|
286
295
|
gap: var(--vx-space-nav-icon-gap);
|
|
287
|
-
padding: var(--vx-space-
|
|
288
|
-
var(--vx-space-sidebar-account-inset-bottom);
|
|
296
|
+
padding: var(--vx-space-row-inset-y) var(--vx-space-row-inset-x);
|
|
289
297
|
border-radius: var(--vx-radius-ctrl);
|
|
290
298
|
}
|
|
291
299
|
|
|
@@ -297,6 +305,7 @@
|
|
|
297
305
|
flex: 1;
|
|
298
306
|
min-width: 0;
|
|
299
307
|
text-align: left;
|
|
308
|
+
line-height: var(--vx-space-row-line-height);
|
|
300
309
|
}
|
|
301
310
|
|
|
302
311
|
/*
|
|
@@ -585,6 +594,7 @@
|
|
|
585
594
|
.footerLinks {
|
|
586
595
|
display: flex;
|
|
587
596
|
flex-direction: column;
|
|
597
|
+
gap: 1px;
|
|
588
598
|
}
|
|
589
599
|
|
|
590
600
|
/* Fixed 18px icon column for a flat footer row — an icon-less row keeps the label aligned with
|
package/src/styles.css
CHANGED
|
@@ -97,11 +97,15 @@
|
|
|
97
97
|
and re-opens the zoom bug — a floor a `style` prop can override is not a floor, it's a
|
|
98
98
|
suggestion. `check-theme`'s `raw-form-control` + `sub-16-input-font` guard kinds catch the
|
|
99
99
|
authoring mistake at build time; this `!important` is the runtime backstop that makes it
|
|
100
|
-
un-defeatable even when a violation slips through.
|
|
100
|
+
un-defeatable even when a violation slips through.
|
|
101
|
+
|
|
102
|
+
`--basalt-input-fz-floor` is the one knob: `createBasaltTheme(…, { inputs: 'desktop' })` has the
|
|
103
|
+
provider set it to 0px for an app that never runs on iOS Safari (a Tauri/desktop shell), where
|
|
104
|
+
the 16px floor is only visual weight. Default stays 16px. */
|
|
101
105
|
input,
|
|
102
106
|
select,
|
|
103
107
|
textarea {
|
|
104
|
-
font-size: max(16px, var(--input-fz, 1rem)) !important;
|
|
108
|
+
font-size: max(var(--basalt-input-fz-floor, 16px), var(--input-fz, 1rem)) !important;
|
|
105
109
|
}
|
|
106
110
|
|
|
107
111
|
/* `data-numeric` — one typographic law, every element. A number read as a MEASUREMENT (a preset
|
package/src/theme/index.ts
CHANGED
|
@@ -810,6 +810,7 @@ function buildTheme(data: PaletteData, options: BuildThemeOptions = {}): Mantine
|
|
|
810
810
|
classNames: {
|
|
811
811
|
label: segmentedControlClasses.label,
|
|
812
812
|
control: segmentedControlClasses.control,
|
|
813
|
+
indicator: segmentedControlClasses.indicator,
|
|
813
814
|
},
|
|
814
815
|
// `label` gets a `ctl`-only min-height (function-form `styles`, same `props.size === …` gate
|
|
815
816
|
// as Button/ActionIcon/Input's `vars` overrides above) so a `size="ctl"` SegmentedControl
|
|
@@ -818,7 +819,7 @@ function buildTheme(data: PaletteData, options: BuildThemeOptions = {}): Mantine
|
|
|
818
819
|
// density. `sm`/`md` SegmentedControls (forms) are untouched, keeping Mantine's own sizing.
|
|
819
820
|
styles: (_theme, props) => ({
|
|
820
821
|
root: {
|
|
821
|
-
backgroundColor: 'color-mix(in srgb, var(--vx-ink)
|
|
822
|
+
backgroundColor: 'color-mix(in srgb, var(--vx-ink) 8%, transparent)',
|
|
822
823
|
padding: `${SPACE_FIXED.segmentedTrackInset}px`,
|
|
823
824
|
// The OUTER safety net a `.control`'s own `min-width: 0` (`segmented-control.module.css`)
|
|
824
825
|
// cannot provide by itself: that rule only lets each SEGMENT shrink below its own label's
|
|
@@ -835,8 +836,13 @@ function buildTheme(data: PaletteData, options: BuildThemeOptions = {}): Mantine
|
|
|
835
836
|
minWidth: 'max-content',
|
|
836
837
|
},
|
|
837
838
|
indicator: {
|
|
838
|
-
|
|
839
|
-
|
|
839
|
+
// A RAISED pill, not a darker one: its fill (`segmented-control.module.css`, per scheme) is
|
|
840
|
+
// lighter than the ink-tinted track in both schemes, + the shared raised shadow + a 1px ink outline.
|
|
841
|
+
// Same model as Mantine's default (white indicator, shadow, on a grey track) and
|
|
842
|
+
// shadcn's Tabs/ToggleGroup (`bg-background` + shadow on a `bg-muted` track). The old
|
|
843
|
+
// ink-12% fill sat one shade from the track and read as a hover state.
|
|
844
|
+
boxShadow: 'var(--vx-shadow-raised)',
|
|
845
|
+
outline: '1px solid color-mix(in srgb, var(--vx-ink) 14%, transparent)',
|
|
840
846
|
borderRadius: 'var(--vx-radius-tight)',
|
|
841
847
|
},
|
|
842
848
|
// Conditionally spread the WHOLE key (never a present `label: undefined`) — the package
|
|
@@ -1577,6 +1583,22 @@ export type CreateBasaltThemeOptions = {
|
|
|
1577
1583
|
* createBasaltTheme(undefined, { density: -2 })
|
|
1578
1584
|
*/
|
|
1579
1585
|
density?: number
|
|
1586
|
+
|
|
1587
|
+
/**
|
|
1588
|
+
* Input sizing for the platform the app runs on. `'touch'` (default) keeps the shipped identity:
|
|
1589
|
+
* `md` inputs (42px) with the 16px font floor that stops iOS Safari's focus-zoom. `'desktop'` is
|
|
1590
|
+
* for an app that never runs in a mobile browser (a Tauri/Electron shell): the Input family
|
|
1591
|
+
* (`TextInput`, `NumberInput`, `PasswordInput`, `Select`, `Textarea`, `Input`) defaults to
|
|
1592
|
+
* Mantine's `sm` (36px, `--vx-text-sm` font) and the provider drops the 16px floor, so every
|
|
1593
|
+
* input Mantine ships — `MultiSelect`/`TagsInput`/pills included, they already default to `sm` —
|
|
1594
|
+
* renders at its own size. An explicit `size` prop still wins. Stashed on
|
|
1595
|
+
* `theme.other.basaltInputs` (only when non-default); `BasaltProvider` emits the floor override,
|
|
1596
|
+
* so `injectPalette={false}` also opts it out.
|
|
1597
|
+
*
|
|
1598
|
+
* @example
|
|
1599
|
+
* createBasaltTheme(undefined, { inputs: 'desktop' })
|
|
1600
|
+
*/
|
|
1601
|
+
inputs?: 'touch' | 'desktop'
|
|
1580
1602
|
}
|
|
1581
1603
|
|
|
1582
1604
|
/**
|
|
@@ -1589,6 +1611,20 @@ function hasFontsConfig(fonts: BasaltFontsConfig | undefined): fonts is BasaltFo
|
|
|
1589
1611
|
return fonts !== undefined && Object.values(fonts).some((value) => value !== undefined)
|
|
1590
1612
|
}
|
|
1591
1613
|
|
|
1614
|
+
/** `{ inputs: 'desktop' }` — see {@link CreateBasaltThemeOptions.inputs}. Merged AFTER the base
|
|
1615
|
+
* theme, so `defaultProps.size: 'sm'` replaces the base's `'md'` and `other` deep-merges. */
|
|
1616
|
+
const DESKTOP_INPUTS_THEME: MantineThemeOverride = {
|
|
1617
|
+
components: {
|
|
1618
|
+
Input: Input.extend({ defaultProps: { size: 'sm' } }),
|
|
1619
|
+
TextInput: TextInput.extend({ defaultProps: { size: 'sm' } }),
|
|
1620
|
+
NumberInput: NumberInput.extend({ defaultProps: { size: 'sm' } }),
|
|
1621
|
+
PasswordInput: PasswordInput.extend({ defaultProps: { size: 'sm' } }),
|
|
1622
|
+
Select: Select.extend({ defaultProps: { size: 'sm' } }),
|
|
1623
|
+
Textarea: Textarea.extend({ defaultProps: { size: 'sm' } }),
|
|
1624
|
+
},
|
|
1625
|
+
other: { basaltInputs: 'desktop' },
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1592
1628
|
/**
|
|
1593
1629
|
* Build the Basalt theme, optionally merged with consumer overrides. Overrides win on conflict
|
|
1594
1630
|
* (Mantine `mergeThemeOverrides` is last-wins), so a consumer can retune any field without
|
|
@@ -1625,6 +1661,7 @@ export function createBasaltTheme(
|
|
|
1625
1661
|
const spaceValues = deriveSpacing(densityLevel)
|
|
1626
1662
|
const isDefaultDensity = densityLevel === 0
|
|
1627
1663
|
const isDefaultDerive = isDefaultDeriveConfig(resolvedConfig)
|
|
1664
|
+
const desktopInputs = options?.inputs === 'desktop'
|
|
1628
1665
|
|
|
1629
1666
|
let theme: MantineThemeOverride
|
|
1630
1667
|
if (isDefaultDerive && isDefaultRadius && isDefaultDensity) {
|
|
@@ -1646,6 +1683,7 @@ export function createBasaltTheme(
|
|
|
1646
1683
|
if (hasFontsConfig(fontsConfig)) {
|
|
1647
1684
|
theme = mergeThemeOverrides(theme, { other: { basaltFonts: fontsConfig } })
|
|
1648
1685
|
}
|
|
1686
|
+
if (desktopInputs) theme = mergeThemeOverrides(theme, DESKTOP_INPUTS_THEME)
|
|
1649
1687
|
if (!overrides) return theme
|
|
1650
1688
|
// `theme.breakpoints` is derived from `SIZE_CLASSES` — a JS caller's override is dropped.
|
|
1651
1689
|
return { ...mergeThemeOverrides(theme, overrides), breakpoints: SIZE_CLASS_BREAKPOINTS }
|
|
@@ -54,3 +54,19 @@
|
|
|
54
54
|
font-family: var(--basalt-font-mono);
|
|
55
55
|
font-size: var(--vx-text-xs);
|
|
56
56
|
}
|
|
57
|
+
|
|
58
|
+
/*
|
|
59
|
+
* Selected-segment fill — LIGHTER than the ink-8% track in both schemes, which is what reads as
|
|
60
|
+
* raised. The two schemes need different recipes: the track is an ink tint, so on light it is
|
|
61
|
+
* darker than the page (the panel surface sits above it) while on dark it is LIGHTER than the page,
|
|
62
|
+
* and the same panel surface would then sink below it. Dark therefore lifts the elevated surface
|
|
63
|
+
* with ink. In the module (not the theme's inline `styles`) so the scheme can be selected on —
|
|
64
|
+
* same ancestor-attribute pattern as `controls.module.css`.
|
|
65
|
+
*/
|
|
66
|
+
.indicator {
|
|
67
|
+
background-color: var(--vx-surface-elevated);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
html[data-mantine-color-scheme='dark'] .indicator {
|
|
71
|
+
background-color: color-mix(in srgb, var(--vx-ink) 14%, var(--vx-surface-elevated));
|
|
72
|
+
}
|