basalt-ui 1.30.2 → 1.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +31 -0
- package/MIGRATING.md +172 -12
- package/agent/rules/basalt-controls.md +19 -19
- package/agent/rules/basalt-mantine.md +1 -1
- package/configs/oxlint-plugin.js +254 -4
- package/configs/oxlint-plugin.test.ts +145 -2
- package/configs/oxlint.json +2 -1
- package/dist/agent-chat/composer.d.ts.map +1 -1
- package/dist/agent-chat/composer.js +41 -43
- package/dist/agent-chat/thread-workspace.d.ts +2 -2
- package/dist/agent-chat/thread-workspace.d.ts.map +1 -1
- package/dist/agent-chat/thread-workspace.js +3 -4
- package/dist/agent-chat/use-keyboard-inset.d.ts +2 -0
- package/dist/agent-chat/use-keyboard-inset.d.ts.map +1 -0
- package/dist/agent-chat/use-keyboard-inset.js +34 -0
- package/dist/charts/cursor/touch-pin.d.ts +45 -0
- package/dist/charts/cursor/touch-pin.d.ts.map +1 -0
- package/dist/charts/cursor/touch-pin.js +79 -0
- package/dist/charts/hooks/use-discrete-cursor.d.ts +66 -0
- package/dist/charts/hooks/use-discrete-cursor.d.ts.map +1 -0
- package/dist/charts/hooks/use-discrete-cursor.js +198 -0
- package/dist/charts/hooks/use-hysteresis.d.ts +18 -0
- package/dist/charts/hooks/use-hysteresis.d.ts.map +1 -0
- package/dist/charts/hooks/use-hysteresis.js +12 -0
- package/dist/charts/hooks/useBandPlot.d.ts +7 -0
- package/dist/charts/hooks/useBandPlot.d.ts.map +1 -1
- package/dist/charts/hooks/useBandPlot.js +62 -18
- package/dist/charts/hooks/useChartCursor.d.ts +40 -11
- package/dist/charts/hooks/useChartCursor.d.ts.map +1 -1
- package/dist/charts/hooks/useChartCursor.js +102 -11
- package/dist/charts/index.d.ts +0 -1
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/index.js +0 -10
- package/dist/charts/kinds/BandStrip.d.ts.map +1 -1
- package/dist/charts/kinds/BandStrip.js +54 -41
- package/dist/charts/kinds/Donut.d.ts +9 -3
- package/dist/charts/kinds/Donut.d.ts.map +1 -1
- package/dist/charts/kinds/Donut.js +110 -48
- package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
- package/dist/charts/kinds/DualPanel.js +145 -131
- package/dist/charts/kinds/Heatmap.d.ts +7 -0
- package/dist/charts/kinds/Heatmap.d.ts.map +1 -1
- package/dist/charts/kinds/Heatmap.js +37 -16
- package/dist/charts/kinds/MirroredBars.d.ts.map +1 -1
- package/dist/charts/kinds/MirroredBars.js +83 -68
- package/dist/charts/kinds/donut-layout.d.ts +47 -0
- package/dist/charts/kinds/donut-layout.d.ts.map +1 -0
- package/dist/charts/kinds/donut-layout.js +52 -0
- package/dist/charts/layout/auto-margin.d.ts +7 -2
- package/dist/charts/layout/auto-margin.d.ts.map +1 -1
- package/dist/charts/layout/auto-margin.js +12 -3
- package/dist/charts/layout/end-labels.d.ts +64 -0
- package/dist/charts/layout/end-labels.d.ts.map +1 -0
- package/dist/charts/layout/end-labels.js +76 -0
- package/dist/charts/primitives/Axes.d.ts +32 -4
- package/dist/charts/primitives/Axes.d.ts.map +1 -1
- package/dist/charts/primitives/Axes.js +62 -16
- package/dist/charts/primitives/CartesianChart.d.ts +7 -6
- package/dist/charts/primitives/CartesianChart.d.ts.map +1 -1
- package/dist/charts/primitives/CartesianChart.js +271 -125
- package/dist/charts/primitives/ChartCard.d.ts +0 -14
- package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
- package/dist/charts/primitives/ChartCard.js +74 -21
- package/dist/charts/primitives/ChartFrame.d.ts +28 -22
- package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
- package/dist/charts/primitives/ChartFrame.js +133 -34
- package/dist/charts/primitives/ChartLegend.d.ts +19 -15
- package/dist/charts/primitives/ChartLegend.d.ts.map +1 -1
- package/dist/charts/primitives/ChartLegend.js +212 -71
- package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
- package/dist/charts/primitives/ChartTooltip.js +4 -3
- package/dist/charts/primitives/Crosshair.js +2 -2
- package/dist/charts/primitives/HoverOverlay.d.ts +6 -1
- package/dist/charts/primitives/HoverOverlay.d.ts.map +1 -1
- package/dist/charts/primitives/HoverOverlay.js +37 -26
- package/dist/charts/primitives/HoverOverlay.module.css +13 -0
- package/dist/charts/primitives/HoverOverlay.module.css.d.ts +8 -0
- package/dist/charts/primitives/chart-card-context.d.ts +29 -0
- package/dist/charts/primitives/chart-card-context.d.ts.map +1 -0
- package/dist/charts/primitives/chart-card-context.js +13 -0
- package/dist/charts/primitives/chart-frame-layout.d.ts +93 -113
- package/dist/charts/primitives/chart-frame-layout.d.ts.map +1 -1
- package/dist/charts/primitives/chart-frame-layout.js +76 -29
- package/dist/charts/primitives/chart-layout.d.ts +112 -0
- package/dist/charts/primitives/chart-layout.d.ts.map +1 -0
- package/dist/charts/primitives/chart-layout.js +175 -0
- package/dist/charts/primitives/chart-tier.d.ts +13 -11
- package/dist/charts/primitives/chart-tier.d.ts.map +1 -1
- package/dist/charts/primitives/chart-tier.js +42 -13
- package/dist/charts/series.d.ts +4 -1
- package/dist/charts/series.d.ts.map +1 -1
- package/dist/charts/utils/ticks.d.ts +27 -8
- package/dist/charts/utils/ticks.d.ts.map +1 -1
- package/dist/charts/utils/ticks.js +35 -11
- package/dist/common/errors.d.ts +11 -1
- package/dist/common/errors.d.ts.map +1 -1
- package/dist/common/errors.js +16 -2
- package/dist/common/hit-floor.d.ts +10 -0
- package/dist/common/hit-floor.d.ts.map +1 -0
- package/dist/common/hit-floor.js +6 -0
- package/dist/{shell → common}/isomorphic-layout-effect.d.ts +4 -3
- package/dist/common/isomorphic-layout-effect.d.ts.map +1 -0
- package/dist/common/use-measured-widths.d.ts +37 -0
- package/dist/common/use-measured-widths.d.ts.map +1 -0
- package/dist/common/use-measured-widths.js +51 -0
- package/dist/common/use-media-query.d.ts +6 -0
- package/dist/common/use-media-query.d.ts.map +1 -0
- package/dist/common/use-media-query.js +40 -0
- package/dist/common/validate.d.ts +9 -0
- package/dist/common/validate.d.ts.map +1 -1
- package/dist/common/validate.js +9 -4
- package/dist/content/article-layout.d.ts +4 -2
- package/dist/content/article-layout.d.ts.map +1 -1
- package/dist/content/article-layout.js +43 -3
- package/dist/content/article-layout.module.css +45 -4
- package/dist/content/article-layout.module.css.d.ts +3 -0
- package/dist/content/code-block.d.ts.map +1 -1
- package/dist/content/code-block.js +40 -2
- package/dist/content/code-block.module.css +32 -2
- package/dist/content/prose.module.css +13 -8
- package/dist/controls/actions.d.ts +29 -6
- package/dist/controls/actions.d.ts.map +1 -1
- package/dist/controls/actions.js +158 -27
- package/dist/controls/actions.module.css +14 -1
- package/dist/controls/actions.module.css.d.ts +1 -0
- package/dist/controls/controls.module.css +8 -31
- package/dist/controls/enum-filter.js +2 -2
- package/dist/controls/filter-context.d.ts +10 -0
- package/dist/controls/filter-context.d.ts.map +1 -1
- package/dist/controls/filter-context.js +2 -0
- package/dist/controls/filter-pill.d.ts.map +1 -1
- package/dist/controls/filter-pill.js +1 -0
- package/dist/controls/index.d.ts +1 -1
- package/dist/controls/index.d.ts.map +1 -1
- package/dist/controls/index.js +0 -2
- package/dist/controls/multi-select-filter.js +2 -2
- package/dist/controls/sync-button.module.css +1 -1
- package/dist/dashboard/section.d.ts.map +1 -1
- package/dist/dashboard/section.js +1 -0
- package/dist/dashboard/section.module.css +1 -26
- package/dist/dashboard/stat-card.d.ts +1 -1
- package/dist/dashboard/stat-card.d.ts.map +1 -1
- package/dist/dashboard/stat-card.js +4 -1
- package/dist/dashboard/stat-card.module.css +6 -9
- package/dist/dashboard/stat-group.module.css +16 -4
- package/dist/dashboard/widget-grid.d.ts.map +1 -1
- package/dist/dashboard/widget-header.d.ts +14 -4
- package/dist/dashboard/widget-header.d.ts.map +1 -1
- package/dist/dashboard/widget-header.js +23 -6
- package/dist/dashboard/widget-header.module.css +145 -133
- package/dist/dashboard/widget-header.module.css.d.ts +2 -0
- package/dist/data/data-table.d.ts +23 -0
- package/dist/data/data-table.d.ts.map +1 -1
- package/dist/data/data-table.js +339 -141
- package/dist/data/data-table.module.css +94 -0
- package/dist/data/data-table.module.css.d.ts +11 -0
- package/dist/data/virtual-list.d.ts.map +1 -1
- package/dist/data/virtual-list.js +10 -0
- package/dist/forms/form-layout.d.ts.map +1 -1
- package/dist/forms/form-layout.js +3 -3
- package/dist/forms/form-layout.module.css +30 -5
- package/dist/forms/form-layout.module.css.d.ts +1 -0
- package/dist/guard/index.d.ts +8 -2
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +67 -2
- package/dist/guard/types.d.ts +3 -3
- package/dist/guard/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/provider/index.d.ts +20 -1
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +22 -2
- package/dist/shell/app-brand.d.ts.map +1 -1
- package/dist/shell/app-brand.js +1 -0
- package/dist/shell/app-brand.module.css +2 -2
- package/dist/shell/app-header.module.css +18 -8
- package/dist/shell/app-main.module.css +54 -3
- package/dist/shell/app-main.module.css.d.ts +1 -0
- package/dist/shell/app-mobile-nav.module.css +16 -4
- package/dist/shell/app-sidebar.module.css +4 -6
- package/dist/shell/index.d.ts +12 -0
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +57 -16
- package/dist/shell/mobile-nav-model.d.ts +1 -1
- package/dist/shell/page-aside.d.ts +22 -3
- package/dist/shell/page-aside.d.ts.map +1 -1
- package/dist/shell/page-aside.js +27 -13
- package/dist/shell/page-bar.d.ts.map +1 -1
- package/dist/shell/page-bar.js +10 -5
- package/dist/shell/page-bar.module.css +10 -34
- package/dist/shell/use-breakpoint.d.ts +3 -4
- package/dist/shell/use-breakpoint.d.ts.map +1 -1
- package/dist/shell/use-breakpoint.js +3 -25
- package/dist/shell/use-size-class.d.ts +6 -0
- package/dist/shell/use-size-class.d.ts.map +1 -0
- package/dist/shell/use-size-class.js +19 -0
- package/dist/state/persisted-or-local.d.ts +4 -1
- package/dist/state/persisted-or-local.d.ts.map +1 -1
- package/dist/state/persisted-or-local.js +4 -3
- package/dist/state/persisted.d.ts +10 -0
- package/dist/state/persisted.d.ts.map +1 -1
- package/dist/state/persisted.js +44 -14
- package/dist/styles.css +41 -0
- package/dist/surfaces.d.ts +4 -4
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +11 -3
- package/dist/theme/controls.module.css +7 -0
- package/dist/theme/ctl-theme.d.ts +1 -0
- package/dist/theme/ctl-theme.d.ts.map +1 -1
- package/dist/theme/ctl-theme.js +12 -4
- package/dist/theme/floating.module.css +36 -0
- package/dist/theme/floating.module.css.d.ts +3 -0
- package/dist/theme/index.d.ts +2 -2
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +34 -5
- package/dist/theme/nav-link.module.css +7 -0
- package/dist/tokens/index.d.ts +7 -16
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +23 -28
- package/dist/tokens/palette.d.ts +5 -27
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +8 -30
- package/dist/tokens/size-classes.d.ts +40 -0
- package/dist/tokens/size-classes.d.ts.map +1 -0
- package/dist/tokens/size-classes.js +29 -0
- package/dist/tokens.css +8 -4
- package/llms.txt +3 -3
- package/package.json +17 -17
- package/src/agent-chat/composer.tsx +74 -47
- package/src/agent-chat/thread-workspace.tsx +8 -11
- package/src/agent-chat/use-keyboard-inset.ts +68 -0
- package/src/charts/cursor/touch-pin.ts +138 -0
- package/src/charts/hooks/use-discrete-cursor.ts +335 -0
- package/src/charts/hooks/use-hysteresis.ts +26 -0
- package/src/charts/hooks/useBandPlot.ts +90 -27
- package/src/charts/hooks/useChartCursor.ts +211 -26
- package/src/charts/index.ts +0 -8
- package/src/charts/kinds/BandStrip.tsx +11 -1
- package/src/charts/kinds/Donut.tsx +153 -62
- package/src/charts/kinds/DualPanel.tsx +19 -4
- package/src/charts/kinds/Heatmap.tsx +61 -22
- package/src/charts/kinds/MirroredBars.tsx +15 -3
- package/src/charts/kinds/donut-layout.ts +82 -0
- package/src/charts/layout/auto-margin.ts +24 -5
- package/src/charts/layout/end-labels.ts +135 -0
- package/src/charts/primitives/Axes.tsx +112 -21
- package/src/charts/primitives/CartesianChart.tsx +282 -68
- package/src/charts/primitives/ChartCard.tsx +141 -48
- package/src/charts/primitives/ChartFrame.tsx +243 -60
- package/src/charts/primitives/ChartLegend.tsx +257 -73
- package/src/charts/primitives/ChartTooltip.tsx +8 -3
- package/src/charts/primitives/Crosshair.tsx +2 -2
- package/src/charts/primitives/HoverOverlay.module.css +13 -0
- package/src/charts/primitives/HoverOverlay.module.css.d.ts +8 -0
- package/src/charts/primitives/HoverOverlay.tsx +15 -4
- package/src/charts/primitives/chart-card-context.ts +37 -0
- package/src/charts/primitives/chart-frame-layout.ts +214 -177
- package/src/charts/primitives/chart-layout.ts +318 -0
- package/src/charts/primitives/chart-tier.tsx +66 -21
- package/src/charts/series.ts +4 -1
- package/src/charts/utils/ticks.ts +95 -19
- package/src/common/errors.ts +25 -1
- package/src/common/hit-floor.ts +9 -0
- package/src/{shell → common}/isomorphic-layout-effect.ts +4 -3
- package/src/common/use-measured-widths.ts +88 -0
- package/src/common/use-media-query.ts +56 -0
- package/src/common/validate.ts +17 -4
- package/src/content/article-layout.module.css +45 -4
- package/src/content/article-layout.module.css.d.ts +3 -0
- package/src/content/article-layout.tsx +60 -5
- package/src/content/code-block.module.css +32 -2
- package/src/content/code-block.tsx +47 -3
- package/src/content/prose.module.css +13 -8
- package/src/controls/actions.module.css +14 -1
- package/src/controls/actions.module.css.d.ts +1 -0
- package/src/controls/actions.tsx +223 -75
- package/src/controls/controls.module.css +8 -31
- package/src/controls/enum-filter.tsx +2 -2
- package/src/controls/filter-context.tsx +11 -0
- package/src/controls/filter-pill.tsx +1 -0
- package/src/controls/index.ts +0 -1
- package/src/controls/multi-select-filter.tsx +2 -2
- package/src/controls/sync-button.module.css +1 -1
- package/src/dashboard/section.module.css +1 -26
- package/src/dashboard/section.tsx +1 -0
- package/src/dashboard/stat-card.module.css +6 -9
- package/src/dashboard/stat-card.tsx +6 -3
- package/src/dashboard/stat-group.module.css +16 -4
- package/src/dashboard/widget-grid.tsx +7 -6
- package/src/dashboard/widget-header.module.css +145 -133
- package/src/dashboard/widget-header.module.css.d.ts +2 -0
- package/src/dashboard/widget-header.tsx +45 -11
- package/src/data/data-table.module.css +94 -0
- package/src/data/data-table.module.css.d.ts +11 -0
- package/src/data/data-table.tsx +503 -164
- package/src/data/virtual-list.tsx +31 -0
- package/src/forms/form-layout.module.css +30 -5
- package/src/forms/form-layout.module.css.d.ts +1 -0
- package/src/forms/form-layout.tsx +12 -10
- package/src/guard/index.ts +183 -3
- package/src/guard/types.ts +3 -2
- package/src/index.ts +2 -0
- package/src/provider/index.tsx +53 -1
- package/src/shell/app-brand.module.css +2 -2
- package/src/shell/app-brand.tsx +1 -0
- package/src/shell/app-header.module.css +18 -8
- package/src/shell/app-main.module.css +54 -3
- package/src/shell/app-main.module.css.d.ts +1 -0
- package/src/shell/app-mobile-nav.module.css +16 -4
- package/src/shell/app-sidebar.module.css +4 -6
- package/src/shell/index.tsx +200 -129
- package/src/shell/mobile-nav-model.ts +1 -1
- package/src/shell/page-aside.tsx +70 -21
- package/src/shell/page-bar.module.css +10 -34
- package/src/shell/page-bar.tsx +10 -5
- package/src/shell/use-breakpoint.ts +9 -45
- package/src/shell/use-size-class.ts +31 -0
- package/src/state/persisted-or-local.ts +10 -4
- package/src/state/persisted.ts +76 -17
- package/src/styles.css +41 -0
- package/src/surfaces.ts +9 -1
- package/src/theme/controls.module.css +7 -0
- package/src/theme/ctl-theme.tsx +15 -3
- package/src/theme/floating.module.css +36 -0
- package/src/theme/floating.module.css.d.ts +3 -0
- package/src/theme/index.ts +46 -8
- package/src/theme/nav-link.module.css +7 -0
- package/src/tokens/index.ts +20 -30
- package/src/tokens/palette.ts +6 -28
- package/src/tokens/size-classes.ts +46 -0
- package/dist/shell/isomorphic-layout-effect.d.ts.map +0 -1
- /package/dist/{shell → common}/isomorphic-layout-effect.js +0 -0
package/src/styles.css
CHANGED
|
@@ -210,6 +210,47 @@
|
|
|
210
210
|
background-color: var(--basalt-scrollbar-thumb-hover);
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
+
/* Bottom-anchored toasts clear the shell's bottom bar + safe area (docs/CONTROLS-SPEC.md §2). */
|
|
214
|
+
.mantine-Notifications-root[data-position^='bottom'] {
|
|
215
|
+
bottom: calc(
|
|
216
|
+
var(--app-shell-footer-offset, 0px) + env(safe-area-inset-bottom, 0px) +
|
|
217
|
+
var(--mantine-spacing-md)
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* Pointer tier: an invisible hit-area expansion to at least `--vx-hit` (44px coarse, 24px fine),
|
|
222
|
+
no layout change. `::after`, not `::before` — Mantine's Button owns `::before` for its loading
|
|
223
|
+
overlay. `overflow: visible` is load-bearing: Button/ActionIcon roots clip to their box, which
|
|
224
|
+
would clip the overlay; the clip only serves their `[data-loading]` overlay, so it stays then.
|
|
225
|
+
Adopters: `data-basalt-hit` on the control, or `data-basalt-hit-scope` on a home whose
|
|
226
|
+
`button`/`a` descendants (consumer-supplied) adopt it.
|
|
227
|
+
DECISION: the axis is `pointer: coarse` (the PRIMARY input, via `--vx-hit`), never
|
|
228
|
+
`any-pointer` — a touch laptop's mouse-first session must not inflate every hit area.
|
|
229
|
+
OVERLAP CAP: a host with neighbours sets `--vx-hit-gap` (its real gap between controls); the
|
|
230
|
+
expansion is then limited to that gap in total (half per side), so adjacent hit boxes never
|
|
231
|
+
overlap and a later sibling never steals a tap. Unset = uncapped (an isolated control). The
|
|
232
|
+
`+ 2px` is the control's own 1px border (Mantine buttons carry one, transparent or not): `100%`
|
|
233
|
+
is the PADDING box, the gap is measured between border boxes.
|
|
234
|
+
`position: relative` is the ::after's containing block; an adopter that must be
|
|
235
|
+
absolute/sticky sets its own `position` in an unlayered rule, which outranks this layer. */
|
|
236
|
+
:is([data-basalt-hit], [data-basalt-hit-scope] :is(button, a)) {
|
|
237
|
+
position: relative;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
:is([data-basalt-hit], [data-basalt-hit-scope] :is(button, a)):not([data-loading]) {
|
|
241
|
+
overflow: visible;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
:is([data-basalt-hit], [data-basalt-hit-scope] :is(button, a))::after {
|
|
245
|
+
content: '';
|
|
246
|
+
position: absolute;
|
|
247
|
+
top: 50%;
|
|
248
|
+
left: 50%;
|
|
249
|
+
translate: -50% -50%;
|
|
250
|
+
width: min(max(100%, var(--vx-hit)), calc(100% + 2px + var(--vx-hit-gap, 100vmax)));
|
|
251
|
+
height: min(max(100%, var(--vx-hit)), calc(100% + 2px + var(--vx-hit-gap, 100vmax)));
|
|
252
|
+
}
|
|
253
|
+
|
|
213
254
|
/* Print: hide the BasaltShell chrome so only page content prints.
|
|
214
255
|
Scoped to AppShell's own class hooks, NOT to bare nav/header/footer. The element form matched
|
|
215
256
|
any landmark on the page — a consumer with a real <nav> and no BasaltShell silently printed a
|
package/src/surfaces.ts
CHANGED
|
@@ -66,6 +66,7 @@ export const PLUGIN_RULE_ID_LIST = [
|
|
|
66
66
|
'agent-no-raw-usechat',
|
|
67
67
|
'ai-sdk-major',
|
|
68
68
|
'no-import-meta-env',
|
|
69
|
+
'raw-breakpoint',
|
|
69
70
|
] as const
|
|
70
71
|
|
|
71
72
|
/** One registered oxlint plugin rule id — the literal union of {@link PLUGIN_RULE_ID_LIST}. */
|
|
@@ -262,12 +263,19 @@ export const SURFACES = {
|
|
|
262
263
|
'inline-display',
|
|
263
264
|
'raw-html-layout',
|
|
264
265
|
'hidden-inline-style',
|
|
266
|
+
// The CSS-text twin of `raw-breakpoint` (below) — a width @media, an undeclared @container
|
|
267
|
+
// name/literal, or a non-coarse pointer query, the shape a JSX-only AST rule cannot see.
|
|
268
|
+
'raw-media-query',
|
|
265
269
|
],
|
|
266
270
|
// The chrome half of the plugin: the shell, the card idiom, the page title and the scroll
|
|
267
271
|
// doctrine. `hand-rolled-shell` sits here rather than on a shell-shaped surface because
|
|
268
272
|
// `BasaltShell` IS the root barrel's promise (docs/CONTROLS-SPEC.md §6 wave 6).
|
|
269
273
|
pluginRules: [
|
|
270
274
|
'hand-rolled-shell',
|
|
275
|
+
// The size-class axis's one JSX escape hatch (docs/CONTROLS-SPEC.md §6) is reserved
|
|
276
|
+
// for BasaltShell/AppSidebar/MobileNav/AppBrand (`SHELL_HOME_NAMES`, configs/oxlint-plugin.js)
|
|
277
|
+
// — the same shell promise `hand-rolled-shell` polices, one axis over.
|
|
278
|
+
'raw-breakpoint',
|
|
271
279
|
'card-inset',
|
|
272
280
|
'in-body-page-title',
|
|
273
281
|
'raw-scroll-container',
|
|
@@ -291,7 +299,7 @@ export const SURFACES = {
|
|
|
291
299
|
"no second cssVariablesResolver — don't hand-build createTheme or re-add the resolver basalt already installs",
|
|
292
300
|
],
|
|
293
301
|
description:
|
|
294
|
-
'BasaltProvider, createBasaltTheme, BasaltShell + sidebar/mobile-nav/breadcrumbs, PageBar, PageAside, useBreakpoint, NavCountBadge, ThemeToggle, ThreadWorkspace + thread-chat components, WidgetHeader, dashboard composites (DeltaBadge, StatCard with threshold tone, EmptyState, QueryState/LoadingState/ErrorState, SettingsSection/SettingsRow/DangerZone), ConnectivityProvider/useConnectivity/ConnectivityIndicator (auto-mounted, C1: absorbed from the dropped ./connectivity), createBasaltQueryClient/unwrap/BasaltQueryDevtools (C1: absorbed from the dropped ./query), BasaltDevDock (C5: lazy Router/Query devtools + theme lab in one dock)',
|
|
302
|
+
'BasaltProvider, createBasaltTheme, BasaltShell + sidebar/mobile-nav/breadcrumbs, PageBar, PageAside, useSizeClass (useBreakpoint on notice), NavCountBadge, ThemeToggle, ThreadWorkspace + thread-chat components, WidgetHeader, dashboard composites (DeltaBadge, StatCard with threshold tone, EmptyState, QueryState/LoadingState/ErrorState, SettingsSection/SettingsRow/DangerZone), ConnectivityProvider/useConnectivity/ConnectivityIndicator (auto-mounted, C1: absorbed from the dropped ./connectivity), createBasaltQueryClient/unwrap/BasaltQueryDevtools (C1: absorbed from the dropped ./query), BasaltDevDock (C5: lazy Router/Query devtools + theme lab in one dock)',
|
|
295
303
|
optionalPeers: [
|
|
296
304
|
'react-markdown',
|
|
297
305
|
'remark-gfm',
|
|
@@ -61,6 +61,13 @@ html[data-mantine-color-scheme] .inputWrapper[data-variant='default']:not([data-
|
|
|
61
61
|
box-shadow: var(--field-depth);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
/* An input cannot host the `[data-basalt-hit]` ::after overlay, so its touch floor is real height. */
|
|
65
|
+
@media (pointer: coarse) {
|
|
66
|
+
.input {
|
|
67
|
+
min-height: var(--vx-hit);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
64
71
|
.input[data-variant='default'] {
|
|
65
72
|
--field-depth: var(--vx-shadow-raised);
|
|
66
73
|
}
|
package/src/theme/ctl-theme.tsx
CHANGED
|
@@ -48,6 +48,12 @@ import {
|
|
|
48
48
|
import type { MantineSize, MantineThemeOverride } from '@mantine/core'
|
|
49
49
|
import type { ReactNode } from 'react'
|
|
50
50
|
|
|
51
|
+
// Mantine's typed prop bags reject `data-*` literals; a spread carries the hit-area attribute in.
|
|
52
|
+
// Lives here (not `./index`, which imports THIS module) rather than the reverse, so `./index`'s own
|
|
53
|
+
// USE of it (alongside `CTL_THEME`/`CtlSlot`) closes no new import cycle. `./index` does not
|
|
54
|
+
// re-export it — it stays internal, reachable only through that import (see `./index`'s own doc).
|
|
55
|
+
export const HIT_ATTR: Record<string, true> = { 'data-basalt-hit': true }
|
|
56
|
+
|
|
51
57
|
/** The `MantineThemeOverride` a `<CtlSlot>` provides — see this module's doc for the merge/
|
|
52
58
|
* precedence contract. Exported so a consumer composing its own slot (a bespoke home not covered
|
|
53
59
|
* by `PageBar`/`Section`/the sidebar blocks) can reach the SAME tier without re-declaring it. */
|
|
@@ -63,7 +69,9 @@ export const CTL_THEME: MantineThemeOverride = {
|
|
|
63
69
|
NativeSelect: NativeSelect.extend({ defaultProps: { size: 'ctl' } }),
|
|
64
70
|
// The theme keys are the component names Mantine's own `useProps` looks up — `RadioGroup`, not
|
|
65
71
|
// `Radio.Group` (`RadioGroup.mjs`: `useProps("RadioGroup", …)`).
|
|
66
|
-
Radio: Radio.extend({
|
|
72
|
+
Radio: Radio.extend({
|
|
73
|
+
defaultProps: { size: 'ctl', attributes: { label: HIT_ATTR } },
|
|
74
|
+
}),
|
|
67
75
|
// Mantine types `RadioGroup.size` as a CLOSED `MantineSize`, while `Radio`, `Checkbox`,
|
|
68
76
|
// `Checkbox.Group`, `Switch` and `Switch.Group` all type theirs OPEN
|
|
69
77
|
// (`MantineSize | (string & {})`) — verified in the installed 9.3.0 `.d.ts` files, so this is an
|
|
@@ -71,9 +79,13 @@ export const CTL_THEME: MantineThemeOverride = {
|
|
|
71
79
|
// runtime `getSize` interpolates the string identically for all six. The cast restores what
|
|
72
80
|
// every sibling type already allows; it widens nothing.
|
|
73
81
|
RadioGroup: Radio.Group.extend({ defaultProps: { size: 'ctl' as MantineSize } }),
|
|
74
|
-
Checkbox: Checkbox.extend({
|
|
82
|
+
Checkbox: Checkbox.extend({
|
|
83
|
+
defaultProps: { size: 'ctl', attributes: { label: HIT_ATTR } },
|
|
84
|
+
}),
|
|
75
85
|
CheckboxGroup: Checkbox.Group.extend({ defaultProps: { size: 'ctl' } }),
|
|
76
|
-
Switch: Switch.extend({
|
|
86
|
+
Switch: Switch.extend({
|
|
87
|
+
defaultProps: { size: 'ctl', attributes: { track: HIT_ATTR } },
|
|
88
|
+
}),
|
|
77
89
|
SwitchGroup: Switch.Group.extend({ defaultProps: { size: 'ctl' } }),
|
|
78
90
|
},
|
|
79
91
|
}
|
|
@@ -33,3 +33,39 @@
|
|
|
33
33
|
.menuItem[data-menu-active]:not(:disabled):not([data-disabled]) {
|
|
34
34
|
background-color: color-mix(in srgb, var(--vx-ink) 6%, transparent);
|
|
35
35
|
}
|
|
36
|
+
|
|
37
|
+
/*
|
|
38
|
+
* Modal as a bottom sheet at the `compact` size class (docs/DESIGN-CORE.md § Layout, elevation,
|
|
39
|
+
* shapes): anchored
|
|
40
|
+
* to the bottom edge, full width, radius on the top corners only, safe-area bottom padding. The
|
|
41
|
+
* literal is `sizeClassMaxEm(SIZE_CLASSES.medium)`, pinned by `breakpoint-literals.test.ts`.
|
|
42
|
+
* `.sheetBody` is the Modal body; it reaches the trailing button row `overlays.confirm`
|
|
43
|
+
* renders (Mantine `Group`), stretching the buttons to full width at the `--vx-hit` floor.
|
|
44
|
+
*/
|
|
45
|
+
@media (max-width: 52.49375em) {
|
|
46
|
+
.sheetInner {
|
|
47
|
+
align-items: flex-end;
|
|
48
|
+
padding: 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.sheetContent {
|
|
52
|
+
flex: 0 0 100%;
|
|
53
|
+
max-width: 100%;
|
|
54
|
+
/* theme-allow css-raw-surface — top corners only, the shape of a sheet. `!important`: the
|
|
55
|
+
Modal content is a Paper whose theme `styles.root` writes `border-radius` inline. */
|
|
56
|
+
border-radius: var(--vx-radius-card) var(--vx-radius-card) 0 0 !important;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.sheetBody {
|
|
60
|
+
padding-bottom: calc(var(--mantine-spacing-md) + env(safe-area-inset-bottom, 0px));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.sheetBody > :global(.mantine-Group-root):last-child {
|
|
64
|
+
flex-wrap: nowrap;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.sheetBody > :global(.mantine-Group-root):last-child > button {
|
|
68
|
+
flex: 1;
|
|
69
|
+
min-height: var(--vx-hit);
|
|
70
|
+
}
|
|
71
|
+
}
|
package/src/theme/index.ts
CHANGED
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
Notification,
|
|
44
44
|
NumberInput,
|
|
45
45
|
Pagination,
|
|
46
|
+
PaginationControl,
|
|
46
47
|
Paper,
|
|
47
48
|
PasswordInput,
|
|
48
49
|
PillsInput,
|
|
@@ -81,12 +82,14 @@ import type { PaletteData, RadiusValues, SpaceValues } from '../tokens/palette'
|
|
|
81
82
|
import { DEFAULT_DERIVE_CONFIG, isDefaultDeriveConfig, resolveDeriveConfig } from '../tokens/derive'
|
|
82
83
|
import type { DeriveConfig } from '../tokens/derive'
|
|
83
84
|
import { pxRem, VX } from '../tokens'
|
|
85
|
+
import { SIZE_CLASS_BREAKPOINTS } from '../tokens/size-classes'
|
|
84
86
|
import type { BasaltFontsConfig } from '../tokens'
|
|
85
87
|
import controlsClasses from './controls.module.css'
|
|
86
88
|
import floatingClasses from './floating.module.css'
|
|
87
89
|
import navLinkClasses from './nav-link.module.css'
|
|
88
90
|
import segmentedControlClasses from './segmented-control.module.css'
|
|
89
91
|
import timelineClasses from './timeline.module.css'
|
|
92
|
+
import { HIT_ATTR } from './ctl-theme'
|
|
90
93
|
|
|
91
94
|
/**
|
|
92
95
|
* The `fonts` option's resolved shape — defined in `basalt-ui/tokens` (`BasaltFontsConfig`,
|
|
@@ -101,7 +104,9 @@ export type { BasaltFontsConfig } from '../tokens'
|
|
|
101
104
|
export { useBasaltSpacing } from './use-basalt-spacing'
|
|
102
105
|
|
|
103
106
|
// The `size="ctl"` slot theme (`docs/CONTROLS-SPEC.md` §5) — see `./ctl-theme`'s own doc for the
|
|
104
|
-
// merge/precedence contract.
|
|
107
|
+
// merge/precedence contract. `HIT_ATTR` is deliberately NOT in this list: it has no consumer of its
|
|
108
|
+
// own (consolidation doctrine — adopt or delete), and stays reachable only through the internal
|
|
109
|
+
// `./ctl-theme` import above, for this file's own use below.
|
|
105
110
|
export { CTL_THEME, CtlSlot, type CtlSlotProps } from './ctl-theme'
|
|
106
111
|
|
|
107
112
|
// `ThemeToggle` moved into this directory (C1 consolidation, merging the former `theme-toggle/`
|
|
@@ -182,6 +187,12 @@ function pinShades(
|
|
|
182
187
|
return out as unknown as MantineColorsTuple
|
|
183
188
|
}
|
|
184
189
|
|
|
190
|
+
// Mantine's typed prop bags reject `data-*` literals; a spread carries the hit-area attribute in.
|
|
191
|
+
// Defined in `./ctl-theme` (imported above) rather than here: `CTL_THEME` there is a top-level
|
|
192
|
+
// `const` built eagerly at module load, so a `HIT_ATTR` defined in THIS file and imported back from
|
|
193
|
+
// `ctl-theme.tsx` would close an import cycle this file's own line 107 doesn't already have — the
|
|
194
|
+
// existing direction (this file depends on `ctl-theme.tsx`, not the reverse) is what keeps it safe.
|
|
195
|
+
|
|
185
196
|
/** The shipped, config-independent palette data — `baseTheme` / `cssVariablesResolver` below are
|
|
186
197
|
* built from this once, at module load. */
|
|
187
198
|
const DEFAULT_PALETTE_DATA = buildPaletteData(DEFAULT_DERIVE_CONFIG)
|
|
@@ -422,6 +433,7 @@ function buildTheme(data: PaletteData, options: BuildThemeOptions = {}): Mantine
|
|
|
422
433
|
const { radius = DEFAULT_RADIUS_VALUES, spacing = DEFAULT_SPACE_VALUES } = options
|
|
423
434
|
const { ACCENT, FILL } = data
|
|
424
435
|
return createTheme({
|
|
436
|
+
breakpoints: SIZE_CLASS_BREAKPOINTS,
|
|
425
437
|
primaryColor: 'blue',
|
|
426
438
|
// ONE shade in both schemes — a filled control is a SURFACE, and the accent surface is the same
|
|
427
439
|
// hex either way (see `ACCENT.accentFill`). The scheme-inverting half of the accent lives in
|
|
@@ -670,6 +682,8 @@ function buildTheme(data: PaletteData, options: BuildThemeOptions = {}): Mantine
|
|
|
670
682
|
classNames: { wrapper: controlsClasses.inputWrapper, input: controlsClasses.input },
|
|
671
683
|
}),
|
|
672
684
|
TextInput: TextInput.extend({ defaultProps: { size: 'md' } }),
|
|
685
|
+
// The stepper chevrons sit ~15px apart, so neither can own a 44px overlay; the coarse
|
|
686
|
+
// `.input` min-height floor (theme/controls.module.css) is the touch floor.
|
|
673
687
|
NumberInput: NumberInput.extend({ defaultProps: { size: 'md' } }),
|
|
674
688
|
PasswordInput: PasswordInput.extend({ defaultProps: { size: 'md' } }),
|
|
675
689
|
Select: Select.extend({ defaultProps: { size: 'md' } }),
|
|
@@ -986,10 +1000,16 @@ function buildTheme(data: PaletteData, options: BuildThemeOptions = {}): Mantine
|
|
|
986
1000
|
// under it but shows every scrolled row straight through the title once one does (the mobile
|
|
987
1001
|
// filter sheet, the shell's "More" nav sheet). The header stays sticky; only its own
|
|
988
1002
|
// background moves back to opaque, matching the content box it sits above rather than the page
|
|
989
|
-
// behind it. Title = head font; close button = the ghost idiom (floating.module.css).
|
|
1003
|
+
// behind it. Title = head font; close button = the ghost idiom (floating.module.css). Below the
|
|
1004
|
+
// `medium` size class the Modal is a bottom sheet (floating.module.css `sheet*`).
|
|
990
1005
|
Modal: Modal.extend({
|
|
991
|
-
defaultProps: { radius: radius.floating },
|
|
992
|
-
classNames: {
|
|
1006
|
+
defaultProps: { radius: radius.floating, closeButtonProps: { ...HIT_ATTR } },
|
|
1007
|
+
classNames: {
|
|
1008
|
+
close: floatingClasses.closeButton,
|
|
1009
|
+
inner: floatingClasses.sheetInner,
|
|
1010
|
+
content: floatingClasses.sheetContent,
|
|
1011
|
+
body: floatingClasses.sheetBody,
|
|
1012
|
+
},
|
|
993
1013
|
styles: {
|
|
994
1014
|
content: {
|
|
995
1015
|
backgroundColor: 'var(--vx-surface-overlay)',
|
|
@@ -1067,7 +1087,15 @@ function buildTheme(data: PaletteData, options: BuildThemeOptions = {}): Mantine
|
|
|
1067
1087
|
// The v9 thumb indicator is a center dot painted in the TRACK color — over the remapped
|
|
1068
1088
|
// neutral track it read as an artifact, so it's disabled: clean track, plain thumb, accent
|
|
1069
1089
|
// fill when checked (`--switch-color` defaults to the primary filled shade = the accent).
|
|
1070
|
-
|
|
1090
|
+
// `track` is the Switch's <label>, so the invisible `--vx-hit` overlay (`data-basalt-hit`)
|
|
1091
|
+
// still toggles it. Checkbox/Radio boxes are bare <input>s under a plain <div> — an overlay
|
|
1092
|
+
// there would swallow clicks, so only their `label` (a real <label>) carries it.
|
|
1093
|
+
Switch: Switch.extend({
|
|
1094
|
+
defaultProps: {
|
|
1095
|
+
withThumbIndicator: false,
|
|
1096
|
+
attributes: { track: HIT_ATTR },
|
|
1097
|
+
},
|
|
1098
|
+
}),
|
|
1071
1099
|
// The tab-list bottom rule (and the inactive-tab hover underline) is a layout separator, not
|
|
1072
1100
|
// control chrome — it reads the divider token instead of the heavy gray-3/dark-4 line
|
|
1073
1101
|
// (controls.module.css; custom properties are stripped from `styles` objects). The active tab
|
|
@@ -1091,6 +1119,7 @@ function buildTheme(data: PaletteData, options: BuildThemeOptions = {}): Mantine
|
|
|
1091
1119
|
// `useStyles`), so re-pointing that var here wins on every render path. Without this, a dark-mode
|
|
1092
1120
|
// Checkbox/Radio paints a white checkmark on the light accent fill — invisible.
|
|
1093
1121
|
Checkbox: Checkbox.extend({
|
|
1122
|
+
defaultProps: { attributes: { label: HIT_ATTR } },
|
|
1094
1123
|
vars: (theme, props) => ({
|
|
1095
1124
|
root: {
|
|
1096
1125
|
// `iconColor` is an explicit call-site override, and `outline` paints the mark in the
|
|
@@ -1101,6 +1130,7 @@ function buildTheme(data: PaletteData, options: BuildThemeOptions = {}): Mantine
|
|
|
1101
1130
|
}),
|
|
1102
1131
|
}),
|
|
1103
1132
|
Radio: Radio.extend({
|
|
1133
|
+
defaultProps: { attributes: { label: HIT_ATTR } },
|
|
1104
1134
|
vars: (theme, props) => ({
|
|
1105
1135
|
root: {
|
|
1106
1136
|
'--radio-icon-color':
|
|
@@ -1113,6 +1143,12 @@ function buildTheme(data: PaletteData, options: BuildThemeOptions = {}): Mantine
|
|
|
1113
1143
|
root: { '--pagination-active-color': onColorFor(theme, props) },
|
|
1114
1144
|
}),
|
|
1115
1145
|
}),
|
|
1146
|
+
// `Pagination`'s own controls (prev/next/page buttons) resolve `PaginationControl`'s
|
|
1147
|
+
// defaultProps independently of the parent — `data-basalt-hit` here is what closes the
|
|
1148
|
+
// gap Switch/Checkbox/Radio already close at their own theme entries.
|
|
1149
|
+
PaginationControl: PaginationControl.extend({
|
|
1150
|
+
defaultProps: { ...HIT_ATTR },
|
|
1151
|
+
}),
|
|
1116
1152
|
Stepper: Stepper.extend({
|
|
1117
1153
|
vars: (theme, props) => ({ root: { '--stepper-icon-color': onColorFor(theme, props) } }),
|
|
1118
1154
|
}),
|
|
@@ -1556,7 +1592,7 @@ function hasFontsConfig(fonts: BasaltFontsConfig | undefined): fonts is BasaltFo
|
|
|
1556
1592
|
/**
|
|
1557
1593
|
* Build the Basalt theme, optionally merged with consumer overrides. Overrides win on conflict
|
|
1558
1594
|
* (Mantine `mergeThemeOverrides` is last-wins), so a consumer can retune any field without
|
|
1559
|
-
* forking the base
|
|
1595
|
+
* forking the base — except `breakpoints`, which is always derived from `SIZE_CLASSES`.
|
|
1560
1596
|
*
|
|
1561
1597
|
* Pass `{ derive }` to retune the palette identity instead of the shipped default, `{ fonts }` to
|
|
1562
1598
|
* retune the three-font system, `{ radius }` to retune the two radius anchors, and/or `{ density }`
|
|
@@ -1578,7 +1614,7 @@ function hasFontsConfig(fonts: BasaltFontsConfig | undefined): fonts is BasaltFo
|
|
|
1578
1614
|
* — it is only lost if a consumer's own `overrides.other` explicitly sets that same key.
|
|
1579
1615
|
*/
|
|
1580
1616
|
export function createBasaltTheme(
|
|
1581
|
-
overrides?: MantineThemeOverride,
|
|
1617
|
+
overrides?: Omit<MantineThemeOverride, 'breakpoints'>,
|
|
1582
1618
|
options?: CreateBasaltThemeOptions,
|
|
1583
1619
|
): MantineThemeOverride {
|
|
1584
1620
|
const resolvedConfig = resolveDeriveConfig(options?.derive)
|
|
@@ -1610,5 +1646,7 @@ export function createBasaltTheme(
|
|
|
1610
1646
|
if (hasFontsConfig(fontsConfig)) {
|
|
1611
1647
|
theme = mergeThemeOverrides(theme, { other: { basaltFonts: fontsConfig } })
|
|
1612
1648
|
}
|
|
1613
|
-
|
|
1649
|
+
if (!overrides) return theme
|
|
1650
|
+
// `theme.breakpoints` is derived from `SIZE_CLASSES` — a JS caller's override is dropped.
|
|
1651
|
+
return { ...mergeThemeOverrides(theme, overrides), breakpoints: SIZE_CLASS_BREAKPOINTS }
|
|
1614
1652
|
}
|
|
@@ -21,6 +21,13 @@
|
|
|
21
21
|
font-weight: 600;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/* Touch floor: rows are adjacent (1px gap), so an overlay would overlap — the row itself is 44. */
|
|
25
|
+
@media (pointer: coarse) {
|
|
26
|
+
.root {
|
|
27
|
+
min-height: var(--vx-hit);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
24
31
|
/* Active + hover — one step stronger than the resting active fill, so a pointer over the active row
|
|
25
32
|
* still reads as feedback rather than as a flat, unresponsive selection. */
|
|
26
33
|
@media (hover: hover) {
|
package/src/tokens/index.ts
CHANGED
|
@@ -16,11 +16,12 @@ import {
|
|
|
16
16
|
ROW_LINE_HEIGHT,
|
|
17
17
|
SHADOW,
|
|
18
18
|
SPACE,
|
|
19
|
-
SPACE_FIXED,
|
|
20
19
|
SPACE_SCALE,
|
|
21
20
|
SPACE_STEP,
|
|
22
21
|
} from './palette'
|
|
23
22
|
import type { PaletteData, RadiusValues, SpaceValues } from './palette'
|
|
23
|
+
import { SIZE_CLASSES, sizeClassMaxEm } from './size-classes'
|
|
24
|
+
import { HIT_COARSE, HIT_FINE } from '../common/hit-floor'
|
|
24
25
|
|
|
25
26
|
// The raw hue families + pair-picker — the building blocks a consumer's series module composes
|
|
26
27
|
// (`hrv: p(BP.blue)`). The doctrine sends every consumer here, so they are public surface, not
|
|
@@ -159,11 +160,9 @@ export const VX = {
|
|
|
159
160
|
// Fully-rounded pill affordance (scroll-to-bottom button, chips) — fixed, does not track the
|
|
160
161
|
// radius knob (see `RADIUS_STEP.pill`'s doc in `tokens/palette.ts`).
|
|
161
162
|
radiusPill: 'var(--vx-radius-pill)',
|
|
162
|
-
// The
|
|
163
|
-
//
|
|
164
|
-
|
|
165
|
-
// `--vx-space-touch-target` var this resolves to.
|
|
166
|
-
spaceTouchTarget: 'var(--vx-space-touch-target)',
|
|
163
|
+
// The pointer-tier hit floor: 44px under `(pointer: coarse)` (WCAG 2.5.5), 24px otherwise
|
|
164
|
+
// (2.5.8). Density-exempt; `[data-basalt-hit]::after` in `styles.css` reads it.
|
|
165
|
+
hit: 'var(--vx-hit)',
|
|
167
166
|
|
|
168
167
|
// Controls tier sizing (`docs/CONTROLS-SPEC.md` §5) — `var()` refs onto the density-tracking
|
|
169
168
|
// `--vx-space-*` declarations `spaceDecls` emits above (same ref pattern as `radiusCard`/
|
|
@@ -173,7 +172,6 @@ export const VX = {
|
|
|
173
172
|
controlHeightTag: 'var(--vx-space-control-height-tag)',
|
|
174
173
|
controlHeightWidget: 'var(--vx-space-control-height-widget)',
|
|
175
174
|
controlHeightCtl: 'var(--vx-space-control-height-ctl)',
|
|
176
|
-
touchControlHeight: 'var(--vx-space-touch-control-height)',
|
|
177
175
|
pageBarRowHeight: 'var(--vx-space-page-bar-row-height)',
|
|
178
176
|
sectionHeaderHeight: 'var(--vx-space-section-header-height)',
|
|
179
177
|
widgetHeaderHeight: 'var(--vx-space-widget-header-height)',
|
|
@@ -292,18 +290,10 @@ export const VX = {
|
|
|
292
290
|
* legend rolls up).
|
|
293
291
|
*/
|
|
294
292
|
minPlotHeight: 120,
|
|
295
|
-
/**
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
|
|
299
|
-
* rollup.
|
|
300
|
-
*
|
|
301
|
-
* Deliberately NOT a media query and NOT a `theme.breakpoints` value. A chart in a two-column
|
|
302
|
-
* grid cell on a 1440px desktop is as narrow as one on a phone, and only the element's own
|
|
303
|
-
* measured box knows that — the viewport does not. It also keeps the chart layer Mantine-free:
|
|
304
|
-
* `theme.breakpoints` lives on the coupled side of the boundary.
|
|
305
|
-
*/
|
|
306
|
-
phoneChartWidth: 480,
|
|
293
|
+
/** Every floating chart overlay portalled to `document.body` — a pinned tooltip, the legend's
|
|
294
|
+
* `All N` disclosure and its backdrop — shares one stacking value, so they always stack the same
|
|
295
|
+
* way relative to page chrome and to each other. */
|
|
296
|
+
zIndexFloating: 9999,
|
|
307
297
|
} as const
|
|
308
298
|
|
|
309
299
|
/**
|
|
@@ -317,13 +307,13 @@ export const VX = {
|
|
|
317
307
|
* `styles.floor.test.ts`).
|
|
318
308
|
*
|
|
319
309
|
* Only breakpoints that are genuinely a CSS-authored `@media` belong here. A component that can
|
|
320
|
-
* measure its own box uses the measurement instead (`
|
|
310
|
+
* measure its own box uses the measurement instead (`CONTAINER_CLASSES`), and Mantine chrome uses
|
|
321
311
|
* `theme.breakpoints`.
|
|
322
312
|
*/
|
|
323
313
|
export const BREAKPOINTS = {
|
|
324
314
|
/**
|
|
325
315
|
* `content/article-layout.module.css` — below this the TOC rail is dropped and the article
|
|
326
|
-
* collapses to a single centred column. Well above the `sm` (
|
|
316
|
+
* collapses to a single centred column. Well above the `sm` (52.5em/840px) shell breakpoint on
|
|
327
317
|
* purpose: the rail needs the prose measure PLUS its own width plus the column gap, which runs
|
|
328
318
|
* out long before the shell goes mobile.
|
|
329
319
|
*/
|
|
@@ -484,7 +474,7 @@ function frameworkPrimitives(side: Side, data: PaletteData, legacyAliases: boole
|
|
|
484
474
|
* the two lists can never drift apart — mirrors `frameworkDerived`'s own single-source reasoning for
|
|
485
475
|
* the radii.
|
|
486
476
|
*
|
|
487
|
-
* The
|
|
477
|
+
* The nine controls-tier anchors/one-offs (`controlHeightTag`/`Widget`/`Ctl`,
|
|
488
478
|
* `pageBarRowHeight`, `sectionHeaderHeight`, `widgetHeaderHeight`, `sidebarBlockRowHeight`,
|
|
489
479
|
* `controlGap`, `sheetRowHeight` — `docs/CONTROLS-SPEC.md` §5) are a DELIBERATE exception to the
|
|
490
480
|
* "has at least one real consumer today" rule below: `theme/ctl-theme.tsx`'s `CTL_THEME` and the
|
|
@@ -548,7 +538,6 @@ function spaceDecls(space: SpaceValues): string[] {
|
|
|
548
538
|
decl('space-control-height-tag', pxRem(space.anchors.controlHeightTag)),
|
|
549
539
|
decl('space-control-height-widget', pxRem(space.anchors.controlHeightWidget)),
|
|
550
540
|
decl('space-control-height-ctl', pxRem(space.anchors.controlHeightCtl)),
|
|
551
|
-
decl('space-touch-control-height', pxRem(space.anchors.touchControlHeight)),
|
|
552
541
|
decl('space-stack-xs', `${space.anchors.stackXs}px`),
|
|
553
542
|
decl('space-stack-sm', `${space.anchors.stackSm}px`),
|
|
554
543
|
decl('space-stack-md', `${space.anchors.stackMd}px`),
|
|
@@ -774,10 +763,9 @@ function frameworkDerived(data: PaletteData, only: 'core' | 'all', legacyAliases
|
|
|
774
763
|
// only in this static block, never in `buildRadiusCss`'s dynamic override (see
|
|
775
764
|
// `RADIUS_STEP.pill`'s doc).
|
|
776
765
|
decl('radius-pill', `${RADIUS_STEP.pill}px`),
|
|
777
|
-
// The
|
|
778
|
-
//
|
|
779
|
-
|
|
780
|
-
decl('space-touch-target', `${SPACE_FIXED.spaceTouchTarget}px`),
|
|
766
|
+
// The pointer tier's fine default — density-EXEMPT, so it lives only in this static block
|
|
767
|
+
// (same law as `radius-pill` above). `coarseHitBlock` re-points it to 44px.
|
|
768
|
+
decl('hit', `${HIT_FINE}px`),
|
|
781
769
|
...(only === 'core' ? space.filter(isCoreSpaceDecl) : space),
|
|
782
770
|
// Article-density Prose measure (docs/CONTENT-SPEC.md §5) — theme-independent, like the radii.
|
|
783
771
|
decl('prose-measure', '72ch'),
|
|
@@ -944,7 +932,9 @@ const indent = (block: string): string =>
|
|
|
944
932
|
* properties exist, so the number cannot be a `var()`. Held here as a constant rather than inlined
|
|
945
933
|
* so the emitter and the modules can be grepped as one set.
|
|
946
934
|
*/
|
|
947
|
-
const MOBILE_MEDIA =
|
|
935
|
+
const MOBILE_MEDIA = `@media (max-width: ${sizeClassMaxEm(SIZE_CLASSES.medium)})`
|
|
936
|
+
|
|
937
|
+
const coarseHitBlock = `@media (pointer: coarse) {\n :root {\n --vx-hit: ${HIT_COARSE}px;\n }\n}`
|
|
948
938
|
|
|
949
939
|
/**
|
|
950
940
|
* The MOBILE INSET LADDER, and this function is its ONE home — framework-wide, not per module.
|
|
@@ -1032,8 +1022,8 @@ export function buildPaletteCss(
|
|
|
1032
1022
|
// consumer never receives the base pair the block exists to override.
|
|
1033
1023
|
const rootBlock =
|
|
1034
1024
|
only === 'core'
|
|
1035
|
-
? `:root {\n${derived}\n}`
|
|
1036
|
-
: `:root {\n${derived}\n}\n${mobileLadderBlock(DEFAULT_SPACE_VALUES)}`
|
|
1025
|
+
? `:root {\n${derived}\n}\n${coarseHitBlock}`
|
|
1026
|
+
: `:root {\n${derived}\n}\n${mobileLadderBlock(DEFAULT_SPACE_VALUES)}\n${coarseHitBlock}`
|
|
1037
1027
|
|
|
1038
1028
|
// The legacy shape — kept verbatim, and only reachable when the caller asked for nothing.
|
|
1039
1029
|
if (opts.scheme === undefined && opts.defaultScheme === undefined && !opts.mediaFallback) {
|
package/src/tokens/palette.ts
CHANGED
|
@@ -321,11 +321,6 @@ const SPACE_ANCHORS_BASE = {
|
|
|
321
321
|
* and the whole band looked unresolved. 32 lands the entire chrome tier on ONE number, which is
|
|
322
322
|
* also the smallest square that still holds a 16px icon with a 8px surround. */
|
|
323
323
|
controlHeightCtl: 32,
|
|
324
|
-
/** The touch hit-area size (`::before`) every home control expands to below `sm` (C15,
|
|
325
|
-
* `docs/CONTROLS-SPEC.md` §5) — independent of the visible `controlHeightCtl` box, same shape as
|
|
326
|
-
* `mobileNavBarHeight`/`mobileNavRowHeight`'s Apple HIG 44pt / WCAG 2.5.5 floors elsewhere in this
|
|
327
|
-
* table. Floored at 30px in {@link deriveSpacing} (C15's "floor 30 at density −3"). */
|
|
328
|
-
touchControlHeight: 36,
|
|
329
324
|
} as const
|
|
330
325
|
|
|
331
326
|
/**
|
|
@@ -618,9 +613,8 @@ const SPACE_STEP_BASE = {
|
|
|
618
613
|
*
|
|
619
614
|
* 44, down from 48. The band holds ONE `ctl`-tier row (32 since this pass), so 48 left 8px of
|
|
620
615
|
* dead air above and below it — the "too much spacing at the top bar" the header reads as. 44
|
|
621
|
-
* keeps 6px of breathing room a side, still clears `
|
|
622
|
-
*
|
|
623
|
-
* this table already floors its touch targets at. */
|
|
616
|
+
* keeps 6px of breathing room a side, still clears `controlHeight` (42) for a `size="md"`
|
|
617
|
+
* control, and is the 44px touch floor (WCAG 2.5.5). */
|
|
624
618
|
appShellHeaderHeight: 44,
|
|
625
619
|
/** AppShell navbar width, expanded — ONE entry for both the `base` (mobile, where the navbar is
|
|
626
620
|
* permanently collapsed and unused — see `MobileNav`) and the expanded `sm` value: the same
|
|
@@ -696,12 +690,9 @@ const SPACE_STEP_BASE = {
|
|
|
696
690
|
mobileNavTabInsetY: 2,
|
|
697
691
|
/** Active-pill inset around the slot icon, horizontal. */
|
|
698
692
|
mobileNavTabInsetX: 12,
|
|
699
|
-
/** Menu/sheet row height.
|
|
700
|
-
*
|
|
701
|
-
|
|
702
|
-
* `sheetRowHeight` still hold to) — the shortest box that still reads as ONE row vocabulary with
|
|
703
|
-
* the sidebar instead of a bespoke touch size. */
|
|
704
|
-
mobileNavRowHeight: 40,
|
|
693
|
+
/** Menu/sheet row height — the WCAG 2.5.5 44px target, floored at 44 in `deriveSpacing` (the
|
|
694
|
+
* pointer tier's `--vx-hit` is the same number under `(pointer: coarse)`). */
|
|
695
|
+
mobileNavRowHeight: 44,
|
|
705
696
|
/** More/group `<Menu width={…}>`. JS-consumed, no var — same rationale as
|
|
706
697
|
* `sidebarSettingsMenuWidth` directly above. */
|
|
707
698
|
mobileNavMenuWidth: 232,
|
|
@@ -853,18 +844,6 @@ export const SPACE_FIXED = {
|
|
|
853
844
|
* `[-3, 3]` range can produce) for no compensating benefit, since the two radii it sits between
|
|
854
845
|
* never move independently of each other. */
|
|
855
846
|
segmentedTrackInset: 2,
|
|
856
|
-
/**
|
|
857
|
-
* The WCAG 2.5.5/2.5.8 touch-target floor (44px) for a hit area that has no `ctl`-tier home to
|
|
858
|
-
* size it — a raw CSS-module rule bumping a control's hit area under `@media (pointer: coarse)`
|
|
859
|
-
* (C5 consolidation: argo carried three such rules with a hand-picked 44/46px literal apiece).
|
|
860
|
-
* Density-exempt for the same reason `touchControlHeight`'s FLOOR is: a target this small must
|
|
861
|
-
* never shrink further, whatever the density knob says elsewhere. Unlike the rest of this
|
|
862
|
-
* object, this ONE key IS emitted as a `--vx-*` var (`tokens/index.ts`'s `space-touch-target`) —
|
|
863
|
-
* the others stay JS-only because nothing outside the framework's own components reads them, but
|
|
864
|
-
* this floor's whole purpose is being read by a CONSUMER's own `.module.css`, which cannot
|
|
865
|
-
* import a JS ref at all.
|
|
866
|
-
*/
|
|
867
|
-
spaceTouchTarget: 44,
|
|
868
847
|
} as const
|
|
869
848
|
|
|
870
849
|
/**
|
|
@@ -1095,7 +1074,6 @@ export function deriveSpacing(level: number): SpaceValues {
|
|
|
1095
1074
|
anchors.controlHeightTag = Math.max(18, anchors.controlHeightTag)
|
|
1096
1075
|
anchors.controlHeightWidget = Math.max(22, anchors.controlHeightWidget)
|
|
1097
1076
|
anchors.controlHeightCtl = Math.max(28, anchors.controlHeightCtl)
|
|
1098
|
-
anchors.touchControlHeight = Math.max(30, anchors.touchControlHeight)
|
|
1099
1077
|
|
|
1100
1078
|
const mappedStep = mapSpaceGroup(SPACE_STEP_BASE, level, multiplier)
|
|
1101
1079
|
// WCAG 2.5.8 minimum interactive-target size — see this function's doc for why the trigger's
|
|
@@ -1112,7 +1090,7 @@ export function deriveSpacing(level: number): SpaceValues {
|
|
|
1112
1090
|
// box) would render at 47px if the floor stopped at 48. +1 buys back exactly the pixel the seam
|
|
1113
1091
|
// claims, so the tappable bar itself still measures >= 48.
|
|
1114
1092
|
mappedStep.mobileNavBarHeight = Math.max(49, mappedStep.mobileNavBarHeight)
|
|
1115
|
-
mappedStep.mobileNavRowHeight = Math.max(
|
|
1093
|
+
mappedStep.mobileNavRowHeight = Math.max(44, mappedStep.mobileNavRowHeight)
|
|
1116
1094
|
// Same floor family, same reason — `sheetRowHeight`'s own doc names it "same floor family as
|
|
1117
1095
|
// `mobileNavRowHeight`", but nothing enforced that below level 0 until now.
|
|
1118
1096
|
mappedStep.sheetRowHeight = Math.max(44, mappedStep.sheetRowHeight)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two responsive tables (`docs/DESIGN-CORE.md` § Layout, elevation, shapes) — pure data, Mantine-free. Each
|
|
3
|
+
* value is the class's minimum width in px.
|
|
4
|
+
*
|
|
5
|
+
* A `@media`/`@container` condition cannot read a custom property, so CSS writes the literal and
|
|
6
|
+
* `breakpoint-literals.test.ts` pins every literal to these tables.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** Viewport classes — shell chrome only (bottom bar / rail / sidebar, aside docking). */
|
|
10
|
+
export const SIZE_CLASSES = { compact: 0, medium: 840, expanded: 1200 } as const
|
|
11
|
+
|
|
12
|
+
/** A component's own inline size — card chrome, chart tier, forms. No CSS reads it yet. */
|
|
13
|
+
export const CONTAINER_CLASSES = { micro: 0, compact: 240, regular: 480, wide: 800 } as const
|
|
14
|
+
|
|
15
|
+
export type ContainerClass = keyof typeof CONTAINER_CLASSES
|
|
16
|
+
|
|
17
|
+
/** The class a MEASURED inline size falls in (`px` is a min-width boundary, so 240 is `compact`). */
|
|
18
|
+
export function resolveContainerClass(px: number): ContainerClass {
|
|
19
|
+
if (px >= CONTAINER_CLASSES.wide) return 'wide'
|
|
20
|
+
if (px >= CONTAINER_CLASSES.regular) return 'regular'
|
|
21
|
+
if (px >= CONTAINER_CLASSES.compact) return 'compact'
|
|
22
|
+
return 'micro'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* `theme.breakpoints`, derived — a consumer cannot retune it. Mantine needs all five keys:
|
|
27
|
+
* `base` (no key) is compact, `xs`/`sm` open the medium class, `md`/`lg`/`xl` the expanded one, so
|
|
28
|
+
* every `visibleFrom`/`hiddenFrom` in the package stays "desktop chrome" from `sm` up.
|
|
29
|
+
*/
|
|
30
|
+
export const SIZE_CLASS_BREAKPOINTS = {
|
|
31
|
+
xs: `${SIZE_CLASSES.medium / 16}em`,
|
|
32
|
+
sm: `${SIZE_CLASSES.medium / 16}em`,
|
|
33
|
+
md: `${SIZE_CLASSES.expanded / 16}em`,
|
|
34
|
+
lg: `${SIZE_CLASSES.expanded / 16}em`,
|
|
35
|
+
xl: `${SIZE_CLASSES.expanded / 16}em`,
|
|
36
|
+
} as const
|
|
37
|
+
|
|
38
|
+
/** A px width as the `em` length media queries use (16px root). */
|
|
39
|
+
export function toEm(px: number): string {
|
|
40
|
+
return `${Number((px / 16).toFixed(5))}em`
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** The `max-width` complement of a class boundary — 0.1px under it, Mantine's own convention. */
|
|
44
|
+
export function sizeClassMaxEm(minPx: number): string {
|
|
45
|
+
return toEm(minPx - 0.1)
|
|
46
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"isomorphic-layout-effect.d.ts","sourceRoot":"","sources":["../../src/shell/isomorphic-layout-effect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,SAAS,EAAmB,MAAM,OAAO,CAAA;AAElD,eAAO,MAAM,yBAAyB,kBAA8D,CAAA"}
|
|
File without changes
|