basalt-ui 1.33.0 → 1.34.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 +26 -0
- package/MIGRATING.md +108 -15
- package/README.md +3 -0
- package/agent/skills/basalt-app/SKILL.md +5 -3
- package/configs/oxlint-basalt.json +33 -0
- package/configs/oxlint-plugin.js +21 -6
- package/configs/oxlint-plugin.test.ts +98 -10
- package/configs/oxlint.json +2 -30
- package/dist/cli/check-theme.d.ts.map +1 -1
- package/dist/cli/check-theme.js +37 -36
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +3 -5
- package/dist/cli/guard-hook.d.ts.map +1 -1
- package/dist/cli/guard-hook.js +6 -34
- package/dist/cli/index.d.ts +29 -61
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +63 -19
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +7 -16
- package/dist/common/vx-families.d.ts +7 -0
- package/dist/common/vx-families.d.ts.map +1 -0
- package/dist/common/vx-families.js +32 -0
- package/dist/common/vx-family.d.ts +11 -0
- package/dist/common/vx-family.d.ts.map +1 -0
- package/dist/common/vx-family.js +12 -0
- package/dist/guard/index.d.ts +2 -2
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +11 -13
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -2
- package/dist/shell/app-mobile-nav.d.ts.map +1 -1
- package/dist/shell/app-mobile-nav.js +2 -1
- package/dist/shell/app-mobile-nav.module.css +12 -0
- package/dist/shell/app-sidebar.d.ts.map +1 -1
- package/dist/shell/app-sidebar.js +32 -36
- package/dist/shell/app-sidebar.module.css +6 -0
- package/dist/shell/index.d.ts +0 -1
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +0 -2
- package/dist/shell/nav-glyph.d.ts +14 -0
- package/dist/shell/nav-glyph.d.ts.map +1 -0
- package/dist/shell/nav-glyph.js +21 -0
- package/dist/shell/nav-types.d.ts +1 -0
- package/dist/shell/nav-types.d.ts.map +1 -1
- package/dist/shell/sidebar-search.d.ts.map +1 -1
- package/dist/shell/sidebar-search.js +1 -3
- package/dist/surfaces.d.ts +2 -2
- package/dist/surfaces.js +2 -2
- package/dist/tokens/index.d.ts +1 -1
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +17 -1
- package/dist/tokens/size-classes.d.ts +12 -0
- package/dist/tokens/size-classes.d.ts.map +1 -1
- package/dist/tokens/size-classes.js +11 -5
- package/llms.txt +4 -4
- package/package.json +1 -1
- package/src/cli/check-theme.ts +60 -37
- package/src/cli/doctor.ts +3 -5
- package/src/cli/guard-hook.ts +8 -35
- package/src/cli/index.ts +102 -91
- package/src/cli/sync.ts +7 -16
- package/src/common/vx-families.ts +36 -0
- package/src/common/vx-family.ts +22 -0
- package/src/guard/index.ts +22 -25
- package/src/index.ts +0 -2
- package/src/shell/app-mobile-nav.module.css +12 -0
- package/src/shell/app-mobile-nav.tsx +8 -1
- package/src/shell/app-sidebar.module.css +6 -0
- package/src/shell/app-sidebar.tsx +52 -50
- package/src/shell/index.tsx +0 -1
- package/src/shell/nav-glyph.tsx +38 -0
- package/src/shell/nav-types.ts +1 -0
- package/src/shell/sidebar-search.tsx +1 -5
- package/src/surfaces.ts +2 -2
- package/src/tokens/index.ts +28 -1
- package/src/tokens/size-classes.ts +18 -5
- package/dist/shell/use-breakpoint.d.ts +0 -10
- package/dist/shell/use-breakpoint.d.ts.map +0 -1
- package/dist/shell/use-breakpoint.js +0 -11
- package/src/shell/use-breakpoint.ts +0 -37
|
@@ -39,6 +39,7 @@ import { assertRequiredProps } from '../common/validate'
|
|
|
39
39
|
import type { BrandConfig, SettingsMenuItem } from './index'
|
|
40
40
|
import type { SidebarBlock, SidebarItem, SidebarSection } from './nav-types'
|
|
41
41
|
import { NavCountBadge } from './nav-count-badge'
|
|
42
|
+
import { iconOrInitial } from './nav-glyph'
|
|
42
43
|
import { SidebarAccount } from './app-sidebar-account'
|
|
43
44
|
import type { AccountMenuItem, BasaltAccountProps } from './account-types'
|
|
44
45
|
import { SidebarSearch } from './sidebar-search'
|
|
@@ -190,12 +191,24 @@ function hasActiveDescendant(item: SidebarItem): boolean {
|
|
|
190
191
|
* element. That is what replaced the old render callback — a callback had to re-derive basalt's
|
|
191
192
|
* own styling to look right, and every consumer got it subtly wrong.
|
|
192
193
|
*/
|
|
193
|
-
function NavLinkBody({
|
|
194
|
+
function NavLinkBody({
|
|
195
|
+
item,
|
|
196
|
+
active,
|
|
197
|
+
collapsed = false,
|
|
198
|
+
}: {
|
|
199
|
+
item: SidebarItem
|
|
200
|
+
active: boolean
|
|
201
|
+
collapsed?: boolean
|
|
202
|
+
}) {
|
|
194
203
|
const Anchor = item.Anchor
|
|
195
204
|
const shared = {
|
|
196
205
|
classNames: { root: classes.link },
|
|
197
206
|
label: item.label,
|
|
198
|
-
leftSection: item.icon,
|
|
207
|
+
leftSection: iconOrInitial(item.icon, item.label),
|
|
208
|
+
// Only the collapsed rail hides the label (CSS), leaving the icon alone — name the link there.
|
|
209
|
+
// Expanded, the visible text IS the name and carries the count badge; an `aria-label` would
|
|
210
|
+
// override both and drop the count ("Inbox 5" → "Inbox").
|
|
211
|
+
...(collapsed && { 'aria-label': item.label }),
|
|
199
212
|
rightSection: item.badge ?? (item.count ? <NavCountBadge count={item.count} /> : undefined),
|
|
200
213
|
active,
|
|
201
214
|
// Basalt is the one that stamps `aria-current`, not the theme and not a router `<Link>`'s own
|
|
@@ -215,6 +228,32 @@ function NavLinkBody({ item, active }: { item: SidebarItem; active: boolean }) {
|
|
|
215
228
|
return <NavLink component="a" {...(item.href !== undefined && { href: item.href })} {...shared} />
|
|
216
229
|
}
|
|
217
230
|
|
|
231
|
+
/**
|
|
232
|
+
* One child row, shared by the inline child list and the hover popover. Never in the rail (the
|
|
233
|
+
* inline list is hidden there and the popover shows labels), so it carries no `aria-label`.
|
|
234
|
+
*/
|
|
235
|
+
function ChildRow({ child }: { child: SidebarItem }) {
|
|
236
|
+
if (!child.disabled) {
|
|
237
|
+
return (
|
|
238
|
+
<Box>
|
|
239
|
+
<NavLinkBody item={child} active={Boolean(child.active)} />
|
|
240
|
+
</Box>
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
return (
|
|
244
|
+
<Tooltip label="Coming soon" position="right" withArrow>
|
|
245
|
+
<Box>
|
|
246
|
+
<NavLink
|
|
247
|
+
classNames={{ root: classes.link }}
|
|
248
|
+
label={child.label}
|
|
249
|
+
leftSection={iconOrInitial(child.icon, child.label)}
|
|
250
|
+
data-disabled
|
|
251
|
+
/>
|
|
252
|
+
</Box>
|
|
253
|
+
</Tooltip>
|
|
254
|
+
)
|
|
255
|
+
}
|
|
256
|
+
|
|
218
257
|
/**
|
|
219
258
|
* Group label — a micro-label (docs/DESIGN-SPEC.md §3: mono, uppercase, tracked, faint). Typography
|
|
220
259
|
* lives entirely in `.sectionLabel` (app-sidebar.module.css), not Mantine `Text` props, since the
|
|
@@ -304,7 +343,8 @@ function NavItemRow({ item, collapsed }: { item: SidebarItem; collapsed: boolean
|
|
|
304
343
|
<NavLink
|
|
305
344
|
classNames={{ root: classes.link }}
|
|
306
345
|
label={item.label}
|
|
307
|
-
leftSection={item.icon}
|
|
346
|
+
leftSection={iconOrInitial(item.icon, item.label)}
|
|
347
|
+
{...(collapsed && { 'aria-label': item.label })}
|
|
308
348
|
data-disabled
|
|
309
349
|
/>
|
|
310
350
|
</Box>
|
|
@@ -316,7 +356,7 @@ function NavItemRow({ item, collapsed }: { item: SidebarItem; collapsed: boolean
|
|
|
316
356
|
return (
|
|
317
357
|
<Tooltip key={item.key} label={item.label} position="right" withArrow disabled={!collapsed}>
|
|
318
358
|
<Box className={classes.navItem}>
|
|
319
|
-
<NavLinkBody item={item} active={active} />
|
|
359
|
+
<NavLinkBody item={item} active={active} collapsed={collapsed} />
|
|
320
360
|
</Box>
|
|
321
361
|
</Tooltip>
|
|
322
362
|
)
|
|
@@ -331,32 +371,13 @@ function NavItemRow({ item, collapsed }: { item: SidebarItem; collapsed: boolean
|
|
|
331
371
|
<Box key={item.key}>
|
|
332
372
|
<Tooltip label={item.label} position="right" withArrow disabled={!collapsed}>
|
|
333
373
|
<Box className={classes.navItem}>
|
|
334
|
-
<NavLinkBody item={item} active={active} />
|
|
374
|
+
<NavLinkBody item={item} active={active} collapsed={collapsed} />
|
|
335
375
|
</Box>
|
|
336
376
|
</Tooltip>
|
|
337
377
|
<Stack gap={0} className={classes.childList}>
|
|
338
|
-
{item.children!.map((child) =>
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
<Tooltip key={child.key} label="Coming soon" position="right" withArrow>
|
|
342
|
-
<Box>
|
|
343
|
-
<NavLink
|
|
344
|
-
classNames={{ root: classes.link }}
|
|
345
|
-
label={child.label}
|
|
346
|
-
leftSection={child.icon}
|
|
347
|
-
data-disabled
|
|
348
|
-
/>
|
|
349
|
-
</Box>
|
|
350
|
-
</Tooltip>
|
|
351
|
-
)
|
|
352
|
-
}
|
|
353
|
-
const childActive = Boolean(child.active)
|
|
354
|
-
return (
|
|
355
|
-
<Box key={child.key}>
|
|
356
|
-
<NavLinkBody item={child} active={childActive} />
|
|
357
|
-
</Box>
|
|
358
|
-
)
|
|
359
|
-
})}
|
|
378
|
+
{item.children!.map((child) => (
|
|
379
|
+
<ChildRow key={child.key} child={child} />
|
|
380
|
+
))}
|
|
360
381
|
</Stack>
|
|
361
382
|
</Box>
|
|
362
383
|
)
|
|
@@ -383,7 +404,7 @@ function NavItemRow({ item, collapsed }: { item: SidebarItem; collapsed: boolean
|
|
|
383
404
|
onFocus={scheduleOpen}
|
|
384
405
|
onBlur={scheduleClose}
|
|
385
406
|
>
|
|
386
|
-
<NavLinkBody item={item} active={active} />
|
|
407
|
+
<NavLinkBody item={item} active={active} collapsed={collapsed} />
|
|
387
408
|
</Box>
|
|
388
409
|
</Tooltip>
|
|
389
410
|
</Popover.Target>
|
|
@@ -397,28 +418,9 @@ function NavItemRow({ item, collapsed }: { item: SidebarItem; collapsed: boolean
|
|
|
397
418
|
onBlur={scheduleClose}
|
|
398
419
|
>
|
|
399
420
|
<Stack gap={0}>
|
|
400
|
-
{item.children!.map((child) =>
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
<Tooltip key={child.key} label="Coming soon" position="right" withArrow>
|
|
404
|
-
<Box>
|
|
405
|
-
<NavLink
|
|
406
|
-
classNames={{ root: classes.link }}
|
|
407
|
-
label={child.label}
|
|
408
|
-
leftSection={child.icon}
|
|
409
|
-
data-disabled
|
|
410
|
-
/>
|
|
411
|
-
</Box>
|
|
412
|
-
</Tooltip>
|
|
413
|
-
)
|
|
414
|
-
}
|
|
415
|
-
const childActive = Boolean(child.active)
|
|
416
|
-
return (
|
|
417
|
-
<Box key={child.key}>
|
|
418
|
-
<NavLinkBody item={child} active={childActive} />
|
|
419
|
-
</Box>
|
|
420
|
-
)
|
|
421
|
-
})}
|
|
421
|
+
{item.children!.map((child) => (
|
|
422
|
+
<ChildRow key={child.key} child={child} />
|
|
423
|
+
))}
|
|
422
424
|
</Stack>
|
|
423
425
|
</Popover.Dropdown>
|
|
424
426
|
|
package/src/shell/index.tsx
CHANGED
|
@@ -84,7 +84,6 @@ export {
|
|
|
84
84
|
export { AppBreadcrumbs, type AppBreadcrumbsProps } from './app-breadcrumbs'
|
|
85
85
|
export { PageBar, type PageBarProps, type PageBarSlot } from './page-bar'
|
|
86
86
|
export { PageAside, type PageAsideProps, type PageAsideSlot } from './page-aside'
|
|
87
|
-
export { useBreakpoint, type BreakpointName } from './use-breakpoint'
|
|
88
87
|
export { useSizeClass, type SizeClass } from './use-size-class'
|
|
89
88
|
export { PageTitle, type PageTitleProps, type PageTitleSlot } from './page-title'
|
|
90
89
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
/** `Intl.Segmenter` is in every supported runtime; the code-point split is the belt-and-braces fallback. */
|
|
4
|
+
const segmenter =
|
|
5
|
+
typeof Intl !== 'undefined' && typeof Intl.Segmenter === 'function'
|
|
6
|
+
? new Intl.Segmenter(undefined, { granularity: 'grapheme' })
|
|
7
|
+
: undefined
|
|
8
|
+
|
|
9
|
+
/** The first grapheme of a label — the fallback body for an item or action shipping no icon. */
|
|
10
|
+
export function initial(label: string): string {
|
|
11
|
+
const first = segmenter
|
|
12
|
+
? segmenter.segment(label)[Symbol.iterator]().next().value?.segment
|
|
13
|
+
: [...label][0]
|
|
14
|
+
return first?.toUpperCase() ?? '?'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** False for everything React renders as nothing — `cond && <Icon />` yields `false`, not `null`. */
|
|
18
|
+
export function hasIcon(icon: ReactNode): boolean {
|
|
19
|
+
return icon !== null && icon !== undefined && typeof icon !== 'boolean' && icon !== ''
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The label's first grapheme as a decorative glyph. The collapsed rail and the landscape tab bar
|
|
24
|
+
* show the icon ALONE, so an absent icon would render a nameless, empty target. `aria-hidden`: the
|
|
25
|
+
* accessible name comes from the label, never the glyph.
|
|
26
|
+
*/
|
|
27
|
+
export function initialGlyph(label: string): ReactNode {
|
|
28
|
+
return (
|
|
29
|
+
<span aria-hidden data-nav-glyph>
|
|
30
|
+
{initial(label)}
|
|
31
|
+
</span>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** A nav item's icon, or its label's first-letter glyph when it ships none. */
|
|
36
|
+
export function iconOrInitial(icon: ReactNode, label: string): ReactNode {
|
|
37
|
+
return hasIcon(icon) ? icon : initialGlyph(label)
|
|
38
|
+
}
|
package/src/shell/nav-types.ts
CHANGED
|
@@ -31,6 +31,7 @@ export type SidebarItem = {
|
|
|
31
31
|
short?: string
|
|
32
32
|
/** Mobile placement. `true` ≡ `'tab'`, `false` ≡ `'hidden'`. @default 'more' */
|
|
33
33
|
mobile?: boolean | NavMobilePlacement
|
|
34
|
+
/** The collapsed rail and landscape tab bar show the icon alone; `null` falls back to the label's first letter. */
|
|
34
35
|
icon: ReactNode
|
|
35
36
|
/**
|
|
36
37
|
* The router seam. basalt renders every pixel of chrome (desktop row, 56px slot, 44px sheet
|
|
@@ -13,6 +13,7 @@ import { OverflowMenu } from '../controls/actions'
|
|
|
13
13
|
import type { BarAction } from '../controls/actions'
|
|
14
14
|
import { cx } from '../common/props'
|
|
15
15
|
import type { BasaltProps } from '../common/props'
|
|
16
|
+
import { initial } from './nav-glyph'
|
|
16
17
|
import classes from './sidebar-search.module.css'
|
|
17
18
|
|
|
18
19
|
/**
|
|
@@ -55,11 +56,6 @@ function IconSearch() {
|
|
|
55
56
|
)
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
/** The first grapheme of a label — the fallback body for an action shipping no icon. */
|
|
59
|
-
function initial(label: string): string {
|
|
60
|
-
return [...label][0]?.toUpperCase() ?? '?'
|
|
61
|
-
}
|
|
62
|
-
|
|
63
59
|
/**
|
|
64
60
|
* One trailing action as an icon-only button. `size="icon"` is EXPLICIT here, not inherited: the
|
|
65
61
|
* search row is chrome, not a control home, so no `CtlSlot` wraps it and nothing else would resolve
|
package/src/surfaces.ts
CHANGED
|
@@ -299,7 +299,7 @@ export const SURFACES = {
|
|
|
299
299
|
"no second cssVariablesResolver — don't hand-build createTheme or re-add the resolver basalt already installs",
|
|
300
300
|
],
|
|
301
301
|
description:
|
|
302
|
-
'BasaltProvider, createBasaltTheme, BasaltShell + sidebar/mobile-nav/breadcrumbs, PageBar, PageAside, useSizeClass
|
|
302
|
+
'BasaltProvider, createBasaltTheme, BasaltShell + sidebar/mobile-nav/breadcrumbs, PageBar, PageAside, useSizeClass, NavCountBadge, ThemeToggle, ThreadWorkspace + thread-chat components, WidgetHeader, dashboard composites (DeltaBadge, StatCard with threshold tone, EmptyState, QueryState/LoadingState/ErrorState, SettingsSection/SettingsRow/DangerZone), ConnectivityProvider/useConnectivity/ConnectivityIndicator (auto-mounted, C1: absorbed from the dropped ./connectivity), createBasaltQueryClient/unwrap/BasaltQueryDevtools (C1: absorbed from the dropped ./query), BasaltDevDock (C5: lazy Router/Query devtools + theme lab in one dock)',
|
|
303
303
|
optionalPeers: [
|
|
304
304
|
'react-markdown',
|
|
305
305
|
'remark-gfm',
|
|
@@ -662,7 +662,7 @@ export const SURFACES = {
|
|
|
662
662
|
kind: 'tooling',
|
|
663
663
|
layer: 'non-js-asset',
|
|
664
664
|
description:
|
|
665
|
-
'Raw toolchain presets for consumer extends: oxlint.json, oxfmt.json, tsconfig.*.json, lefthook.yml, check.yml',
|
|
665
|
+
'Raw toolchain presets for consumer extends: oxlint.json (or the guards-only oxlint-basalt.json), oxfmt.json, tsconfig.*.json, lefthook.yml, check.yml',
|
|
666
666
|
forbiddenImports: [],
|
|
667
667
|
},
|
|
668
668
|
'./llms.txt': {
|
package/src/tokens/index.ts
CHANGED
|
@@ -22,6 +22,8 @@ import {
|
|
|
22
22
|
import type { PaletteData, RadiusValues, SpaceValues } from './palette'
|
|
23
23
|
import { SIZE_CLASSES, sizeClassMaxEm } from './size-classes'
|
|
24
24
|
import { HIT_COARSE, HIT_FINE } from '../common/hit-floor'
|
|
25
|
+
import { BASALT_VX_FAMILIES } from '../common/vx-families'
|
|
26
|
+
import { vxFamilyOf } from '../common/vx-family'
|
|
25
27
|
|
|
26
28
|
// The raw hue families + pair-picker — the building blocks a consumer's series module composes
|
|
27
29
|
// (`hrv: p(BP.blue)`). The doctrine sends every consumer here, so they are public surface, not
|
|
@@ -57,6 +59,7 @@ export type { SpaceValues } from './palette'
|
|
|
57
59
|
export {
|
|
58
60
|
CONTAINER_CLASSES,
|
|
59
61
|
CONTAINER_GRID_BREAKPOINTS,
|
|
62
|
+
CONTAINER_KEYS,
|
|
60
63
|
resolveContainerClass,
|
|
61
64
|
SIZE_CLASSES,
|
|
62
65
|
} from './size-classes'
|
|
@@ -805,6 +808,26 @@ function frameworkDerived(data: PaletteData, only: 'core' | 'all', legacyAliases
|
|
|
805
808
|
return legacyAliases ? `${canonical}\n${frameworkDerivedAliases(data)}` : canonical
|
|
806
809
|
}
|
|
807
810
|
|
|
811
|
+
const warnedVxNames = new Set<string>()
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* Warn, once per name, on a consumer series/group name inside a basalt-owned `--vx-*` family
|
|
815
|
+
* (`surface`, `space`, `ink`, …): `check-theme`'s `unknown-vx-token` judges every name there against
|
|
816
|
+
* what basalt emits, which is sound only while no consumer REF points there. A WARNING for the
|
|
817
|
+
* minor that kind is graced in (1.34.x), dev builds only; `grace.test.ts` pins the message's
|
|
818
|
+
* version to the ledger's `promote` and requires a throw once the kind promotes.
|
|
819
|
+
* The families are the guard's own generated `BASALT_VX_FAMILIES`. Checked on the refs, not in
|
|
820
|
+
* `buildPaletteCss`, which runs on every first paint.
|
|
821
|
+
*/
|
|
822
|
+
function warnConsumerVxName(name: string): void {
|
|
823
|
+
const family = vxFamilyOf(name)
|
|
824
|
+
if (!BASALT_VX_FAMILIES.includes(family) || warnedVxNames.has(name)) return
|
|
825
|
+
warnedVxNames.add(name)
|
|
826
|
+
console.warn(
|
|
827
|
+
`[basalt] ${name} is inside basalt's own --vx-${family} family — name your series group something basalt does not own (groupTokens('app', …)). Throws from 1.35.0.`,
|
|
828
|
+
)
|
|
829
|
+
}
|
|
830
|
+
|
|
808
831
|
/**
|
|
809
832
|
* Wrap a map of `{ name: ColorPair }` into `var(--vx-<prefix><name>)` token refs — the runtime
|
|
810
833
|
* companion to `buildPaletteCss`. Lets a consumer define its own series and read stable refs.
|
|
@@ -817,7 +840,11 @@ export function seriesTokens<const T extends SeriesMap>(
|
|
|
817
840
|
prefix = '',
|
|
818
841
|
): { [K in keyof T]: string } {
|
|
819
842
|
const out = {} as { [K in keyof T]: string }
|
|
820
|
-
for (const key of Object.keys(map) as (keyof T)[])
|
|
843
|
+
for (const key of Object.keys(map) as (keyof T)[]) {
|
|
844
|
+
// Literal read at the branch, so a production bundle drops the check (common/dev-gate.test.ts).
|
|
845
|
+
if (process.env.NODE_ENV !== 'production') warnConsumerVxName(`--vx-${prefix}${String(key)}`)
|
|
846
|
+
out[key] = `var(--vx-${prefix}${String(key)})`
|
|
847
|
+
}
|
|
821
848
|
return out
|
|
822
849
|
}
|
|
823
850
|
|
|
@@ -14,6 +14,19 @@ export const CONTAINER_CLASSES = { micro: 0, compact: 240, regular: 480, wide: 8
|
|
|
14
14
|
|
|
15
15
|
export type ContainerClass = keyof typeof CONTAINER_CLASSES
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* `CONTAINER_CLASSES` as the CSS lengths `<SimpleGrid type="container">` keys take — Mantine writes
|
|
19
|
+
* each key verbatim into `@container simple-grid (min-width: <key>)`, so a theme name (`sm`) is a
|
|
20
|
+
* dead query and the key has to be a px length: `cols={{ base: 1, [CONTAINER_KEYS.regular]: 2 }}`.
|
|
21
|
+
* `micro` has no key — `base` is that class. `basalt/raw-breakpoint` judges theme names only, so a
|
|
22
|
+
* computed key is green.
|
|
23
|
+
*/
|
|
24
|
+
export const CONTAINER_KEYS = {
|
|
25
|
+
compact: `${CONTAINER_CLASSES.compact}px`,
|
|
26
|
+
regular: `${CONTAINER_CLASSES.regular}px`,
|
|
27
|
+
wide: `${CONTAINER_CLASSES.wide}px`,
|
|
28
|
+
} as const
|
|
29
|
+
|
|
17
30
|
/**
|
|
18
31
|
* `CONTAINER_CLASSES` as the five-key map `<Grid type="container" breakpoints={…}>` takes (Mantine
|
|
19
32
|
* types it `Record<MantineSize, string>`), so `Grid.Col` keys resolve onto the grid's own width:
|
|
@@ -21,11 +34,11 @@ export type ContainerClass = keyof typeof CONTAINER_CLASSES
|
|
|
21
34
|
* by name (from `basalt-ui/tokens`) — any other imported map is unreadable to it and flagged.
|
|
22
35
|
*/
|
|
23
36
|
export const CONTAINER_GRID_BREAKPOINTS = {
|
|
24
|
-
xs:
|
|
25
|
-
sm:
|
|
26
|
-
md:
|
|
27
|
-
lg:
|
|
28
|
-
xl:
|
|
37
|
+
xs: CONTAINER_KEYS.compact,
|
|
38
|
+
sm: CONTAINER_KEYS.regular,
|
|
39
|
+
md: CONTAINER_KEYS.wide,
|
|
40
|
+
lg: CONTAINER_KEYS.wide,
|
|
41
|
+
xl: CONTAINER_KEYS.wide,
|
|
29
42
|
} as const
|
|
30
43
|
|
|
31
44
|
/** The class a MEASURED inline size falls in (`px` is a min-width boundary, so 240 is `compact`). */
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export type BreakpointName = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
2
|
-
/**
|
|
3
|
-
* @deprecated Use `useSizeClass` — removed in 1.33.0 (see `MIGRATING.md`). `theme.breakpoints` is
|
|
4
|
-
* now derived: `sm` moved 48em → 52.5em and `md`/`lg`/`xl` all sit at 75em (`xs`/`sm` collapse,
|
|
5
|
-
* `md`/`lg`/`xl` collapse).
|
|
6
|
-
*/
|
|
7
|
-
export declare function useBreakpoint(name: BreakpointName, edge?: 'min' | 'max', options?: {
|
|
8
|
-
fallback?: boolean;
|
|
9
|
-
}): boolean;
|
|
10
|
-
//# sourceMappingURL=use-breakpoint.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"use-breakpoint.d.ts","sourceRoot":"","sources":["../../src/shell/use-breakpoint.ts"],"names":[],"mappings":"AAoBA,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAE7D;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,cAAc,EACpB,IAAI,GAAE,KAAK,GAAG,KAAa,EAC3B,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/B,OAAO,CAKT"}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { useMantineTheme } from "@mantine/core";
|
|
2
|
-
import { useMediaQuery } from "../common/use-media-query.js";
|
|
3
|
-
function useBreakpoint(name, edge = "min", options) {
|
|
4
|
-
const theme = useMantineTheme();
|
|
5
|
-
const bp = theme.breakpoints[name];
|
|
6
|
-
const fallback = options?.fallback ?? edge === "max";
|
|
7
|
-
return useMediaQuery(`(${edge}-width: ${bp})`, fallback);
|
|
8
|
-
}
|
|
9
|
-
export {
|
|
10
|
-
useBreakpoint
|
|
11
|
-
};
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `useBreakpoint` — DEPRECATED (on notice in `MIGRATING.md`, removed in 1.33.0): use
|
|
3
|
-
* `useSizeClass` from `./use-size-class`. One media-query hook over the theme's own breakpoints,
|
|
4
|
-
* built on the shared `useMediaQuery` (`common/use-media-query`) — SSR/hydration-safe.
|
|
5
|
-
*
|
|
6
|
-
* `edge` defaults to `'min'` (`"at least this wide"`); pass `'max'` for `"narrower than"`. The
|
|
7
|
-
* `fallback` (what the server, and a shim with no `matchMedia`, sees) defaults to the answer that
|
|
8
|
-
* assumes the NARROW side of the query — `false` for `min`, `true` for `max` — since a component
|
|
9
|
-
* library cannot know a consumer's real device mix; `options.fallback` overrides it.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* import { useBreakpoint } from 'basalt-ui'
|
|
13
|
-
*
|
|
14
|
-
* const isDesktop = useBreakpoint('sm') // >= theme.breakpoints.sm
|
|
15
|
-
* const isNarrow = !useBreakpoint('sm') // < theme.breakpoints.sm
|
|
16
|
-
* const isWide = useBreakpoint('lg') // >= theme.breakpoints.lg
|
|
17
|
-
*/
|
|
18
|
-
import { useMantineTheme } from '@mantine/core'
|
|
19
|
-
import { useMediaQuery } from '../common/use-media-query'
|
|
20
|
-
|
|
21
|
-
export type BreakpointName = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* @deprecated Use `useSizeClass` — removed in 1.33.0 (see `MIGRATING.md`). `theme.breakpoints` is
|
|
25
|
-
* now derived: `sm` moved 48em → 52.5em and `md`/`lg`/`xl` all sit at 75em (`xs`/`sm` collapse,
|
|
26
|
-
* `md`/`lg`/`xl` collapse).
|
|
27
|
-
*/
|
|
28
|
-
export function useBreakpoint(
|
|
29
|
-
name: BreakpointName,
|
|
30
|
-
edge: 'min' | 'max' = 'min',
|
|
31
|
-
options?: { fallback?: boolean },
|
|
32
|
-
): boolean {
|
|
33
|
-
const theme = useMantineTheme()
|
|
34
|
-
const bp = theme.breakpoints[name]
|
|
35
|
-
const fallback = options?.fallback ?? edge === 'max'
|
|
36
|
-
return useMediaQuery(`(${edge}-width: ${bp})`, fallback)
|
|
37
|
-
}
|