basalt-ui 1.25.0 → 1.27.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 +8 -6
- package/CHANGELOG.md +26 -0
- package/MIGRATING.md +815 -23
- package/README.md +45 -32
- package/agent/rules/basalt-batteries.md +205 -0
- package/agent/rules/basalt-charts.md +125 -555
- package/agent/rules/basalt-controls.md +160 -0
- package/agent/rules/basalt-mantine.md +146 -439
- package/agent/rules/basalt-state.md +118 -96
- package/agent/rules/basalt-tokens.md +126 -288
- package/agent/skills/basalt-app/SKILL.md +70 -195
- package/agent/skills/basalt-charts/SKILL.md +54 -234
- package/agent/skills/basalt-design/SKILL.md +56 -248
- package/agent/templates/CLAUDE-block.md.tpl +31 -40
- package/agent/templates/DESIGN.md.tpl +22 -51
- package/configs/oxlint-plugin.js +1465 -36
- package/configs/oxlint-plugin.test.ts +1190 -11
- package/configs/oxlint.json +11 -3
- package/dist/charts/cursor/resolve.d.ts +18 -0
- package/dist/charts/cursor/resolve.d.ts.map +1 -1
- package/dist/charts/cursor/resolve.js +15 -6
- package/dist/charts/cursor/scope.d.ts +6 -3
- package/dist/charts/cursor/scope.d.ts.map +1 -1
- package/dist/charts/cursor/store.d.ts +5 -1
- package/dist/charts/cursor/store.d.ts.map +1 -1
- package/dist/charts/cursor/store.js +4 -4
- package/dist/charts/hooks/useChartCursor.d.ts.map +1 -1
- package/dist/charts/hooks/useChartCursor.js +9 -8
- package/dist/charts/index.d.ts +2 -2
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/primitives/ChartCard.d.ts +34 -9
- package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
- package/dist/charts/primitives/ChartCard.js +29 -114
- package/dist/charts/primitives/ChartCard.test.d.ts +2 -0
- package/dist/charts/primitives/ChartCard.test.d.ts.map +1 -0
- package/dist/charts/sparklines/BarSparkline.d.ts +26 -2
- package/dist/charts/sparklines/BarSparkline.d.ts.map +1 -1
- package/dist/charts/sparklines/BarSparkline.js +18 -4
- package/dist/cli/check-coverage.test.d.ts +2 -0
- package/dist/cli/check-coverage.test.d.ts.map +1 -0
- package/dist/cli/index.d.ts +67 -4
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +225 -7
- package/dist/connectivity/connectivity-indicator.d.ts +4 -1
- package/dist/connectivity/connectivity-indicator.d.ts.map +1 -1
- package/dist/connectivity/connectivity-indicator.js +1 -1
- package/dist/content/index.d.ts +5 -3
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +4 -2
- package/dist/content/prose.module.css +7 -28
- package/dist/controls/actions.d.ts +169 -0
- package/dist/controls/actions.d.ts.map +1 -0
- package/dist/controls/actions.js +287 -0
- package/dist/controls/actions.module.css +62 -0
- package/dist/controls/actions.module.css.d.ts +13 -0
- package/dist/controls/actions.test.d.ts +2 -0
- package/dist/controls/actions.test.d.ts.map +1 -0
- package/dist/controls/actions.type-guard.test.d.ts +2 -0
- package/dist/controls/actions.type-guard.test.d.ts.map +1 -0
- package/dist/controls/compare-filter.d.ts +41 -0
- package/dist/controls/compare-filter.d.ts.map +1 -0
- package/dist/controls/compare-filter.js +23 -0
- package/dist/controls/control-group.d.ts +19 -0
- package/dist/controls/control-group.d.ts.map +1 -0
- package/dist/controls/control-group.js +20 -0
- package/dist/controls/control-group.test.d.ts +2 -0
- package/dist/controls/control-group.test.d.ts.map +1 -0
- package/dist/controls/controls.module.css +542 -0
- package/dist/controls/controls.module.css.d.ts +35 -0
- package/dist/controls/controls.router.test.d.ts +2 -0
- package/dist/controls/controls.router.test.d.ts.map +1 -0
- package/dist/controls/enum-filter.d.ts +39 -0
- package/dist/controls/enum-filter.d.ts.map +1 -0
- package/dist/controls/enum-filter.js +93 -0
- package/dist/controls/filter-context.d.ts +60 -0
- package/dist/controls/filter-context.d.ts.map +1 -0
- package/dist/controls/filter-context.js +69 -0
- package/dist/controls/filter-pill.d.ts +28 -0
- package/dist/controls/filter-pill.d.ts.map +1 -0
- package/dist/controls/filter-pill.js +70 -0
- package/dist/controls/filter-set.d.ts +13 -0
- package/dist/controls/filter-set.d.ts.map +1 -0
- package/dist/controls/filter-set.js +125 -0
- package/dist/controls/filter-set.test.d.ts +2 -0
- package/dist/controls/filter-set.test.d.ts.map +1 -0
- package/dist/controls/filter-sheet.d.ts +117 -0
- package/dist/controls/filter-sheet.d.ts.map +1 -0
- package/dist/controls/filter-sheet.js +146 -0
- package/dist/controls/glyphs.d.ts +27 -0
- package/dist/controls/glyphs.d.ts.map +1 -0
- package/dist/controls/glyphs.js +49 -0
- package/dist/controls/index.d.ts +20 -0
- package/dist/controls/index.d.ts.map +1 -0
- package/dist/controls/index.js +46 -0
- package/dist/controls/multi-select-filter.d.ts +23 -0
- package/dist/controls/multi-select-filter.d.ts.map +1 -0
- package/dist/controls/multi-select-filter.js +89 -0
- package/dist/controls/number-filter.d.ts +26 -0
- package/dist/controls/number-filter.d.ts.map +1 -0
- package/dist/controls/number-filter.js +143 -0
- package/dist/controls/peer-boundary.test.d.ts +2 -0
- package/dist/controls/peer-boundary.test.d.ts.map +1 -0
- package/dist/controls/props-contract.test.d.ts +2 -0
- package/dist/controls/props-contract.test.d.ts.map +1 -0
- package/dist/controls/range-filter.d.ts +43 -0
- package/dist/controls/range-filter.d.ts.map +1 -0
- package/dist/controls/range-filter.js +135 -0
- package/dist/controls/range-filter.type-guard.test.d.ts +2 -0
- package/dist/controls/range-filter.type-guard.test.d.ts.map +1 -0
- package/dist/controls/search-filter.d.ts +10 -0
- package/dist/controls/search-filter.d.ts.map +1 -0
- package/dist/controls/search-filter.js +81 -0
- package/dist/controls/select-filter.d.ts +74 -0
- package/dist/controls/select-filter.d.ts.map +1 -0
- package/dist/controls/select-filter.js +10 -0
- package/dist/controls/sync-button.d.ts +38 -0
- package/dist/controls/sync-button.d.ts.map +1 -0
- package/dist/controls/sync-button.js +119 -0
- package/dist/controls/sync-button.module.css +63 -0
- package/dist/controls/sync-button.module.css.d.ts +11 -0
- package/dist/controls/sync-button.test.d.ts +2 -0
- package/dist/controls/sync-button.test.d.ts.map +1 -0
- package/dist/controls/toggle-filter.d.ts +9 -0
- package/dist/controls/toggle-filter.d.ts.map +1 -0
- package/dist/controls/toggle-filter.js +42 -0
- package/dist/controls/view-tabs.d.ts +23 -0
- package/dist/controls/view-tabs.d.ts.map +1 -0
- package/dist/controls/view-tabs.js +59 -0
- package/dist/controls-dates/date-range-picker.d.ts +4 -0
- package/dist/controls-dates/date-range-picker.d.ts.map +1 -0
- package/dist/controls-dates/date-range-picker.js +18 -0
- package/dist/controls-dates/index.d.ts +9 -0
- package/dist/controls-dates/index.d.ts.map +1 -0
- package/dist/controls-dates/index.js +4 -0
- package/dist/dashboard/delta-badge.d.ts +6 -12
- package/dist/dashboard/delta-badge.d.ts.map +1 -1
- package/dist/dashboard/delta-badge.js +1 -50
- package/dist/dashboard/index.d.ts +1 -1
- package/dist/dashboard/index.d.ts.map +1 -1
- package/dist/dashboard/index.js +3 -1
- package/dist/dashboard/settings-section.d.ts +6 -4
- package/dist/dashboard/settings-section.d.ts.map +1 -1
- package/dist/dashboard/settings-section.js +23 -16
- package/dist/dashboard/settings-section.module.css +10 -0
- package/dist/dashboard/settings-section.module.css.d.ts +1 -0
- package/dist/dashboard/settings-section.test.d.ts +2 -0
- package/dist/dashboard/settings-section.test.d.ts.map +1 -0
- package/dist/dashboard/stat-card.d.ts +95 -9
- package/dist/dashboard/stat-card.d.ts.map +1 -1
- package/dist/dashboard/stat-card.js +108 -49
- package/dist/dashboard/stat-card.module.css +153 -0
- package/dist/dashboard/stat-card.module.css.d.ts +15 -0
- package/dist/data/data-table.d.ts +37 -15
- package/dist/data/data-table.d.ts.map +1 -1
- package/dist/data/data-table.js +96 -58
- package/dist/guard/grace.test.d.ts +20 -0
- package/dist/guard/grace.test.d.ts.map +1 -0
- package/dist/guard/index.d.ts +66 -3
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +78 -7
- package/dist/guard/types.d.ts +3 -3
- package/dist/guard/types.d.ts.map +1 -1
- package/dist/index.d.ts +13 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -10
- package/dist/nav/types.d.ts +78 -2
- package/dist/nav/types.d.ts.map +1 -1
- package/dist/notifications/bell.d.ts +4 -1
- package/dist/notifications/bell.d.ts.map +1 -1
- package/dist/notifications/index.d.ts +1 -1
- package/dist/router-tanstack/field.d.ts +14 -0
- package/dist/router-tanstack/field.d.ts.map +1 -0
- package/dist/router-tanstack/field.js +4 -0
- package/dist/router-tanstack/index.d.ts +6 -2
- package/dist/router-tanstack/index.d.ts.map +1 -1
- package/dist/router-tanstack/index.js +4 -0
- package/dist/router-tanstack/multi-search-param-store.d.ts +4 -52
- package/dist/router-tanstack/multi-search-param-store.d.ts.map +1 -1
- package/dist/router-tanstack/multi-search-param-store.js +19 -27
- package/dist/router-tanstack/search-param-store.d.ts +3 -87
- package/dist/router-tanstack/search-param-store.d.ts.map +1 -1
- package/dist/router-tanstack/search-param-store.js +16 -45
- package/dist/router-tanstack/search-store.d.ts +84 -0
- package/dist/router-tanstack/search-store.d.ts.map +1 -0
- package/dist/router-tanstack/search-store.js +271 -0
- package/dist/router-tanstack/search-store.router.test.d.ts +2 -0
- package/dist/router-tanstack/search-store.router.test.d.ts.map +1 -0
- package/dist/router-tanstack/search-store.test.d.ts +2 -0
- package/dist/router-tanstack/search-store.test.d.ts.map +1 -0
- package/dist/router-tanstack/use-nav.d.ts +0 -2
- package/dist/router-tanstack/use-nav.d.ts.map +1 -1
- package/dist/router-tanstack/use-nav.js +34 -12
- package/dist/router-tanstack/use-nav.test.d.ts +2 -0
- package/dist/router-tanstack/use-nav.test.d.ts.map +1 -0
- package/dist/section/index.d.ts +2 -0
- package/dist/section/index.d.ts.map +1 -0
- package/dist/section/index.js +4 -0
- package/dist/section/section.d.ts +24 -0
- package/dist/section/section.d.ts.map +1 -0
- package/dist/section/section.js +126 -0
- package/dist/section/section.module.css +104 -0
- package/dist/section/section.module.css.d.ts +11 -0
- package/dist/section/section.test.d.ts +2 -0
- package/dist/section/section.test.d.ts.map +1 -0
- package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
- package/dist/shell/app-breadcrumbs.js +18 -15
- package/dist/shell/app-header.module.css +77 -37
- package/dist/shell/app-header.module.css.d.ts +2 -1
- package/dist/shell/app-mobile-nav.d.ts +9 -2
- package/dist/shell/app-mobile-nav.d.ts.map +1 -1
- package/dist/shell/app-mobile-nav.js +98 -11
- package/dist/shell/app-mobile-nav.module.css +8 -0
- package/dist/shell/app-mobile-nav.module.css.d.ts +1 -0
- package/dist/shell/app-sidebar.d.ts +55 -28
- package/dist/shell/app-sidebar.d.ts.map +1 -1
- package/dist/shell/app-sidebar.js +116 -67
- package/dist/shell/app-sidebar.module.css +303 -20
- package/dist/shell/app-sidebar.module.css.d.ts +19 -1
- package/dist/shell/index.d.ts +55 -21
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +40 -18
- package/dist/shell/mobile-nav-model.d.ts +13 -2
- package/dist/shell/mobile-nav-model.d.ts.map +1 -1
- package/dist/shell/mobile-nav-model.js +15 -1
- package/dist/shell/nav-count-badge.d.ts +1 -1
- package/dist/shell/page-bar.d.ts +51 -0
- package/dist/shell/page-bar.d.ts.map +1 -0
- package/dist/shell/page-bar.js +154 -0
- package/dist/shell/page-bar.module.css +178 -0
- package/dist/shell/page-bar.module.css.d.ts +17 -0
- package/dist/shell/page-bar.test.d.ts +2 -0
- package/dist/shell/page-bar.test.d.ts.map +1 -0
- package/dist/shell/sidebar-block-model.d.ts +48 -0
- package/dist/shell/sidebar-block-model.d.ts.map +1 -0
- package/dist/shell/sidebar-block-model.js +39 -0
- package/dist/shell/sidebar-blocks.d.ts +32 -0
- package/dist/shell/sidebar-blocks.d.ts.map +1 -0
- package/dist/shell/sidebar-blocks.js +169 -0
- package/dist/shell/sidebar-search.d.ts +13 -1
- package/dist/shell/sidebar-search.d.ts.map +1 -1
- package/dist/shell/sidebar-search.js +34 -1
- package/dist/shell/sidebar-search.module.css +26 -0
- package/dist/shell/sidebar-search.module.css.d.ts +2 -0
- package/dist/state/fields.d.ts +565 -0
- package/dist/state/fields.d.ts.map +1 -0
- package/dist/state/fields.js +405 -0
- package/dist/state/fields.test.d.ts +2 -0
- package/dist/state/fields.test.d.ts.map +1 -0
- package/dist/state/persisted.d.ts +60 -0
- package/dist/state/persisted.d.ts.map +1 -0
- package/dist/state/persisted.js +123 -0
- package/dist/state/persisted.test.d.ts +2 -0
- package/dist/state/persisted.test.d.ts.map +1 -0
- package/dist/state.d.ts +11 -56
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +14 -120
- package/dist/styles.css +13 -0
- package/dist/surfaces.d.ts +106 -24
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +156 -19
- package/dist/theme/controls.module.css +16 -1
- package/dist/theme/ctl-theme.d.ts +32 -0
- package/dist/theme/ctl-theme.d.ts.map +1 -0
- package/dist/theme/ctl-theme.js +54 -0
- package/dist/theme/ctl-theme.test.d.ts +2 -0
- package/dist/theme/ctl-theme.test.d.ts.map +1 -0
- package/dist/theme/ctl-tier-coverage.test.d.ts +2 -0
- package/dist/theme/ctl-tier-coverage.test.d.ts.map +1 -0
- package/dist/theme/divider-law.test.d.ts +2 -0
- package/dist/theme/divider-law.test.d.ts.map +1 -0
- package/dist/theme/icon-slot.d.ts +44 -0
- package/dist/theme/icon-slot.d.ts.map +1 -0
- package/dist/theme/icon-slot.js +21 -0
- package/dist/theme/icon-slot.module.css +47 -0
- package/dist/theme/icon-slot.module.css.d.ts +8 -0
- package/dist/theme/icon-slot.test.d.ts +2 -0
- package/dist/theme/icon-slot.test.d.ts.map +1 -0
- package/dist/theme/index.d.ts +1 -0
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +82 -5
- package/dist/theme/layout-rhythm-css.test.d.ts +2 -0
- package/dist/theme/layout-rhythm-css.test.d.ts.map +1 -0
- package/dist/theme/segmented-control.module.css +20 -0
- package/dist/theme/shadow-surfaces.test.d.ts.map +1 -1
- package/dist/tokens/index.d.ts +12 -0
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +38 -8
- package/dist/tokens/palette.d.ts +97 -61
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +76 -38
- package/dist/tokens.css +10 -2
- package/dist/widget-header/delta-badge.d.ts +13 -0
- package/dist/widget-header/delta-badge.d.ts.map +1 -0
- package/dist/widget-header/delta-badge.js +29 -0
- package/dist/widget-header/delta-badge.test.d.ts +2 -0
- package/dist/widget-header/delta-badge.test.d.ts.map +1 -0
- package/dist/widget-header/index.d.ts +3 -0
- package/dist/widget-header/index.d.ts.map +1 -0
- package/dist/widget-header/index.js +6 -0
- package/dist/widget-header/no-mantine.test.d.ts +2 -0
- package/dist/widget-header/no-mantine.test.d.ts.map +1 -0
- package/dist/widget-header/widget-header.d.ts +67 -0
- package/dist/widget-header/widget-header.d.ts.map +1 -0
- package/dist/widget-header/widget-header.js +89 -0
- package/dist/widget-header/widget-header.module.css +350 -0
- package/dist/widget-header/widget-header.module.css.d.ts +25 -0
- package/dist/widget-header/widget-header.test.d.ts +2 -0
- package/dist/widget-header/widget-header.test.d.ts.map +1 -0
- package/llms.txt +30 -17
- package/package.json +14 -1
- package/src/charts/cursor/resolve.test.ts +29 -1
- package/src/charts/cursor/resolve.ts +38 -6
- package/src/charts/cursor/scope.tsx +6 -3
- package/src/charts/cursor/store.test.ts +19 -1
- package/src/charts/cursor/store.ts +10 -5
- package/src/charts/hooks/useChartCursor.ts +16 -8
- package/src/charts/index.ts +2 -2
- package/src/charts/primitives/CartesianChart.test.tsx +124 -0
- package/src/charts/primitives/ChartCard.test.tsx +66 -0
- package/src/charts/primitives/ChartCard.tsx +86 -146
- package/src/charts/sparklines/BarSparkline.tsx +43 -5
- package/src/cli/check-coverage.test.ts +297 -0
- package/src/cli/check-theme.test.ts +38 -14
- package/src/cli/index.ts +392 -16
- package/src/cli/init-sync.test.ts +97 -0
- package/src/connectivity/connectivity-indicator.tsx +16 -9
- package/src/content/index.ts +16 -3
- package/src/content/prose.module.css +7 -28
- package/src/content/prose.module.css.test.ts +8 -24
- package/src/controls/actions.module.css +62 -0
- package/src/controls/actions.module.css.d.ts +13 -0
- package/src/controls/actions.test.tsx +252 -0
- package/src/controls/actions.tsx +655 -0
- package/src/controls/actions.type-guard.test.ts +63 -0
- package/src/controls/compare-filter.tsx +64 -0
- package/src/controls/control-group.test.tsx +229 -0
- package/src/controls/control-group.tsx +63 -0
- package/src/controls/controls.module.css +542 -0
- package/src/controls/controls.module.css.d.ts +35 -0
- package/src/controls/controls.router.test.tsx +1053 -0
- package/src/controls/enum-filter.tsx +170 -0
- package/src/controls/filter-context.tsx +158 -0
- package/src/controls/filter-pill.tsx +120 -0
- package/src/controls/filter-set.test.tsx +472 -0
- package/src/controls/filter-set.tsx +225 -0
- package/src/controls/filter-sheet.tsx +285 -0
- package/src/controls/glyphs.tsx +81 -0
- package/src/controls/index.ts +49 -0
- package/src/controls/multi-select-filter.tsx +140 -0
- package/src/controls/number-filter.tsx +291 -0
- package/src/controls/peer-boundary.test.ts +85 -0
- package/src/controls/props-contract.test.tsx +137 -0
- package/src/controls/range-filter.tsx +237 -0
- package/src/controls/range-filter.type-guard.test.tsx +122 -0
- package/src/controls/search-filter.tsx +123 -0
- package/src/controls/select-filter.tsx +89 -0
- package/src/controls/sync-button.module.css +63 -0
- package/src/controls/sync-button.module.css.d.ts +11 -0
- package/src/controls/sync-button.test.tsx +183 -0
- package/src/controls/sync-button.tsx +197 -0
- package/src/controls/toggle-filter.tsx +60 -0
- package/src/controls/view-tabs.tsx +110 -0
- package/src/controls-dates/date-range-picker.tsx +42 -0
- package/src/controls-dates/index.ts +8 -0
- package/src/dashboard/delta-badge.tsx +4 -74
- package/src/dashboard/index.ts +6 -1
- package/src/dashboard/settings-section.module.css +10 -0
- package/src/dashboard/settings-section.module.css.d.ts +1 -0
- package/src/dashboard/settings-section.test.tsx +56 -0
- package/src/dashboard/settings-section.tsx +33 -30
- package/src/dashboard/stat-card.module.css +153 -0
- package/src/dashboard/stat-card.module.css.d.ts +15 -0
- package/src/dashboard/stat-card.test.tsx +298 -1
- package/src/dashboard/stat-card.tsx +289 -67
- package/src/data/data-table.test.tsx +96 -11
- package/src/data/data-table.tsx +228 -84
- package/src/guard/check-source.test.ts +159 -13
- package/src/guard/grace.test.ts +158 -0
- package/src/guard/index.ts +185 -29
- package/src/guard/types.ts +4 -2
- package/src/index.ts +51 -5
- package/src/nav/types.ts +84 -2
- package/src/notifications/bell.tsx +6 -3
- package/src/notifications/index.ts +1 -1
- package/src/router-tanstack/field.ts +35 -0
- package/src/router-tanstack/index.ts +28 -2
- package/src/router-tanstack/multi-search-param-store.test.ts +71 -3
- package/src/router-tanstack/multi-search-param-store.ts +46 -77
- package/src/router-tanstack/search-param-store.ts +42 -159
- package/src/router-tanstack/search-store.router.test.tsx +882 -0
- package/src/router-tanstack/search-store.test.ts +578 -0
- package/src/router-tanstack/search-store.ts +546 -0
- package/src/router-tanstack/use-nav.test.tsx +187 -0
- package/src/router-tanstack/use-nav.ts +72 -14
- package/src/section/index.ts +1 -0
- package/src/section/section.module.css +104 -0
- package/src/section/section.module.css.d.ts +11 -0
- package/src/section/section.test.tsx +204 -0
- package/src/section/section.tsx +220 -0
- package/src/shell/app-breadcrumbs.tsx +44 -31
- package/src/shell/app-header.module.css +77 -37
- package/src/shell/app-header.module.css.d.ts +2 -1
- package/src/shell/app-mobile-nav.module.css +8 -0
- package/src/shell/app-mobile-nav.module.css.d.ts +1 -0
- package/src/shell/app-mobile-nav.tsx +147 -8
- package/src/shell/app-sidebar-account.tsx +2 -2
- package/src/shell/app-sidebar.module.css +303 -20
- package/src/shell/app-sidebar.module.css.d.ts +19 -1
- package/src/shell/app-sidebar.test.tsx +456 -41
- package/src/shell/app-sidebar.tsx +276 -140
- package/src/shell/index.test.tsx +291 -5
- package/src/shell/index.tsx +137 -38
- package/src/shell/mobile-nav-model.test.ts +228 -7
- package/src/shell/mobile-nav-model.ts +53 -3
- package/src/shell/nav-count-badge.tsx +1 -1
- package/src/shell/page-bar.module.css +178 -0
- package/src/shell/page-bar.module.css.d.ts +17 -0
- package/src/shell/page-bar.test.tsx +316 -0
- package/src/shell/page-bar.tsx +315 -0
- package/src/shell/sidebar-block-model.ts +83 -0
- package/src/shell/sidebar-blocks.tsx +301 -0
- package/src/shell/sidebar-search.module.css +26 -0
- package/src/shell/sidebar-search.module.css.d.ts +2 -0
- package/src/shell/sidebar-search.tsx +71 -2
- package/src/state/fields.test.tsx +512 -0
- package/src/state/fields.ts +1150 -0
- package/src/state/persisted.test.ts +91 -0
- package/src/state/persisted.ts +261 -0
- package/src/state.ts +52 -240
- package/src/styles.css +13 -0
- package/src/surfaces.ts +204 -36
- package/src/theme/controls.module.css +16 -1
- package/src/theme/ctl-theme.test.tsx +275 -0
- package/src/theme/ctl-theme.tsx +138 -0
- package/src/theme/ctl-tier-coverage.test.ts +170 -0
- package/src/theme/density-relations.test.ts +106 -51
- package/src/theme/divider-law.test.ts +259 -0
- package/src/theme/icon-slot.module.css +47 -0
- package/src/theme/icon-slot.module.css.d.ts +8 -0
- package/src/theme/icon-slot.test.tsx +275 -0
- package/src/theme/icon-slot.tsx +59 -0
- package/src/theme/index.ts +165 -3
- package/src/theme/layout-rhythm-css.test.ts +72 -0
- package/src/theme/radius.test.ts +11 -1
- package/src/theme/segmented-control.module.css +20 -0
- package/src/theme/shadow-surfaces.test.ts +20 -7
- package/src/theme/spacing.test.ts +75 -27
- package/src/theme/use-basalt-spacing.ts +2 -2
- package/src/tokens/density.test.ts +7 -25
- package/src/tokens/index.ts +48 -8
- package/src/tokens/palette.ts +107 -73
- package/src/widget-header/delta-badge.test.tsx +60 -0
- package/src/widget-header/delta-badge.tsx +57 -0
- package/src/widget-header/index.ts +2 -0
- package/src/widget-header/no-mantine.test.ts +33 -0
- package/src/widget-header/widget-header.module.css +350 -0
- package/src/widget-header/widget-header.module.css.d.ts +25 -0
- package/src/widget-header/widget-header.test.tsx +187 -0
- package/src/widget-header/widget-header.tsx +213 -0
- package/agent/rules/basalt-agent.md +0 -452
- package/agent/rules/basalt-app.md +0 -246
- package/agent/rules/basalt-commands.md +0 -320
- package/agent/rules/basalt-content.md +0 -409
- package/agent/rules/basalt-data.md +0 -329
- package/agent/rules/basalt-forms.md +0 -186
- package/agent/rules/basalt-notifications.md +0 -264
- package/agent/rules/basalt-query.md +0 -187
- package/agent/rules/basalt-router.md +0 -336
- package/dist/content/article-filter-bar.d.ts +0 -40
- package/dist/content/article-filter-bar.d.ts.map +0 -1
- package/dist/content/article-filter-bar.js +0 -56
- package/dist/shell/page-header.d.ts +0 -13
- package/dist/shell/page-header.d.ts.map +0 -1
- package/dist/shell/page-header.js +0 -23
- package/src/content/article-filter-bar.tsx +0 -88
- package/src/shell/page-header.tsx +0 -35
|
@@ -1,268 +1,76 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: basalt-design
|
|
3
|
-
description: Design and restyle
|
|
4
|
-
when_to_use: User is building or restyling charts, dashboards
|
|
3
|
+
description: Design and restyle dashboards, charts and metric UIs in a basalt-ui app (Mantine v9 + visx) — the procedure: read the law, decide before coding, tune by eye in the theme lab, bake the values back into the palette. Use when building or restyling charts/dashboards, choosing or tuning a palette, or when a UI looks childish / inconsistent / over-colored.
|
|
4
|
+
when_to_use: User is building or restyling charts, dashboards or metric UIs in a basalt-ui consumer; picking or tuning a palette; deciding dark/light shades; or complaining that a UI looks "AI default", inconsistent across tabs or over-saturated. Also the entry point for any "make this look professional" request in a Mantine + visx app.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
`/basalt-design` is the
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
`/basalt-design` is the METHOD. The law is the `basalt-*` rules plus this app's `DESIGN.md`, and the
|
|
8
|
+
managed `CLAUDE.md` block states which wins — read them, do not re-derive them here. This skill is
|
|
9
|
+
the sequence to follow and the loop to run.
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## 1. Load the law before touching code
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
- `.claude/rules/basalt-tokens.md` — the identity, the color/spacing/type law, `theme-allow`.
|
|
14
|
+
- `.claude/rules/basalt-mantine.md` — provider, shell, surfaces, depth, motion.
|
|
15
|
+
- `.claude/rules/basalt-controls.md` — where a control lives and what sizes it.
|
|
16
|
+
- `.claude/rules/basalt-charts.md` — the chart contract. `/basalt-charts` is the how-to.
|
|
17
|
+
- `./DESIGN.md` — this app's deltas: identity confirmation, series dictionary, deviations.
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
Google-spec convention (YAML token front matter + Markdown prose, sibling to `CLAUDE.md`).
|
|
19
|
-
2. **Shipped `basalt-*` rules** (`.claude/rules/basalt-{tokens,charts,mantine,state}.md`) — the
|
|
20
|
-
universal law AND its enforcement: the Basalt modern-zinc palette + saturated sky accent,
|
|
21
|
-
one-hue-per-metric, neutral structure, gradient defaults, the three-tier token contract, the
|
|
22
|
-
elevation/density doctrine.
|
|
23
|
-
3. **This skill** — only the loop and the judgment, where the above are silent.
|
|
19
|
+
**No `DESIGN.md` means the app was never scaffolded** — run `/basalt-app` first. Never invent a
|
|
20
|
+
palette ad hoc.
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
(it wraps `basalt-ui init`, which writes the thin `DESIGN.md` and the `basalt-*` rules). Do not invent
|
|
27
|
-
a palette ad hoc.
|
|
22
|
+
## 2. Decide before coding
|
|
28
23
|
|
|
29
|
-
|
|
24
|
+
Answer these four in one pass; each one is a decision, not a preference:
|
|
30
25
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
1. **What earns a hue here?** Trend, signal/status, or genuine categorical separation. Everything
|
|
27
|
+
else is neutral. One hue per metric, stable across every view.
|
|
28
|
+
2. **Where does each control live?** One home per control, and the home sets its size (law C1/C5).
|
|
29
|
+
3. **What is the one primary action on this view?** Exactly one filled accent; everything else
|
|
30
|
+
neutral.
|
|
31
|
+
4. **What does this surface reuse?** Check the shipped composite FIRST — `StatCard`, `ChartCard`,
|
|
32
|
+
`Section`, `BasaltDataTable`, `QueryState`, `EmptyState`. A fork by a token-fluent author passes
|
|
33
|
+
every gate, so no guard will tell you; asking is the control. If a composite genuinely cannot
|
|
34
|
+
express the case, that is a gap to REPORT, not to route around.
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
Material / AntD / Tailwind primaries (they read childish and clash). A small harmonious subset
|
|
37
|
-
across every page so tabs feel like one app.
|
|
38
|
-
- **Single hue per metric.** Each metric owns ONE color, stable across all views (HRV always
|
|
39
|
-
violet, bench always amber). Series colors are app-side and distinct from the UI accent — don't
|
|
40
|
-
reuse the sky accent as a metric hue. Multiple colors only for genuinely categorical data
|
|
41
|
-
(sleep stages, source breakdown). A line chart almost never needs more than one hue plus neutrals.
|
|
42
|
-
- **Neutral structure, colored signal.** Lines/axes/grids in muted neutrals; color is the data and
|
|
43
|
-
status (good/warn/bad). A single neutral line with a soft tinted area reads more premium than a
|
|
44
|
-
rainbow.
|
|
45
|
-
- **Gradients on areas, not everything.** A soft single-hue vertical gradient under a line is the
|
|
46
|
-
canonical "modern" move (`AreaGradient`). Keep stacked-area bands opaque — transparency leaks
|
|
47
|
-
lower bands.
|
|
48
|
-
- **Quiet chrome.** Hairline grids (~6–8% neutral), thin crosshairs, restrained tooltips with a
|
|
49
|
-
card surface and one accent swatch per row. Generous whitespace.
|
|
50
|
-
- **Motion restraint (same doctrine, applied to animation).** Subtle and purposeful, never
|
|
51
|
-
decorative — a state change earns a transition, idle chrome does not. Reach for the shared
|
|
52
|
-
`MOTION_DURATION` / `MOTION_SPRING` tokens (`basalt-ui`'s `src/motion`) instead of inventing
|
|
53
|
-
per-component durations/easings, so animated chrome feels like one identity the same way the
|
|
54
|
-
`--vx-*` palette does for color. Never a looping/pulsing idle animation. Always respect
|
|
55
|
-
`useReducedMotion` (`@mantine/hooks`) — an animated component needs a real unanimated code path,
|
|
56
|
-
not just `duration: 0`.
|
|
36
|
+
## 3. Build it
|
|
57
37
|
|
|
58
|
-
|
|
38
|
+
Compose Mantine primitives and shipped composites; pull every color from `VX.*` / a series token.
|
|
39
|
+
Let the theme carry size, radius, depth and font — a prop you did not have to pass is the point.
|
|
59
40
|
|
|
60
|
-
|
|
61
|
-
(Tailwind zinc family) on both schemes, a slightly darker page with panels lifting off it via depth
|
|
62
|
-
(a whisper shadow + 1px ring, `shadow-card` — never a bare border), and one saturated sky-blue
|
|
63
|
-
accent. Restraint is now about _placement_, not _saturation_ — the accent is louder than the old
|
|
64
|
-
muted-slate identity, so the discipline below (where it appears / where it never appears) carries
|
|
65
|
-
more of the weight.
|
|
41
|
+
## 4. Tune by eye, then bake
|
|
66
42
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
- **Dark:** bg `color-mix(in srgb, #27272a 70%, #18181b)` (#232326), panel `#27272a`, panel-hover
|
|
71
|
-
`color-mix(in srgb, #3f3f46 50%, #27272a)` (#333338), hairline (card ring)
|
|
72
|
-
`color-mix(in srgb, #52525c 50%, #3f3f46)` (#494951), line (strong border) `#3f3f46`. Ink
|
|
73
|
-
`#e5e5e5`, muted `#d4d4d4`, faint `#a1a1a1`.
|
|
74
|
-
- **Light:** bg `color-mix(in srgb, #f4f4f5 50%, #e4e4e7)` (#ececee), panel `#f4f4f5`, panel-hover
|
|
75
|
-
`#fafafa`, hairline `#e5e5e5`, line (strong border) `color-mix(in srgb, #e4e4e7 50%, #d4d4d8)`
|
|
76
|
-
(#dcdce0). Ink `#262626`, muted `#525252`, faint `#737373`.
|
|
77
|
-
|
|
78
|
-
The surface token set is `bg` (page) · `panel` (cards, controls) · `panel-hover` · `line` (strong
|
|
79
|
-
border) · `hairline` (card ring, lives inside `shadow-card`, never rendered as a bare `border`) ·
|
|
80
|
-
`divider` (layout separators).
|
|
81
|
-
|
|
82
|
-
**One accent, saturated sky-blue — split by ROLE.** As **ink** (links, active-nav icon, chart lines,
|
|
83
|
-
focus ring) it is read against the page, so it inverts: `#0077bd` light / `#8ec5ff` dark. As a
|
|
84
|
-
**filled surface** it carries a label, so it is squeezed between that label (≥4.5:1) and the page
|
|
85
|
-
behind it (≥3:1) — which leaves one luminance band, the same on both pages: `#0077bd` with a WHITE
|
|
86
|
-
label, in BOTH schemes. Fill with `--vx-accent-fill` / `--vx-fill-{family}`, never with `--vx-accent`
|
|
87
|
-
(a light fill cannot carry white text). Every Mantine family's fill sits in that same band, so a
|
|
88
|
-
filled surface always reads white. With `accent-hover` (derived from the fill) and `on-accent`
|
|
89
|
-
companions (NOT the old ~50%-saturation muted slate).
|
|
90
|
-
|
|
91
|
-
The accent appears on: the primary data series, active-nav **icons** and active **child** labels,
|
|
92
|
-
links, primary buttons, focus rings, and the leader bar in meters. It stays off: general chrome,
|
|
93
|
-
borders, large fills, every icon, secondary buttons. "Ink earns its color" — neutral does the
|
|
94
|
-
structure, the accent only points.
|
|
95
|
-
|
|
96
|
-
- **Active nav = an ink-9% tint + accent-colored icon + weight-600 ink text** — no panel fill, no
|
|
97
|
-
shadow, and never a full accent fill. It's a selected row, not a raised control. The theme bakes
|
|
98
|
-
this into `NavLink` for every render path — don't re-color it.
|
|
99
|
-
- **Buttons:** exactly one filled-accent primary CTA per view; every other action is
|
|
100
|
-
`variant="default"` (neutral). Avoid colored `variant="light"` for routine actions.
|
|
101
|
-
- **Status:** positive deltas `color="green"` (forest), never `teal`/turquoise/saturated emerald.
|
|
102
|
-
- **Dark done right:** cool zinc, neutral/faint-cool, never steel-blue, never pure black; the
|
|
103
|
-
dark-scheme accent (`#8ec5ff`) is lighter/less saturated than its light counterpart so it doesn't
|
|
104
|
-
glow/bleed.
|
|
105
|
-
- **Light done right:** cool zinc page + panel lift (never warm/off-white); depth comes from
|
|
106
|
-
`shadow-card`, not a bare hairline border.
|
|
107
|
-
|
|
108
|
-
### Density & surface consistency
|
|
109
|
-
|
|
110
|
-
- **Dense by default** (Linear/Notion): compact nav rows, `sm` (12px) gaps and card padding, a
|
|
111
|
-
48px shell header. Separate by surface + `shadow-card`, not by large air — default to the
|
|
112
|
-
tighter step.
|
|
113
|
-
- **All cards render identically:** Mantine `Card`/`Paper` **and** the Mantine-free `ChartCard`
|
|
114
|
-
resolve to **one radius token** (`--vx-radius-card` = 7px) and **`shadow-card`** — a whisper
|
|
115
|
-
shadow with the 1px ring baked into the shadow itself, **no `border` property**. Never
|
|
116
|
-
inline-override a surface's `border`/`borderRadius`/`boxShadow`/`backgroundColor` — use the
|
|
117
|
-
radius token + `VX.surface.*` / the shadow-card token. Mechanically enforced by
|
|
118
|
-
`basalt-ui check-theme`'s `raw-surface` guard.
|
|
119
|
-
- **Depth splits control-vs-panel-vs-floating** — three tokens, not one. Panels holding content
|
|
120
|
-
(Card/Paper/Notification/ChartCard) take `shadow-card`; anything you click or type into (Button,
|
|
121
|
-
ActionIcon, inputs, chips, the search trigger) takes `shadow-raised`, whose ring is INSET so one
|
|
122
|
-
value rides a fill, a tint and a panel alike; detached floating surfaces (Tooltip/Popover/Menu/
|
|
123
|
-
Modal/Drawer) take `shadow-overlay`. Depth is static — nothing lifts or gains a shadow on hover,
|
|
124
|
-
and emphasis is carried by fill weight, never z-height. Full doctrine in the `basalt-mantine`
|
|
125
|
-
rule.
|
|
126
|
-
- **Strict surfaces & primitives.** The theme runs a strict surface system: Mantine components read
|
|
127
|
-
raw ramp steps directly, so `cssVariablesResolver` **collapses the ramp steps onto the
|
|
128
|
-
`--vx-surface-*` tokens** — one depth token per tier, one card bg, one radius across every
|
|
129
|
-
component (AppShell, Table, Input, Divider, Tabs, Popover, Accordion, cards). Consumer code must
|
|
130
|
-
**use Mantine layout primitives** (`Box`/`Flex`/`Grid`/`SimpleGrid`/`Stack`/`Group`/`Paper`/`Card`),
|
|
131
|
-
not raw `<div>`/`<span>` with inline `style`. `check-theme` adds five guard kinds to enforce this
|
|
132
|
-
(default ON, `theme-allow` escape): `off-system-surface-var` (raw ramp-step vars),
|
|
133
|
-
`mantine-shade-index` (a shade-pinned color — `c="yellow.7"` is one fixed swatch in both schemes;
|
|
134
|
-
use `VX.status.*` or the bare hue name), `raw-html-layout`, `inline-spacing`, `inline-display`.
|
|
135
|
-
Only the Mantine-free `src/charts/**` may use raw `<div>` (still with `VX.*` tokens).
|
|
136
|
-
- **Reach for the shipped composite before wrapping one.** A `StatCard` carrying a threshold verdict
|
|
137
|
-
takes `tone="good" | "warn" | "bad"` (an accent rail down its leading edge). Omitting `tone` is not
|
|
138
|
-
the same as `"good"` — omitted means "fine, or nothing measured" and stays untinted, so `"good"` is
|
|
139
|
-
a positive assertion you opt into for a value that earned it (`0 min` downtime), never a default.
|
|
140
|
-
A chart goes in `ChartCard`. Wrapping
|
|
141
|
-
a shipped card in a hand-positioned `Box` to add a mark it already has is how an app grows a second
|
|
142
|
-
card idiom — one the guard cannot recognize as a card, and therefore cannot police. If a composite
|
|
143
|
-
genuinely can't express what you need, that is a gap to report, not to route around.
|
|
144
|
-
|
|
145
|
-
**Anti-slop checklist:** no pure `#000`/`#fff`; one accent locked page-wide; neutral does the
|
|
146
|
-
structure, accent only points; don't flood blue; hierarchy via scale/weight/contrast/space, not
|
|
147
|
-
color; depth = `shadow-card` (whisper shadow + ring), never a bare hairline border on cards.
|
|
148
|
-
|
|
149
|
-
## The three-tier token system (centralize + enforce)
|
|
150
|
-
|
|
151
|
-
Aesthetics only survive if they cannot be bypassed. basalt-ui ships this as the `./tokens` and
|
|
152
|
-
`./charts` surfaces:
|
|
153
|
-
|
|
154
|
-
1. **Palette data** — the Basalt modern-zinc ramps + the saturated sky accent family, plus
|
|
155
|
-
every semantic/status/neutral/surface entry as a per-theme `{ light, dark }` pair. Pure data: no
|
|
156
|
-
React, no `@mantine/*`, no browser API.
|
|
157
|
-
2. **CSS variables** — `buildPaletteCss(opts)` emits the pairs as `--vx-*` custom properties under
|
|
158
|
-
the light/dark color-scheme selector. Theme resolution is **pure CSS** — no JS branching, works
|
|
159
|
-
in non-component files.
|
|
160
|
-
3. **Token refs** — `VX.*` are just `var(--vx-*)` strings (colors) plus non-color sizing constants.
|
|
161
|
-
Opacity via `alpha(token, a)` (= `color-mix`), **never** `rgba()` — the hue must keep resolving
|
|
162
|
-
per scheme.
|
|
163
|
-
|
|
164
|
-
Hard rules (the guard enforces these, not just prose):
|
|
165
|
-
|
|
166
|
-
- **Never a raw hex / `rgb()` / `hsl()` in chart or app source.** Route every color through
|
|
167
|
-
`VX.*` / the palette. `basalt-ui check-theme` fails the build on a literal; a `theme-allow` comment
|
|
168
|
-
is the only deliberate exception.
|
|
169
|
-
- **Never `localStorage.getItem('theme')`.** The scheme resolves via Mantine's color scheme + the
|
|
170
|
-
`--vx-*` vars. The guard bans this read.
|
|
171
|
-
- New colors go into the palette as `{ light, dark }` pairs — never inline, never two
|
|
172
|
-
`fooLight`/`fooDark` keys.
|
|
173
|
-
|
|
174
|
-
```ts
|
|
175
|
-
import { VX, alpha } from 'basalt-ui/tokens'
|
|
176
|
-
import { SERIES } from '../lib/series' // consumer-owned — see /basalt-charts
|
|
177
|
-
|
|
178
|
-
const stroke = SERIES.hrv // app-side series ref (consumer-defined — see /basalt-charts)
|
|
179
|
-
const fill = alpha(SERIES.hrv, 0.12) // theme-aware soft area, NOT rgba()
|
|
180
|
-
const grid = VX.grid // framework neutral
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
## Drive the theme-lab loop (tune by eye, then bake)
|
|
184
|
-
|
|
185
|
-
Static HTML POCs do not translate 1:1 to visx/Mantine. Tune in the **real app** with the shipped
|
|
186
|
-
theme lab (`basalt-ui/theme-lab`), then bake the values into the palette. The lab writes overrides
|
|
187
|
-
as inline `--vx-*` styles on `<html>`, which beat the stylesheet's per-scheme rules — so everything
|
|
188
|
-
restyles **instantly with no React re-render**.
|
|
189
|
-
|
|
190
|
-
Wire it (DEV-only) inside a dev dock popover:
|
|
43
|
+
A static POC never translates 1:1 to visx + Mantine. Tune in the REAL app with the shipped lab
|
|
44
|
+
(`basalt-ui/theme-lab`), which writes inline `--vx-*` overrides on `<html>` — they beat the
|
|
45
|
+
stylesheet, so everything restyles instantly with no React re-render.
|
|
191
46
|
|
|
192
47
|
```tsx
|
|
193
48
|
import { ThemeLabControls, applyOverrides, loadOverrides } from 'basalt-ui/theme-lab'
|
|
194
49
|
|
|
195
|
-
//
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
// In the dev dock — pass the consumer's own series groups so they're tunable too:
|
|
199
|
-
<ThemeLabControls
|
|
200
|
-
groups={[
|
|
201
|
-
...COLOR_GROUPS, // framework: Semantic / Status / Neutral / Surface
|
|
202
|
-
{ title: 'Health', items: [{ var: '--vx-hrv', label: 'HRV' }, /* ... */] },
|
|
203
|
-
]}
|
|
204
|
-
onCopy={(json) => notifications.show({ message: 'Overrides copied' })}
|
|
205
|
-
copyIcon={<IconCopy size={16} />} // framework ships no icon/notification dep
|
|
206
|
-
resetIcon={<IconRefresh size={16} />}
|
|
207
|
-
/>
|
|
50
|
+
applyOverrides(loadOverrides()) // at boot — the control body does NOT do the initial apply
|
|
51
|
+
;<ThemeLabControls groups={[...COLOR_GROUPS, appSeriesGroup]} onCopy={notifyCopied} />
|
|
208
52
|
```
|
|
209
53
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
-
|
|
230
|
-
-
|
|
231
|
-
|
|
232
|
-
-
|
|
233
|
-
`<AxisLeft>`/`<AxisBottom>` (they miss theme tokens + smart ticks). Now lint-enforced:
|
|
234
|
-
`basalt-ui check-theme`'s `raw-visx-axis` guard fails the build on a raw axis inside a `/charts/`
|
|
235
|
-
file (escape via `theme-allow`).
|
|
236
|
-
- **The cursor is shared by default** — `CartesianChart` wires the hover overlay, crosshair, dots
|
|
237
|
-
and tooltip itself, and every chart on the page tracks one cursor through a module-level store
|
|
238
|
-
with no provider. Reach for `useChartCursor` only in a hand-composed non-single-plot shape, and
|
|
239
|
-
for **`ChartCursorScope`** only to opt a subtree OUT of sharing.
|
|
240
|
-
- **`AreaGradient`** / `areaFillUrl` for the soft single-hue fill.
|
|
241
|
-
|
|
242
|
-
Shipped kinds beyond `ZonedLine` / `Bars` / `StackedArea` / `Donut`: **`MultiLine`** (N series on a
|
|
243
|
-
shared y-axis, legend-hover dimming, dashed MA companions, per-point markers, zones/refLines, fixed
|
|
244
|
-
or auto domain — also z-score/σ via a symmetric domain + zero refLine), **`DualPanel`** (line pane
|
|
245
|
-
|
|
246
|
-
- signed-histogram pane on one x-scale and cursor, optional fill-between), **`Heatmap`**
|
|
247
|
-
(category×category intensity grid with per-cell tooltip + optional gradient legend strip).
|
|
248
|
-
|
|
249
|
-
> If the new chart does not fit the primitives, **add a kind — don't loosen the primitives.**
|
|
250
|
-
> Recurring shape → extract a kind via the Rule of Three. Genuinely unique → stay bespoke,
|
|
251
|
-
> composing primitives directly. New color → a `{ light, dark }` pair in the palette, wired through
|
|
252
|
-
> `--vx-*`, exposed as a `VX.*` ref — never an inline hex. The mechanics of all three live in
|
|
253
|
-
> **`/basalt-charts`** — defer to it for the "how".
|
|
254
|
-
|
|
255
|
-
The Mantine-free boundary holds here too: `./charts` and `./tokens` import zero `@mantine/*` (an
|
|
256
|
-
internal invariant keeping the token layer upstream of Mantine, and letting those two subpaths
|
|
257
|
-
resolve/render with no `@mantine/*` installed — CI-tested, repo-local only, not shipped), and
|
|
258
|
-
`@visx/*` is only allowed under chart files (oxlint-enforced in the shipped consumer preset too).
|
|
259
|
-
|
|
260
|
-
## Checklist before declaring a chart "done"
|
|
261
|
-
|
|
262
|
-
- Every color comes from a token (`VX.*` / palette) — `basalt-ui check-theme` green, zero raw hex.
|
|
263
|
-
- One hue per metric; categorical multi-color only where the data is genuinely categorical.
|
|
264
|
-
- Neutral line/axis/grid; soft `AreaGradient` on plain metric lines, opaque stacked-area bands.
|
|
265
|
-
- Tooltip = `ChartCard` / `ChartTooltipFloat` primitives, one swatch per row — no hand-rolled markup.
|
|
266
|
-
- Looks right in BOTH schemes (toggled and checked: glow on dark, contrast on light).
|
|
267
|
-
- New colors landed as `{ light, dark }` pairs in the palette, not inline.
|
|
268
|
-
- Nothing reads `localStorage.getItem('theme')`.
|
|
54
|
+
1. Adjust colors and the area-gradient strength by eye — in BOTH schemes.
|
|
55
|
+
2. Toggle the scheme and re-check: a saturated mid-tone glows on dark (step lighter, slightly
|
|
56
|
+
desaturated) and washes out on light (step deeper). Same hue, different shade, **never the same
|
|
57
|
+
value in both**.
|
|
58
|
+
3. Copy the JSON and hand the values back into the palette as `{ light, dark }` pairs — or, for the
|
|
59
|
+
identity itself, into the derive config's knobs rather than a hand-picked hex.
|
|
60
|
+
4. Remove the override and confirm the baked palette matches.
|
|
61
|
+
|
|
62
|
+
`DeriveControls` is the identity/colour knobs (seed + the bounded levels + radius + density) and is
|
|
63
|
+
faithful to `createBasaltTheme`; `ThemeLabControls` is the low-level inspector for the non-derived
|
|
64
|
+
structural tokens. Both are DEV tools and inert in a production build — run the dev server.
|
|
65
|
+
|
|
66
|
+
## 5. Verify before calling it done
|
|
67
|
+
|
|
68
|
+
- `basalt-ui check-theme` green: zero raw hex, zero `rgba()`, nothing reading `localStorage['theme']`.
|
|
69
|
+
- `oxlint .` green on the `basalt/*` rules — no hand-rolled filter (in a slot) and no in-body
|
|
70
|
+
`<Title order={1|2}>`. Hand-rolled section headings are NOT lint-visible (see the `not guarded`
|
|
71
|
+
line in `basalt-controls.md`'s coverage header): grep the diff for `fw={600}` / `<Title order={3`
|
|
72
|
+
headings by hand and replace them with `WidgetHeader` / `Section`.
|
|
73
|
+
- One hue per metric; neutral structure; the accent only points.
|
|
74
|
+
- Checked in BOTH schemes, and below `sm` as well as on a wide viewport.
|
|
75
|
+
- Any new color landed as a `{ light, dark }` pair in the consumer series file, never inline.
|
|
76
|
+
- Nothing you built duplicates a shipped composite.
|
|
@@ -2,48 +2,39 @@
|
|
|
2
2
|
|
|
3
3
|
## basalt-ui (managed — do not hand-edit)
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
**DESIGN.md is law.** `./DESIGN.md` (imported below) records this app's palette identity and series
|
|
33
|
-
dictionary. Precedence: **DESIGN.md > `basalt-*` rules > skills.** When building or restyling any
|
|
34
|
-
UI, that law wins over habit, over library defaults, and over a skill's instinct. The design/charts
|
|
35
|
-
workflows are the managed skills in `.claude/skills/` (`/basalt-design`, `/basalt-charts`) — they
|
|
36
|
-
defer to DESIGN.md.
|
|
5
|
+
Placed by `bunx basalt-ui init` and refreshed by `basalt-ui sync` after every upgrade
|
|
6
|
+
(`sync --check` gates drift in CI). Framework-owned: edit `DESIGN.md` or your own files instead —
|
|
7
|
+
changes here are overwritten on the next sync.
|
|
8
|
+
|
|
9
|
+
**Stack:** React 19 + Mantine v9, themed by `basalt-ui` (`BasaltProvider` + `createBasaltTheme`), Bun
|
|
10
|
+
runtime, oxlint + oxfmt (no ESLint/Biome/Prettier), no Tailwind. Color comes from the three-tier
|
|
11
|
+
`--vx-*` token system — read `VX.*` or a series token, never a raw hex/`rgb()`/`hsl()`. Charts are
|
|
12
|
+
visx via `basalt-ui/charts`. `basalt-ui check-theme` plus the `basalt/*` oxlint rules are the teeth.
|
|
13
|
+
|
|
14
|
+
**Precedence — the only statement of it.** Highest wins; a lower layer fills gaps and never
|
|
15
|
+
overrides a higher one:
|
|
16
|
+
|
|
17
|
+
> consumer `DESIGN.md` (app deltas) > the six shipped `basalt-*` rules > the `basalt-*` skills
|
|
18
|
+
|
|
19
|
+
**All six rules are law** — `basalt-tokens`, `basalt-mantine`, `basalt-charts`, `basalt-state`,
|
|
20
|
+
`basalt-controls`, `basalt-batteries` in `.claude/rules/`, each carrying a generated
|
|
21
|
+
`<!-- basalt:coverage -->` header naming what enforces it and what is only advisory. The skills
|
|
22
|
+
(`/basalt-app`, `/basalt-design`, `/basalt-charts`) are the METHOD that obeys them, never law.
|
|
23
|
+
|
|
24
|
+
**Run the LOCAL bin, not `bunx`** — `./node_modules/.bin/basalt-ui`, or a `package.json` script.
|
|
25
|
+
`bunx` does not re-resolve a cached package, so it can answer for a version you upgraded away from;
|
|
26
|
+
`init` is the one legitimate `bunx` invocation, because nothing is installed yet.
|
|
27
|
+
|
|
28
|
+
**Before guessing an import, read the installed package's machine docs** — `llms.txt` (per-subpath
|
|
29
|
+
import map) and `AGENTS.md`, at the install directory. In a workspace that is under the package that
|
|
30
|
+
depends on basalt, not the repo root; `basalt-ui doctor`'s `basalt-resolves` line prints where.
|
|
37
31
|
|
|
38
32
|
@./DESIGN.md
|
|
39
33
|
|
|
40
|
-
**Restraint override (supersedes `/frontend-design`).** This app is a calm, data-dense
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
`shadow-raised` on controls — never a decorative drop shadow, never a hover lift), neutral
|
|
46
|
-
zinc-by-default with the single saturated accent spent only when earned (trend /
|
|
47
|
-
signal / categorical separation). Restraint **is** the identity.
|
|
34
|
+
**Restraint override (supersedes `/frontend-design`).** This app is a calm, data-dense professional
|
|
35
|
+
surface, not a showcase. Ignore the push toward a "BOLD aesthetic direction", gradient meshes,
|
|
36
|
+
noise/grain and dramatic motion. Here: the shipped three-font system, depth from a whisper shadow
|
|
37
|
+
with a 1px ring, neutral-by-default with the single accent spent only when earned — trend, signal,
|
|
38
|
+
or genuine categorical separation. Restraint **is** the identity.
|
|
48
39
|
|
|
49
40
|
<!-- basalt:end -->
|
|
@@ -1,74 +1,45 @@
|
|
|
1
1
|
# {{APP_NAME}} — Design
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
>
|
|
5
|
-
>
|
|
6
|
-
>
|
|
7
|
-
> neutral-by-default, three-tier `--vx-*` tokens, theme-is-data, the chart primitive contract, the
|
|
8
|
-
> elevation/density/shape doctrine) lives in those rules and the `/basalt:design` skill, and is
|
|
9
|
-
> **not** repeated here. Touch this file only to confirm identity, register the app's series, or
|
|
10
|
-
> record a genuine deviation.
|
|
11
|
-
|
|
12
|
-
## Precedence (when guidance conflicts)
|
|
13
|
-
|
|
14
|
-
This file's deltas win, then the shipped rules, then any skill:
|
|
15
|
-
|
|
16
|
-
1. **This file** (`{{APP_NAME}}` DESIGN.md) — app-specific deltas. Highest authority.
|
|
17
|
-
2. **`basalt-*` rules** (`.claude/rules/basalt-*.md`) — the shipped law and its enforcement.
|
|
18
|
-
3. **Skills** (`/basalt:design`, `/basalt:charts`, `/frontend-design`, …) — generic method, lowest.
|
|
19
|
-
|
|
20
|
-
A skill never overrides this file or the `basalt-*` rules. When a skill's instinct collides with the
|
|
21
|
-
law, the law wins.
|
|
3
|
+
> Seeded by basalt-ui `init`, then owned by you — `sync` never touches it again, and no version is
|
|
4
|
+
> stamped here (run `basalt-ui doctor` for the version; a number written into a file is frozen at
|
|
5
|
+
> scaffold time). It records this app's **deltas only**: the law lives in `.claude/rules/basalt-*.md`,
|
|
6
|
+
> and the precedence between this file, those rules and the skills is stated once in `CLAUDE.md`.
|
|
22
7
|
|
|
23
8
|
## Identity
|
|
24
9
|
|
|
25
|
-
{{APP_NAME}} inherits the basalt-ui identity verbatim
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
law itself — every hex, role split, and enforcement rule — lives in the `basalt-tokens` and
|
|
29
|
-
`basalt-mantine` rules (`.claude/rules/basalt-{tokens,mantine}.md`) and `docs/DESIGN-SPEC.md` in
|
|
30
|
-
the basalt-ui repo; it is **not** restated here. Confirm or restate any intentional identity shift
|
|
31
|
-
below; **silence means "inherits the basalt-ui defaults unchanged."**
|
|
10
|
+
{{APP_NAME}} inherits the basalt-ui identity verbatim, and every value behind it is DERIVED from the
|
|
11
|
+
theme's config knobs (see the `basalt-tokens` rule) — so nothing here restates one. **Silence means
|
|
12
|
+
"inherits the basalt-ui defaults unchanged."**
|
|
32
13
|
|
|
33
|
-
- **Accent hue:** {{ACCENT_HUE}} (default: the
|
|
34
|
-
|
|
35
|
-
|
|
14
|
+
- **Accent hue:** {{ACCENT_HUE}} (default: the shipped seed; a single-series mark stays neutral)
|
|
15
|
+
- **Theme knobs:** _(none — `createBasaltTheme()` bare)_ · record any non-zero `derive`/`fonts`/
|
|
16
|
+
`radius`/`density` level here, with its reason.
|
|
36
17
|
|
|
37
18
|
## Series dictionary
|
|
38
19
|
|
|
39
|
-
The framework owns the
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
consumer; keep it the single source of truth and never inline a hex elsewhere.
|
|
20
|
+
The framework owns the roles and the available hues; this table is the app's **data dictionary** —
|
|
21
|
+
which metric maps to which hue, as `{ light, dark }` pairs wired through `defineSeries()`. It is the
|
|
22
|
+
one design artifact that legitimately lives in the consumer; never inline a color elsewhere.
|
|
43
23
|
|
|
44
|
-
| Series name
|
|
45
|
-
|
|
46
|
-
| _e.g. requests_
|
|
47
|
-
| | | | | |
|
|
24
|
+
| Series name | `defineSeries` key | Role / earned reason |
|
|
25
|
+
| ---------------- | ------------------ | ---------------------------------- |
|
|
26
|
+
| _e.g. requests_ | `requests` | Primary metric (earned trend hue) |
|
|
48
27
|
|
|
49
28
|
```ts
|
|
50
29
|
// {{SERIES_MODULE_PATH}} — the app's guard-exempt series file
|
|
51
30
|
import { defineSeries, groupTokens } from 'basalt-ui/tokens'
|
|
52
|
-
|
|
53
31
|
const SERIES_MAP = defineSeries({
|
|
54
|
-
// requests: { light: '
|
|
32
|
+
// requests: { light: '…', dark: '…' }, // deeper on light, lighter on dark — never one value
|
|
55
33
|
})
|
|
56
|
-
|
|
57
|
-
export const
|
|
58
|
-
|
|
59
|
-
// wire into the provider: <BasaltProvider paletteOptions={{ groups: paletteGroups }} .../>
|
|
60
|
-
// (paletteOptions takes the group map directly — not a CSS string; read `series.requests` in charts)
|
|
34
|
+
export const series = groupTokens('app', SERIES_MAP) // { requests: 'var(--vx-app-requests)', … }
|
|
35
|
+
export const paletteGroups = { 'app-': SERIES_MAP } // the trailing dash is a CSS-var prefix
|
|
36
|
+
// <BasaltProvider paletteOptions={{ groups: paletteGroups }} …/> — then charts read `series.requests`
|
|
61
37
|
```
|
|
62
38
|
|
|
63
|
-
|
|
64
|
-
- One hue per series, drawn from the identity families only. Never raw Material/AntD/Tailwind.
|
|
65
|
-
- A series earns a color only for **trend**, **signal/status**, or **categorical separation**.
|
|
66
|
-
A lone single-series metric stays neutral (`var(--vx-line)`).
|
|
67
|
-
- Light is one shade **deeper**, dark one shade **lighter** — same hue, never the same hex.
|
|
39
|
+
A series earns a color only for trend, signal/status or categorical separation. `/basalt-design` is the tuning loop; `/basalt-charts` is the wiring.
|
|
68
40
|
|
|
69
41
|
## App deviations
|
|
70
42
|
|
|
71
|
-
|
|
72
|
-
empty section is the correct default; do not invent deviations to fill it.
|
|
43
|
+
Intentional departures from the defaults, each with a one-line justification. Empty is correct.
|
|
73
44
|
|
|
74
45
|
- _(none yet)_
|