basalt-ui 1.5.0 → 1.7.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 +25 -0
- package/agent/rules/basalt-mantine.md +80 -40
- package/agent/rules/basalt-tokens.md +3 -1
- package/agent/skills/basalt-design/SKILL.md +21 -7
- package/agent/templates/CLAUDE-block.md.tpl +3 -2
- package/agent/templates/DESIGN.md.tpl +2 -1
- package/configs/oxlint-plugin.js +75 -1
- package/configs/oxlint.json +1 -0
- package/dist/agent/stick-to-bottom.d.ts.map +1 -1
- package/dist/agent/stick-to-bottom.js +2 -1
- package/dist/agent-chat/composer.d.ts.map +1 -1
- package/dist/agent-chat/composer.js +5 -4
- package/dist/cli/index.d.ts +9 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/dashboard/index.d.ts +1 -1
- package/dist/dashboard/index.d.ts.map +1 -1
- package/dist/dashboard/stat-card.d.ts +6 -1
- package/dist/dashboard/stat-card.d.ts.map +1 -1
- package/dist/dashboard/stat-card.js +37 -4
- package/dist/guard/index.d.ts +7 -1
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +16 -1
- package/dist/guard/types.d.ts +18 -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/shell/app-sidebar.module.css +4 -6
- package/dist/shell/sidebar-search.module.css +4 -2
- package/dist/surfaces.d.ts +2 -2
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +8 -2
- package/dist/theme/controls.module.css +167 -26
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +36 -19
- package/dist/theme/nav-link.module.css +10 -10
- package/dist/tokens/index.d.ts +1 -0
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +4 -0
- package/dist/tokens/palette.d.ts +31 -0
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +31 -0
- package/dist/tokens.css +2 -0
- package/dist/vite.d.ts +6 -0
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +21 -1
- package/llms.txt +3 -3
- package/package.json +1 -1
- package/src/agent/stick-to-bottom.tsx +2 -1
- package/src/agent-chat/composer.tsx +5 -4
- package/src/cli/index.ts +11 -0
- package/src/dashboard/index.ts +1 -1
- package/src/dashboard/stat-card.tsx +61 -2
- package/src/guard/check-source.test.ts +63 -1
- package/src/guard/index.ts +39 -3
- package/src/guard/types.ts +18 -2
- package/src/index.ts +1 -0
- package/src/shell/app-sidebar.module.css +4 -6
- package/src/shell/sidebar-search.module.css +4 -2
- package/src/surfaces.ts +8 -2
- package/src/theme/controls.module.css +167 -26
- package/src/theme/index.ts +50 -19
- package/src/theme/nav-link.module.css +10 -10
- package/src/theme/shadow-surfaces.test.ts +72 -33
- package/src/tokens/index.ts +4 -0
- package/src/tokens/palette.ts +31 -0
- package/src/vite.ts +62 -1
package/dist/theme/index.js
CHANGED
|
@@ -147,6 +147,10 @@ const basaltVariantColorResolver = (input) => {
|
|
|
147
147
|
const autoContrast = input.autoContrast ?? input.theme.autoContrast;
|
|
148
148
|
return autoContrast ? { ...resolved, color: on } : resolved;
|
|
149
149
|
}
|
|
150
|
+
if (input.variant === "subtle") {
|
|
151
|
+
const isDefaultColor = input.color === void 0 || input.color === input.theme.primaryColor;
|
|
152
|
+
return isDefaultColor ? { ...resolved, hover: `color-mix(in srgb, var(--vx-ink) 6%, transparent)` } : resolved;
|
|
153
|
+
}
|
|
150
154
|
if (input.variant !== "light") return resolved;
|
|
151
155
|
const ink = `var(--mantine-color-${colorName}-light-color)`;
|
|
152
156
|
return {
|
|
@@ -295,17 +299,26 @@ function buildTheme(data, options = {}) {
|
|
|
295
299
|
}
|
|
296
300
|
}),
|
|
297
301
|
// "Ink earns its color" — a nav selection is UI state, not the identity accent on the LABEL.
|
|
298
|
-
// The active item is
|
|
299
|
-
//
|
|
300
|
-
//
|
|
301
|
-
//
|
|
302
|
-
//
|
|
302
|
+
// The active item is a SELECTED ROW, not a raised control: the same ink-tint idiom as the
|
|
303
|
+
// hover state (one step stronger, ink-9%), no panel fill and no `shadow-card` — a raised,
|
|
304
|
+
// panel-filled row read as a button sitting in the nav rather than as the current location.
|
|
305
|
+
// Selection is carried by the tint + ink text + weight 600 + the accent icon instead. Forced
|
|
306
|
+
// here at the THEME level via NavLink's `--nl-*` vars so it holds for every render path —
|
|
307
|
+
// including a consumer's router `<Link>` via `renderNavLink`, which never sees the shell CSS
|
|
308
|
+
// module; the active ICON is accent-colored via `nav-link.module.css` (targets the
|
|
309
|
+
// `[data-position='left']` leftSection, which the flat `vars`/`styles` API can't express
|
|
310
|
+
// conditionally on `[data-active]`).
|
|
311
|
+
//
|
|
312
|
+
// `--nl-hover` (ink-13%) only ever reaches an ACTIVE row in practice — the inactive hover is
|
|
313
|
+
// set at ink-6% by higher-specificity `:not([data-active])` rules in `nav-link.module.css` /
|
|
314
|
+
// `app-sidebar.module.css` — so it exists to keep the active row responding to the pointer
|
|
315
|
+
// now that its rest state sits in the same tint family as an inactive hover.
|
|
303
316
|
NavLink: NavLink.extend({
|
|
304
317
|
vars: () => ({
|
|
305
318
|
root: {
|
|
306
|
-
"--nl-bg": "var(--vx-
|
|
319
|
+
"--nl-bg": "color-mix(in srgb, var(--vx-ink) 9%, transparent)",
|
|
307
320
|
"--nl-color": "var(--vx-ink)",
|
|
308
|
-
"--nl-hover": "color-mix(in srgb, var(--vx-ink)
|
|
321
|
+
"--nl-hover": "color-mix(in srgb, var(--vx-ink) 13%, transparent)"
|
|
309
322
|
},
|
|
310
323
|
children: {}
|
|
311
324
|
}),
|
|
@@ -339,7 +352,7 @@ function buildTheme(data, options = {}) {
|
|
|
339
352
|
label: { fontSize: VX.text.md, lineHeight: "var(--vx-space-row-line-height)" }
|
|
340
353
|
}
|
|
341
354
|
}),
|
|
342
|
-
// Field idiom (docs/DESIGN-SPEC.md §5): field surface + `shadow-
|
|
355
|
+
// Field idiom (docs/DESIGN-SPEC.md §5): field surface + `shadow-raised` depth + faint placeholder,
|
|
343
356
|
// accent border + subtle accent ring on focus — see controls.module.css. The two slots carry
|
|
344
357
|
// different halves of it: the `wrapper` is where Mantine declares the `--input-*` vars, the
|
|
345
358
|
// `input` is the box that actually paints the surface (bg + radius) and therefore the only
|
|
@@ -391,16 +404,19 @@ function buildTheme(data, options = {}) {
|
|
|
391
404
|
PillsInput: PillsInput.extend({
|
|
392
405
|
classNames: { wrapper: controlsClasses.inputWrapper, input: controlsClasses.input }
|
|
393
406
|
}),
|
|
394
|
-
// Button / ActionIcon
|
|
395
|
-
//
|
|
396
|
-
//
|
|
407
|
+
// Button / ActionIcon: depth for EVERY variant that owns a box (docs/DESIGN-SPEC.md §5's
|
|
408
|
+
// Button idiom; doctrine inversion #1) — `default`/`filled`/`light` on the shared
|
|
409
|
+
// `shadow-raised`, `outline` on ring-free `shadow-ctrl`, `subtle` flat at rest. Emphasis stays
|
|
410
|
+
// in fill weight,
|
|
411
|
+
// never in z-height. Background for `default` already resolves through the shared
|
|
397
412
|
// `--mantine-color-default` var (→ `--vx-surface-panel`, see cssVariablesResolver below) and
|
|
398
|
-
//
|
|
413
|
+
// its border through `--mantine-color-default-border` (→ transparent, same resolver) — neither
|
|
399
414
|
// component had a `.extend()` block at all before this, which is why they never picked up
|
|
400
|
-
// either.
|
|
401
|
-
//
|
|
402
|
-
//
|
|
403
|
-
//
|
|
415
|
+
// either. Everything else — the per-variant depth and the disabled/reduced-motion grounding —
|
|
416
|
+
// lives in controls.module.css, where the variant scoping
|
|
417
|
+
// is an attribute selector: Mantine declares no `box-shadow` on either component at any
|
|
418
|
+
// specificity, so none of it needs a specificity fight. Nothing about depth is expressible in
|
|
419
|
+
// the flat `styles` objects here, which is the whole reason that file exists.
|
|
404
420
|
//
|
|
405
421
|
// Density coupling: mirrors Input's `--input-height` override above — Mantine's OWN
|
|
406
422
|
// `--button-height-md`/`--ai-size` (via `--ai-size-md`) are global, non-retunable statics
|
|
@@ -439,7 +455,8 @@ function buildTheme(data, options = {}) {
|
|
|
439
455
|
// real attribute selectors ((0,2,0)) — Mantine's own rule wraps the same attribute in `:where()`
|
|
440
456
|
// (0 specificity, landing at (0,1,0) total), so no `html[…]` prefix fight is needed here either.
|
|
441
457
|
// Neither component sets a background of its own, so panel bg is added alongside the shadow —
|
|
442
|
-
// the same panel-bg +
|
|
458
|
+
// the same panel-bg + depth + no-border triad as Card/Paper — on `shadow-raised`, the shared
|
|
459
|
+
// CONTROL depth, not Card/Paper's `shadow-card`: a selection card is something you click.
|
|
443
460
|
CheckboxCard: CheckboxCard.extend({
|
|
444
461
|
classNames: { card: controlsClasses.checkboxCardRoot }
|
|
445
462
|
}),
|
|
@@ -780,8 +797,8 @@ function buildCssVariablesResolver(data) {
|
|
|
780
797
|
"--mantine-color-default": `var(--vx-surface-panel, ${SURFACE.panel.light})`,
|
|
781
798
|
// cards / default controls
|
|
782
799
|
"--mantine-color-default-hover": `var(--vx-surface-elevated, ${SURFACE.elevated.light})`,
|
|
783
|
-
// THE central lever for doctrine inversion #1 on CONTROLS (docs/DESIGN-SPEC.md §8): depth is
|
|
784
|
-
//
|
|
800
|
+
// THE central lever for doctrine inversion #1 on CONTROLS (docs/DESIGN-SPEC.md §8): depth is a
|
|
801
|
+
// shadow, never a hairline, for every `variant="default"` control — not just Card/Paper.
|
|
785
802
|
// `defaultVariantColorsResolver`'s `variant === 'default'` branch reads this ONE var for every
|
|
786
803
|
// component that uses it (Button, ActionIcon, Chip, CheckboxCard, RadioCard, …), so pointing it
|
|
787
804
|
// at `transparent` kills the stock 1px hairline everywhere at once. `transparent`, not `border:
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Active nav row = heavier weight
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* selectors live here. React Router's NavLink marks the active item via `aria-current` rather than
|
|
7
|
-
* the `active` prop's `data-active`, so both states are matched.
|
|
2
|
+
* Active nav row = heavier weight over the ink tint `--nl-bg` carries (set in theme/index.ts,
|
|
3
|
+
* applied unconditionally by Mantine's own `[data-active]` CSS). NO depth: an active row is a
|
|
4
|
+
* SELECTED ROW, not a raised control — it deliberately reads as a stronger version of the hover
|
|
5
|
+
* tint rather than as a button, so the row never competes with the real filled controls around it.
|
|
8
6
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* Applied at the THEME level via `classNames` so it reaches EVERY NavLink render path — including a
|
|
8
|
+
* consumer's router `<Link>` mounted through `renderNavLink`, which a shell-scoped CSS module never
|
|
9
|
+
* sees. Mantine's `styles` prop only takes flat inline properties (a nested `&[data-active]` there
|
|
10
|
+
* throws a React warning), so the state selectors live here. React Router's NavLink marks the
|
|
11
|
+
* active item via `aria-current` rather than the `active` prop's `data-active`, so both states are
|
|
12
|
+
* matched.
|
|
12
13
|
*/
|
|
13
14
|
.root[data-active],
|
|
14
15
|
.root[aria-current='page'] {
|
|
15
16
|
font-weight: 600;
|
|
16
|
-
box-shadow: var(--vx-shadow-card);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
/*
|
package/dist/tokens/index.d.ts
CHANGED
|
@@ -86,6 +86,7 @@ export declare const VX: {
|
|
|
86
86
|
readonly divider: "var(--vx-divider)";
|
|
87
87
|
readonly shadowCard: "var(--vx-shadow-card)";
|
|
88
88
|
readonly shadowCtrl: "var(--vx-shadow-ctrl)";
|
|
89
|
+
readonly shadowRaised: "var(--vx-shadow-raised)";
|
|
89
90
|
readonly shadowOverlay: "var(--vx-shadow-overlay)";
|
|
90
91
|
readonly surface: {
|
|
91
92
|
readonly bg: "var(--vx-surface-bg)";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tokens/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAYH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAMvE,OAAO,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,WAAW,CAAA;AAOjC,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AACnF,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAM5D,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxC,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAO7C,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AACzC,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAE5C,wFAAwF;AACxF,MAAM,MAAM,SAAS,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEvD,8FAA8F;AAC9F,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;AAEjD;;;;;;GAMG;AACH,eAAO,MAAM,KAAK,GAAI,OAAO,MAAM,EAAE,GAAG,MAAM,KAAG,MACoB,CAAA;AAsCrE,eAAO,MAAM,EAAE
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tokens/index.ts"],"names":[],"mappings":"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"}
|
package/dist/tokens/index.js
CHANGED
|
@@ -119,6 +119,9 @@ const VX = {
|
|
|
119
119
|
// Depth shadows — a whisper shadow + a 1px ring baked into the value (never a `border` prop).
|
|
120
120
|
shadowCard: "var(--vx-shadow-card)",
|
|
121
121
|
shadowCtrl: "var(--vx-shadow-ctrl)",
|
|
122
|
+
// Raised interactive controls — drop + a 1px INSET edge, the one depth every raised Button/
|
|
123
|
+
// ActionIcon variant shares (works over a fill, a tint or a panel alike; see SHADOW.raised).
|
|
124
|
+
shadowRaised: "var(--vx-shadow-raised)",
|
|
122
125
|
// Floating-layer elevation (menus, popovers, tooltips, modals, drawers) — a real drop shadow.
|
|
123
126
|
shadowOverlay: "var(--vx-shadow-overlay)",
|
|
124
127
|
// App surfaces (cards, borders) — shared with the Mantine chrome
|
|
@@ -218,6 +221,7 @@ function frameworkPrimitives(side, data, legacyAliases) {
|
|
|
218
221
|
decl("divider", su.divider[side]),
|
|
219
222
|
decl("shadow-card", SHADOW.card[side]),
|
|
220
223
|
decl("shadow-ctrl", SHADOW.ctrl[side]),
|
|
224
|
+
decl("shadow-raised", SHADOW.raised[side]),
|
|
221
225
|
decl("shadow-overlay", SHADOW.overlay[side]),
|
|
222
226
|
decl("surface-bg", su.bg[side]),
|
|
223
227
|
decl("surface-panel", su.panel[side]),
|
package/dist/tokens/palette.d.ts
CHANGED
|
@@ -84,6 +84,37 @@ export declare const SHADOW: {
|
|
|
84
84
|
readonly light: "0 1px 2px rgba(28,25,23,0.12)";
|
|
85
85
|
readonly dark: "0 1px 2px rgba(0,0,0,0.35)";
|
|
86
86
|
};
|
|
87
|
+
/**
|
|
88
|
+
* Interactive-control depth — the ONE value every raised Button/ActionIcon/Input variant shares
|
|
89
|
+
* (`docs/DESIGN-SPEC.md` §5's Button idiom). Exists rather than reusing `card` because `card`'s
|
|
90
|
+
* ring is OUTSET, and an outset ring paints in the color of whatever it is drawn over: fine
|
|
91
|
+
* between neutral panels, but on light scheme it resolves to `--vx-surface-hairline` and draws a
|
|
92
|
+
* pale grey outline around a saturated `filled` button, which reads as a rendering defect.
|
|
93
|
+
*
|
|
94
|
+
* THE TWO SCHEMES ARE SHAPED DIFFERENTLY, and the asymmetry is the whole design — do NOT "tidy"
|
|
95
|
+
* them into one shape. Both were arrived at by elimination, on screen:
|
|
96
|
+
*
|
|
97
|
+
* - DARK = drop + a full inset rim. Its edge is a LIGHTENING, so it cannot collide with the
|
|
98
|
+
* dark drop below the box; a uniform lightening on a dark surface reads as rim light wrapping
|
|
99
|
+
* a raised object, which is exactly right.
|
|
100
|
+
*
|
|
101
|
+
* - LIGHT = drop ONLY, no edge at all. A light-mode edge would have to be a DARKENING (there is
|
|
102
|
+
* nothing lighter than a near-white control to lighten with), and every position for one
|
|
103
|
+
* fails: a uniform dark ring does not read as depth, it reads as a BORDER — it made `default`
|
|
104
|
+
* and `light` look like outlined buttons and every form field look boxed. Moving it to the
|
|
105
|
+
* bottom edge alone then put a dark inner line directly above the dark drop, i.e. a visible
|
|
106
|
+
* DOUBLE BOTTOM BORDER. There is no third position, because a light-mode raised object's only
|
|
107
|
+
* honest cue is the shadow it casts. So light casts one and draws nothing.
|
|
108
|
+
*
|
|
109
|
+
* Consequence to know, on light only: a `default` control nested in a Card shares
|
|
110
|
+
* `--vx-surface-panel` with it and has no boundary but the drop. That is accepted — a wrong-
|
|
111
|
+
* looking border on every control in the app is worse than a soft edge in one nesting case. If it
|
|
112
|
+
* ever needs more, raise the DROP. Do not reintroduce an edge; both shapes have been tried.
|
|
113
|
+
*/
|
|
114
|
+
readonly raised: {
|
|
115
|
+
readonly light: "0 1px 2px rgba(28,25,23,0.10)";
|
|
116
|
+
readonly dark: "0 1px 2px rgba(0,0,0,0.35), inset 0 0 0 1px color-mix(in srgb, #ffffff 8%, transparent)";
|
|
117
|
+
};
|
|
87
118
|
readonly overlay: {
|
|
88
119
|
readonly light: "0 8px 24px rgba(28,25,23,0.10)";
|
|
89
120
|
readonly dark: "0 8px 24px rgba(0,0,0,0.5)";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"palette.d.ts","sourceRoot":"","sources":["../../src/tokens/palette.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAC5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAExC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;CAgCL,CAAA;AAEV;;;;GAIG;AACH,eAAO,MAAM,CAAC,GAAI,KAAK,SAAS,MAAM,EAAE,EAAE,cAAS,EAAE,aAAQ,KAAG,SAG9D,CAAA;AAEF;;;;;GAKG;AACH,eAAO,MAAM,MAAM
|
|
1
|
+
{"version":3,"file":"palette.d.ts","sourceRoot":"","sources":["../../src/tokens/palette.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAC5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAExC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;CAgCL,CAAA;AAEV;;;;GAIG;AACH,eAAO,MAAM,CAAC,GAAI,KAAK,SAAS,MAAM,EAAE,EAAE,cAAS,EAAE,aAAQ,KAAG,SAG9D,CAAA;AAEF;;;;;GAKG;AACH,eAAO,MAAM,MAAM;;;;;;;;;IASjB;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;;;;;;;;;CAaK,CAAA;AAEV;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAOD;;;8DAG8D;AAC9D,eAAO,MAAM,kBAAkB;;;CAA+B,CAAA;AAE9D;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAmBxD;AAMD;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM;IACjB,oFAAoF;;IAEpF;2BACuB;;CAEf,CAAA;AAEV;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW;IACtB;gCAC4B;;IAE5B,0FAA0F;;IAE1F,4FAA4F;;IAE5F,4EAA4E;;IAE5E,4EAA4E;;IAE5E,4EAA4E;;IAE5E;uFACmF;;CAE3E,CAAA;AAWV;;;;;;;;;;;;;;;GAeG;AACH,QAAA,MAAM,kBAAkB;IACtB,oEAAoE;;IAEpE,kEAAkE;;IAElE;yFACqF;;IAErF,kGAAkG;;IAElG;;qEAEiE;;IAEjE,gEAAgE;;IAEhE,oEAAoE;;IAEpE,gFAAgF;;IAEhF;;;mGAG+F;;CAEvF,CAAA;AAEV;;;;;;;;;;;;;;GAcG;AACH,QAAA,MAAM,gBAAgB;;;;;;CAMZ,CAAA;AAEV;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,QAAA,MAAM,eAAe;IACnB,0CAA0C;;IAY1C;;;iDAG6C;;IAE7C;;sFAEkF;;IAIlF,4CAA4C;;IAE5C,4CAA4C;;IAE5C,uCAAuC;;IAEvC,yCAAyC;;IAEzC,iDAAiD;;IAEjD,mDAAmD;;IAEnD,4DAA4D;;IAE5D,iCAAiC;;IAEjC,mCAAmC;;IAEnC,6EAA6E;;IAE7E,gCAAgC;;IAEhC,8CAA8C;;IAE9C,gDAAgD;;IAEhD,uCAAuC;;IAEvC,0CAA0C;;IAE1C,+CAA+C;;IAE/C,uCAAuC;;IAEvC,0CAA0C;;IAE1C,mCAAmC;;IAEnC,uCAAuC;;IAEvC,0CAA0C;;IAE1C;iEAC6D;;IAE7D,kGAAkG;;IAElG,kDAAkD;;IAElD,+EAA+E;;IAI/E;;;;;;;qEAOiE;;IAEjE;;;;mEAI+D;;IAE/D,wEAAwE;;IAExE,wDAAwD;;IAExD,uDAAuD;;IAEvD,iCAAiC;;IAEjC,qCAAqC;;IAErC,yDAAyD;;IAEzD,0DAA0D;;IAE1D,6CAA6C;;IAI7C,sCAAsC;;IAEtC,+BAA+B;;IAE/B,sEAAsE;;IAEtE;8EAC0E;;IAE1E,8CAA8C;;IAE9C,6BAA6B;;IAE7B,0FAA0F;;IAI1F,4BAA4B;;IAE5B,8BAA8B;;IAE9B,oCAAoC;;IAIpC,sDAAsD;;IAEtD,iCAAiC;;IAEjC,kDAAkD;;IAElD,gDAAgD;;IAIhD,yBAAyB;;IAEzB,iCAAiC;;IAEjC,mCAAmC;;IAEnC,2BAA2B;;IAI3B,oDAAoD;;IAEpD,+CAA+C;;IAE/C,8CAA8C;;IAI9C,mCAAmC;;IAEnC;;yGAEqG;;IAIrG,4BAA4B;;IAE5B,gDAAgD;;IAIhD,6CAA6C;;IAI7C,6BAA6B;;IAE7B,oCAAoC;;IAEpC,gCAAgC;;IAEhC,+BAA+B;;IAE/B,sCAAsC;;IAEtC,kCAAkC;;IAElC,sEAAsE;;IAEtE,iEAAiE;;IAEjE,mDAAmD;;IAEnD,sDAAsD;;IAEtD,oDAAoD;;IAEpD,0CAA0C;;IAE1C,mEAAmE;;IAYnE,mEAAmE;;IAEnE,4DAA4D;;IAI5D,uDAAuD;;IAIvD;;;+FAG2F;;IAE3F;;;;;;;;;;;;;;;qGAeiG;;IAEjG;sFACkF;;IAElF;iEAC6D;;IAI7D,+BAA+B;;IAO/B,uDAAuD;;IAEvD,qDAAqD;;IAErD,yDAAyD;;IAEzD,2DAA2D;;IAE3D,uDAAuD;;IAEvD;;+FAE2F;;IAI3F;;6DAEyD;;IAKzD,qGAAqG;;IAErG,wFAAwF;;IAExF,oDAAoD;;IAEpD,6DAA6D;;IAE7D,+DAA+D;;IAE/D,qEAAqE;;IAErE,uEAAuE;;IAEvE,yEAAyE;;IAEzE,kGAAkG;;IAElG,wGAAwG;;IAExG,0GAA0G;;IAE1G,oEAAoE;;IAEpE,6DAA6D;;IAE7D,+DAA+D;;CAEvD,CAAA;AAEV;;;;GAIG;AACH,eAAO,MAAM,WAAW;IACtB,oEAAoE;;IAEpE;;sEAEkE;;IAElE;;;;;;;;;iDAS6C;;CAErC,CAAA;AAEV;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE;SAAG,CAAC,IAAI,MAAM,OAAO,kBAAkB,GAAG,MAAM;KAAE,CAAA;IAC3D,KAAK,EAAE;SAAG,CAAC,IAAI,MAAM,OAAO,gBAAgB,GAAG,MAAM;KAAE,CAAA;IACvD;;;;;;;gGAO4F;IAC5F,IAAI,EAAE;SAAG,CAAC,IAAI,MAAM,OAAO,eAAe,GAAG,MAAM;KAAE,GAAG;QACtD,qBAAqB,EAAE,MAAM,CAAA;QAC7B,2BAA2B,EAAE,MAAM,CAAA;KACpC,CAAA;IACD;kEAC8D;IAC9D,aAAa,EAAE,MAAM,CAAA;CACtB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAInF;AAiCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+GG;AACH;;;;wEAIwE;AACxE,eAAO,MAAM,mBAAmB;;;CAA+B,CAAA;AAE/D,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CA2CxD;AAED;;;;;oFAKoF;AACpF,eAAO,MAAM,oBAAoB,aAAmB,CAAA;AAEpD;;2DAE2D;AAC3D,eAAO,MAAM,KAAK;;;;;;;;;;CAA+B,CAAA;AAEjD;;+BAE+B;AAC/B,eAAO,MAAM,WAAW;;;;;;CAA6B,CAAA;AAErD;;;;sGAIsG;AACtG,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BA/PI,MAAM;iCACA,MAAM;CA8PY,CAAA;AAEnD;8EAC8E;AAC9E,eAAO,MAAM,eAAe,QAAqC,CAAA;AA0BjE,iBAAS,wBAAwB,CAAC,MAAM,EAAE,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwLrD;AAaD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,GAAE,YAAoC,GAAG,WAAW,CAe1F;AAED,oGAAoG;AACpG,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,wBAAwB,CAAC,CAAA;AAMrE;+CAC+C;AAC/C,eAAO,MAAM,QAAQ;;;;CAA+B,CAAA;AAEpD;;;GAGG;AACH,eAAO,MAAM,MAAM;;;;;;CAA6B,CAAA;AAEhD;;;GAGG;AACH,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAA8B,CAAA;AAElD,0FAA0F;AAC1F,eAAO,MAAM,GAAG;;;;;CAA0B,CAAA;AAE1C,mFAAmF;AACnF,eAAO,MAAM,MAAM;;;;;;CAA6B,CAAA;AAEhD;+BAC+B;AAC/B,eAAO,MAAM,IAAI;;;;;;;;;;;;;CAA2B,CAAA;AAE5C;+CAC+C;AAC/C,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;CAA8B,CAAA"}
|
package/dist/tokens/palette.js
CHANGED
|
@@ -44,6 +44,37 @@ const SHADOW = {
|
|
|
44
44
|
light: "0 1px 2px rgba(28,25,23,0.12)",
|
|
45
45
|
dark: "0 1px 2px rgba(0,0,0,0.35)"
|
|
46
46
|
},
|
|
47
|
+
/**
|
|
48
|
+
* Interactive-control depth — the ONE value every raised Button/ActionIcon/Input variant shares
|
|
49
|
+
* (`docs/DESIGN-SPEC.md` §5's Button idiom). Exists rather than reusing `card` because `card`'s
|
|
50
|
+
* ring is OUTSET, and an outset ring paints in the color of whatever it is drawn over: fine
|
|
51
|
+
* between neutral panels, but on light scheme it resolves to `--vx-surface-hairline` and draws a
|
|
52
|
+
* pale grey outline around a saturated `filled` button, which reads as a rendering defect.
|
|
53
|
+
*
|
|
54
|
+
* THE TWO SCHEMES ARE SHAPED DIFFERENTLY, and the asymmetry is the whole design — do NOT "tidy"
|
|
55
|
+
* them into one shape. Both were arrived at by elimination, on screen:
|
|
56
|
+
*
|
|
57
|
+
* - DARK = drop + a full inset rim. Its edge is a LIGHTENING, so it cannot collide with the
|
|
58
|
+
* dark drop below the box; a uniform lightening on a dark surface reads as rim light wrapping
|
|
59
|
+
* a raised object, which is exactly right.
|
|
60
|
+
*
|
|
61
|
+
* - LIGHT = drop ONLY, no edge at all. A light-mode edge would have to be a DARKENING (there is
|
|
62
|
+
* nothing lighter than a near-white control to lighten with), and every position for one
|
|
63
|
+
* fails: a uniform dark ring does not read as depth, it reads as a BORDER — it made `default`
|
|
64
|
+
* and `light` look like outlined buttons and every form field look boxed. Moving it to the
|
|
65
|
+
* bottom edge alone then put a dark inner line directly above the dark drop, i.e. a visible
|
|
66
|
+
* DOUBLE BOTTOM BORDER. There is no third position, because a light-mode raised object's only
|
|
67
|
+
* honest cue is the shadow it casts. So light casts one and draws nothing.
|
|
68
|
+
*
|
|
69
|
+
* Consequence to know, on light only: a `default` control nested in a Card shares
|
|
70
|
+
* `--vx-surface-panel` with it and has no boundary but the drop. That is accepted — a wrong-
|
|
71
|
+
* looking border on every control in the app is worse than a soft edge in one nesting case. If it
|
|
72
|
+
* ever needs more, raise the DROP. Do not reintroduce an edge; both shapes have been tried.
|
|
73
|
+
*/
|
|
74
|
+
raised: {
|
|
75
|
+
light: "0 1px 2px rgba(28,25,23,0.10)",
|
|
76
|
+
dark: "0 1px 2px rgba(0,0,0,0.35), inset 0 0 0 1px color-mix(in srgb, #ffffff 8%, transparent)"
|
|
77
|
+
},
|
|
47
78
|
// Floating-layer elevation (menus, popovers, tooltips, modals, drawers) — a REAL drop shadow, a
|
|
48
79
|
// step above `card`, so detached surfaces read as lifted off the page (`docs/DESIGN-SPEC.md` §5).
|
|
49
80
|
// Unlike `card`, no embedded ring: floating surfaces carry a real 1px `--vx-surface-border` so
|
package/dist/tokens.css
CHANGED
|
@@ -206,6 +206,7 @@ html[data-mantine-color-scheme='dark'] {
|
|
|
206
206
|
--vx-divider: color-mix(in srgb, #ffffff 6%, transparent);
|
|
207
207
|
--vx-shadow-card: 0 1px 3px rgba(0,0,0,0.4), inset 0 0 0 1px color-mix(in srgb, #ffffff 4%, transparent);
|
|
208
208
|
--vx-shadow-ctrl: 0 1px 2px rgba(0,0,0,0.35);
|
|
209
|
+
--vx-shadow-raised: 0 1px 2px rgba(0,0,0,0.35), inset 0 0 0 1px color-mix(in srgb, #ffffff 8%, transparent);
|
|
209
210
|
--vx-shadow-overlay: 0 8px 24px rgba(0,0,0,0.5);
|
|
210
211
|
--vx-surface-bg: #27272a;
|
|
211
212
|
--vx-surface-panel: #2b2b2e;
|
|
@@ -269,6 +270,7 @@ html[data-mantine-color-scheme='light'] {
|
|
|
269
270
|
--vx-divider: color-mix(in srgb, #e5e5e5 65%, transparent);
|
|
270
271
|
--vx-shadow-card: 0 1px 2px rgba(28,25,23,0.05), 0 0 0 1px var(--vx-surface-hairline);
|
|
271
272
|
--vx-shadow-ctrl: 0 1px 2px rgba(28,25,23,0.12);
|
|
273
|
+
--vx-shadow-raised: 0 1px 2px rgba(28,25,23,0.10);
|
|
272
274
|
--vx-shadow-overlay: 0 8px 24px rgba(28,25,23,0.10);
|
|
273
275
|
--vx-surface-bg: #f2f2f5;
|
|
274
276
|
--vx-surface-panel: #fafafd;
|
package/dist/vite.d.ts
CHANGED
|
@@ -25,6 +25,12 @@ export type BasaltViteOptions = {
|
|
|
25
25
|
basaltSrc?: string;
|
|
26
26
|
/** App version surfaced via the `__APP_VERSION__` define. Defaults to `0.0.0`. */
|
|
27
27
|
version?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Print a one-time notice when `basalt-ui init` has never been run in this project (no
|
|
30
|
+
* `.basalt/manifest.json` found at or above the cwd) — see `warnIfUnenforced`. Default `true`.
|
|
31
|
+
* Set `false` to adopt the components without the toolchain and stop being told about it.
|
|
32
|
+
*/
|
|
33
|
+
enforcementNotice?: boolean;
|
|
28
34
|
};
|
|
29
35
|
export declare function basaltViteConfig(opts: BasaltViteOptions): UserConfig;
|
|
30
36
|
export type BasaltAppOptions = {
|
package/dist/vite.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAA6B,YAAY,EAAE,UAAU,EAAE,MAAM,MAAM,CAAA;AAI/E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAA;IACZ,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IACvB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,kFAAkF;IAClF,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B,CAAA;AA6CD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,GAAG,UAAU,CAgFpE;AAgBD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,gGAAgG;IAChG,IAAI,EAAE,MAAM,CAAA;IACZ,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gGAAgG;IAChG,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IACrD,mGAAmG;IACnG,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,wDAAwD;IACxD,OAAO,CAAC,EAAE,YAAY,GAAG,YAAY,GAAG,YAAY,GAAG,SAAS,CAAA;IAChE;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,mGAAmG;IACnG,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,wFAAwF;IACxF,KAAK,CAAC,EAAE,KAAK,GAAG;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAChC,8FAA8F;IAC9F,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,CAAA;IAC3B,oFAAoF;IACpF,GAAG,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,SAAS,GAAG,qBAAqB,CAAA;KAAE,CAAA;IACvF,8FAA8F;IAC9F,QAAQ,CAAC,EAAE,KAAK,CAAA;IAChB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAClD,CAAA;AAuZD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,YAAY,EAAE,CAMzE"}
|
package/dist/vite.js
CHANGED
|
@@ -1,10 +1,30 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
2
3
|
import { searchForWorkspaceRoot } from "vite";
|
|
3
4
|
import { SURFACE } from "./tokens/palette.js";
|
|
4
5
|
import { resolveColorMix } from "./vite-color-mix.js";
|
|
6
|
+
const UNENFORCED_SEARCH_DEPTH = 3;
|
|
7
|
+
let unenforcedNoticePrinted = false;
|
|
8
|
+
function warnIfUnenforced(cwd) {
|
|
9
|
+
if (unenforcedNoticePrinted) return;
|
|
10
|
+
let dir = cwd;
|
|
11
|
+
for (let i = 0; i <= UNENFORCED_SEARCH_DEPTH; i++) {
|
|
12
|
+
if (existsSync(resolve(dir, ".basalt/manifest.json"))) return;
|
|
13
|
+
const parent = dirname(dir);
|
|
14
|
+
if (parent === dir) break;
|
|
15
|
+
dir = parent;
|
|
16
|
+
}
|
|
17
|
+
unenforcedNoticePrinted = true;
|
|
18
|
+
console.warn(
|
|
19
|
+
"\n[basalt-ui] Installed, but `basalt-ui init` has never run here \u2014 no oxlint preset, no theme\n guard, no agent rules. You have the components and none of the enforcement.\n Fix: `bunx basalt-ui init`, then add `oxlint . && basalt-ui check-theme` as your\n lint script. Silence: pass `enforcementNotice: false` to basaltViteConfig().\n"
|
|
20
|
+
);
|
|
21
|
+
}
|
|
5
22
|
function basaltViteConfig(opts) {
|
|
6
23
|
const { port, host, allowedHosts, apiTarget, version = "0.0.0" } = opts;
|
|
7
24
|
const basaltSrc = opts.basaltSrc ?? process.env["BASALT_LOCAL"];
|
|
25
|
+
if (opts.enforcementNotice !== false && basaltSrc === void 0) {
|
|
26
|
+
warnIfUnenforced(process.cwd());
|
|
27
|
+
}
|
|
8
28
|
const config = {
|
|
9
29
|
define: {
|
|
10
30
|
__APP_VERSION__: JSON.stringify(process.env["BUILD_VERSION"] ?? version),
|
package/llms.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# basalt-ui v1.
|
|
1
|
+
# basalt-ui v1.7.0
|
|
2
2
|
# Opinionated framework for Mantine-based React apps — theme, app shell, visx charts, toolchain presets
|
|
3
3
|
#
|
|
4
4
|
# basalt-ui is an opinionated framework for Mantine-based React apps.
|
|
@@ -19,11 +19,11 @@
|
|
|
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.7.0
|
|
23
23
|
|
|
24
24
|
## basalt-ui
|
|
25
25
|
Import: import { ... } from 'basalt-ui'
|
|
26
|
-
Description: BasaltProvider, createBasaltTheme, BasaltShell + sidebar/mobile-nav/breadcrumbs, NavCountBadge, ThemeToggle, ThreadWorkspace + thread-chat components, dashboard composites (DeltaBadge, StatCard, EmptyState, SettingsSection/SettingsRow/DangerZone)
|
|
26
|
+
Description: BasaltProvider, createBasaltTheme, BasaltShell + sidebar/mobile-nav/breadcrumbs, NavCountBadge, ThemeToggle, ThreadWorkspace + thread-chat components, dashboard composites (DeltaBadge, StatCard with threshold tone, EmptyState, SettingsSection/SettingsRow/DangerZone)
|
|
27
27
|
Layer: mantine-coupled
|
|
28
28
|
Rule: basalt-mantine
|
|
29
29
|
OptionalPeers: react-markdown@^10.1.0, remark-gfm@^4.0.1, shiki@^4.3.0, beautiful-mermaid@^1.1.0
|
package/package.json
CHANGED
|
@@ -81,7 +81,8 @@ const LazyStickToBottom = lazy(() =>
|
|
|
81
81
|
border: 'none',
|
|
82
82
|
cursor: 'pointer',
|
|
83
83
|
backgroundColor: 'var(--vx-surface-panel)',
|
|
84
|
-
|
|
84
|
+
// Control depth, not panel depth — this is a real <button> (DESIGN-SPEC §2).
|
|
85
|
+
boxShadow: 'var(--vx-shadow-raised)',
|
|
85
86
|
color: 'var(--vx-faint)',
|
|
86
87
|
fontFamily: 'var(--basalt-font-mono)',
|
|
87
88
|
fontSize: VX.text.md,
|
|
@@ -76,13 +76,14 @@ export function Composer({
|
|
|
76
76
|
disabled,
|
|
77
77
|
onChange: (event) => setValue(event.currentTarget.value),
|
|
78
78
|
onKeyDown: handleKeyDown,
|
|
79
|
-
// Composer input surface (docs/DESIGN-SPEC.md §5): panel +
|
|
80
|
-
//
|
|
81
|
-
//
|
|
79
|
+
// Composer input surface (docs/DESIGN-SPEC.md §5): panel + control radius (6px, VX.radiusCtrl);
|
|
80
|
+
// the ring lives in the shadow, so the input carries no separate border. Depth is deliberately
|
|
81
|
+
// NOT set here — it arrives from the themed `.input` class (theme/controls.module.css), which
|
|
82
|
+
// also grounds it when disabled. A `boxShadow` in this `styles` object renders as an INLINE
|
|
83
|
+
// style and would beat that class unconditionally, leaving a disabled composer raised.
|
|
82
84
|
styles: {
|
|
83
85
|
input: {
|
|
84
86
|
backgroundColor: 'var(--vx-surface-panel)',
|
|
85
|
-
boxShadow: 'var(--vx-shadow-card)',
|
|
86
87
|
borderRadius: VX.radiusCtrl,
|
|
87
88
|
border: 'none',
|
|
88
89
|
},
|
package/src/cli/index.ts
CHANGED
|
@@ -80,6 +80,15 @@ export type BasaltConfig = {
|
|
|
80
80
|
* surface tokens. Default: `true` (ON). Set `false` to disable the `off-system-surface-var` check.
|
|
81
81
|
*/
|
|
82
82
|
offSystemSurfaceVar?: boolean
|
|
83
|
+
/**
|
|
84
|
+
* Flag a SHADE-PINNED Mantine color — `c="yellow.7"`, `bg="blue.4"`,
|
|
85
|
+
* `var(--mantine-color-red-6)`. A pinned step is one fixed swatch in BOTH color schemes, so it
|
|
86
|
+
* cannot stay legible in either; route a verdict color through `VX.status.*` / `--vx-status-*`,
|
|
87
|
+
* or drop the index (`c="red"`) and let the theme resolve the shade per scheme. `gray-*`/`dark-*`
|
|
88
|
+
* in `var()` form belong to `off-system-surface-var` instead. Default: `true` (ON). Set `false`
|
|
89
|
+
* to disable the `mantine-shade-index` check.
|
|
90
|
+
*/
|
|
91
|
+
mantineShadeIndex?: boolean
|
|
83
92
|
/**
|
|
84
93
|
* Flag raw lowercase JSX layout/surface elements (`div`/`span`/`section`/…) carrying an inline
|
|
85
94
|
* `style={{}}` with a layout/surface property — steer to a Mantine layout primitive
|
|
@@ -348,6 +357,7 @@ export function checkTheme(cwd: string = process.cwd()): number {
|
|
|
348
357
|
spacingSteps: cfg.spacingSteps ?? DEFAULT_GUARD_CONFIG.spacingSteps,
|
|
349
358
|
rawRadius: cfg.rawRadius ?? DEFAULT_GUARD_CONFIG.rawRadius,
|
|
350
359
|
forbiddenAccents: cfg.forbiddenAccents ?? DEFAULT_GUARD_CONFIG.forbiddenAccents,
|
|
360
|
+
mantineShadeIndex: cfg.mantineShadeIndex ?? DEFAULT_GUARD_CONFIG.mantineShadeIndex,
|
|
351
361
|
rawSurface: cfg.rawSurface ?? DEFAULT_GUARD_CONFIG.rawSurface,
|
|
352
362
|
cardWithBorder: cfg.cardWithBorder ?? DEFAULT_GUARD_CONFIG.cardWithBorder,
|
|
353
363
|
offSystemSurfaceVar: cfg.offSystemSurfaceVar ?? DEFAULT_GUARD_CONFIG.offSystemSurfaceVar,
|
|
@@ -1827,6 +1837,7 @@ export async function guardHook(cwd: string = process.cwd()): Promise<number> {
|
|
|
1827
1837
|
spacingSteps: cfg.spacingSteps ?? DEFAULT_GUARD_CONFIG.spacingSteps,
|
|
1828
1838
|
rawRadius: cfg.rawRadius ?? DEFAULT_GUARD_CONFIG.rawRadius,
|
|
1829
1839
|
forbiddenAccents: cfg.forbiddenAccents ?? DEFAULT_GUARD_CONFIG.forbiddenAccents,
|
|
1840
|
+
mantineShadeIndex: cfg.mantineShadeIndex ?? DEFAULT_GUARD_CONFIG.mantineShadeIndex,
|
|
1830
1841
|
rawSurface: cfg.rawSurface ?? DEFAULT_GUARD_CONFIG.rawSurface,
|
|
1831
1842
|
cardWithBorder: cfg.cardWithBorder ?? DEFAULT_GUARD_CONFIG.cardWithBorder,
|
|
1832
1843
|
offSystemSurfaceVar: cfg.offSystemSurfaceVar ?? DEFAULT_GUARD_CONFIG.offSystemSurfaceVar,
|
package/src/dashboard/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { DeltaBadge, type DeltaBadgeProps } from './delta-badge'
|
|
2
|
-
export { StatCard, type StatCardProps } from './stat-card'
|
|
2
|
+
export { StatCard, type StatCardProps, type StatCardTone } from './stat-card'
|
|
3
3
|
export { EmptyState, type EmptyStateProps } from './empty-state'
|
|
4
4
|
export {
|
|
5
5
|
SettingsSection,
|
|
@@ -8,6 +8,15 @@
|
|
|
8
8
|
* `src/dashboard` stays @visx-free — `sparkline` is a plain `ReactNode` slot, never a chart import
|
|
9
9
|
* here. Pass a `LineSparkline`/`BarSparkline` from `basalt-ui/charts` at the call site.
|
|
10
10
|
*
|
|
11
|
+
* `tone` marks the card as past a threshold with an accent rail down its leading edge. It exists
|
|
12
|
+
* because `value` is typed `string`: without it, a card whose number has crossed a threshold reads
|
|
13
|
+
* exactly as calm as one that hasn't, and the only way out from the consumer side is to wrap the
|
|
14
|
+
* card in a hand-rolled positioned `<Box>` — which is a second card idiom in the app, drawn by code
|
|
15
|
+
* the guard has no way to recognize as one. The rail is the largest signal available without
|
|
16
|
+
* touching the value's own type: full card height, overlaying the card's edge rather than adding to
|
|
17
|
+
* it, so layout is unchanged. `undefined` renders nothing at all — absence of a reading is neither a
|
|
18
|
+
* good one nor a bad one, and must never be tinted.
|
|
19
|
+
*
|
|
11
20
|
* @example
|
|
12
21
|
* import { StatCard } from 'basalt-ui'
|
|
13
22
|
* import { LineSparkline } from 'basalt-ui/charts'
|
|
@@ -18,12 +27,28 @@
|
|
|
18
27
|
* delta={4.2}
|
|
19
28
|
* sparkline={<LineSparkline data={history} width={160} height={32} />}
|
|
20
29
|
* />
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // Past a threshold — an accent rail down the leading edge, value untouched.
|
|
33
|
+
* <StatCard label="Downtime" value="29 min" tone="bad" />
|
|
21
34
|
*/
|
|
22
|
-
import { Card, Group } from '@mantine/core'
|
|
35
|
+
import { Box, Card, Group, VisuallyHidden } from '@mantine/core'
|
|
23
36
|
import type { ReactNode } from 'react'
|
|
24
37
|
import { DeltaBadge } from './delta-badge'
|
|
25
38
|
import { VX } from '../tokens'
|
|
26
39
|
|
|
40
|
+
/** A threshold verdict on the card's value. `undefined` is never tinted — see the module docblock. */
|
|
41
|
+
export type StatCardTone = 'warn' | 'bad'
|
|
42
|
+
|
|
43
|
+
/** The rail's width, in px. Wide enough to read as a deliberate mark at a glance, narrow enough
|
|
44
|
+
* that it cannot be mistaken for the card's own edge. */
|
|
45
|
+
const TONE_RAIL_WIDTH = 3
|
|
46
|
+
|
|
47
|
+
const TONE_LABEL: Record<StatCardTone, string> = {
|
|
48
|
+
warn: 'Past the warning threshold',
|
|
49
|
+
bad: 'Past the severe threshold',
|
|
50
|
+
}
|
|
51
|
+
|
|
27
52
|
export type StatCardProps = {
|
|
28
53
|
/** Mono uppercase micro-label (docs/DESIGN-SPEC.md §3: 12.5px, tracking 0.06em, faint). */
|
|
29
54
|
label: string
|
|
@@ -38,9 +63,20 @@ export type StatCardProps = {
|
|
|
38
63
|
sparkline?: ReactNode
|
|
39
64
|
/** Optional header-right slot (e.g. a ghost "..." menu trigger) — consumer-owned. */
|
|
40
65
|
menu?: ReactNode
|
|
66
|
+
/** Threshold verdict — draws an accent rail down the card's leading edge and announces itself to
|
|
67
|
+
* assistive tech. Omit it for a reading that is fine, and for one that is absent. */
|
|
68
|
+
tone?: StatCardTone
|
|
41
69
|
}
|
|
42
70
|
|
|
43
|
-
export function StatCard({
|
|
71
|
+
export function StatCard({
|
|
72
|
+
label,
|
|
73
|
+
value,
|
|
74
|
+
delta,
|
|
75
|
+
deltaPeriod,
|
|
76
|
+
sparkline,
|
|
77
|
+
menu,
|
|
78
|
+
tone,
|
|
79
|
+
}: StatCardProps) {
|
|
44
80
|
return (
|
|
45
81
|
<Card
|
|
46
82
|
style={{
|
|
@@ -56,8 +92,31 @@ export function StatCard({ label, value, delta, deltaPeriod, sparkline, menu }:
|
|
|
56
92
|
flexDirection: 'column',
|
|
57
93
|
gap: 'var(--vx-space-stat-card-gap)',
|
|
58
94
|
overflow: 'hidden',
|
|
95
|
+
// Anchors the tone rail. Set unconditionally so a card's stacking context does not change
|
|
96
|
+
// depending on whether it happens to have crossed a threshold this render.
|
|
97
|
+
position: 'relative',
|
|
59
98
|
}}
|
|
99
|
+
data-tone={tone}
|
|
60
100
|
>
|
|
101
|
+
{tone !== undefined && (
|
|
102
|
+
<>
|
|
103
|
+
{/* The rail is decoration; the verdict it encodes is real information, so it is also
|
|
104
|
+
stated in text for anyone who cannot see a 3px colour bar. Colour alone is never the
|
|
105
|
+
only carrier of a threshold. */}
|
|
106
|
+
<VisuallyHidden>{TONE_LABEL[tone]}</VisuallyHidden>
|
|
107
|
+
<Box
|
|
108
|
+
aria-hidden="true"
|
|
109
|
+
style={{
|
|
110
|
+
position: 'absolute',
|
|
111
|
+
insetBlock: 0,
|
|
112
|
+
insetInlineStart: 0,
|
|
113
|
+
width: TONE_RAIL_WIDTH,
|
|
114
|
+
background: tone === 'bad' ? VX.status.bad : VX.status.warn,
|
|
115
|
+
}}
|
|
116
|
+
/>
|
|
117
|
+
</>
|
|
118
|
+
)}
|
|
119
|
+
|
|
61
120
|
<Group justify="space-between" align="flex-start" wrap="nowrap" gap="xs">
|
|
62
121
|
<span
|
|
63
122
|
style={{
|