@humanforest/ui 0.1.3 → 0.2.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/package.json +4 -4
- package/preset.gen.js +26 -6
- package/src/themes/checkbox.ts +34 -16
- package/src/themes/index.ts +2 -0
- package/src/themes/progress-group.ts +39 -0
- package/src/themes/splitter.ts +33 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@humanforest/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"src",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"preset.gen.js"
|
|
10
10
|
],
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@humanforest/tokens": "^0.
|
|
12
|
+
"@humanforest/tokens": "^0.2.0",
|
|
13
13
|
"@internationalized/date": "^3.12.2",
|
|
14
14
|
"@mapbox/search-js-core": "^1.6.0",
|
|
15
15
|
"@turf/boolean-point-in-polygon": "^7.4.0",
|
|
@@ -50,12 +50,12 @@
|
|
|
50
50
|
"./kpi/format": "./src/kpi/kpiFormat.ts"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@nuxt/ui": "^4.
|
|
53
|
+
"@nuxt/ui": "^4.11.0",
|
|
54
54
|
"@tailwindcss/vite": "^4.3.3",
|
|
55
55
|
"@vitejs/plugin-vue": "^6.0.8",
|
|
56
56
|
"@vitest/browser": "^4.1.10",
|
|
57
57
|
"playwright": "^1.61.1",
|
|
58
|
-
"reka-ui": "2.10.
|
|
58
|
+
"reka-ui": "2.10.4",
|
|
59
59
|
"tailwindcss": "^4.3.3",
|
|
60
60
|
"vite": "^8.1.5",
|
|
61
61
|
"vitest": "^4.1.10",
|
package/preset.gen.js
CHANGED
|
@@ -28,6 +28,7 @@ __export(exports_themes, {
|
|
|
28
28
|
table: () => tableTheme,
|
|
29
29
|
switch: () => switchTheme,
|
|
30
30
|
stepper: () => stepperTheme,
|
|
31
|
+
splitter: () => splitterTheme,
|
|
31
32
|
slider: () => sliderTheme,
|
|
32
33
|
slideover: () => slideoverTheme,
|
|
33
34
|
skeleton: () => skeletonTheme,
|
|
@@ -38,6 +39,7 @@ __export(exports_themes, {
|
|
|
38
39
|
scrollArea: () => scrollAreaTheme,
|
|
39
40
|
radioGroup: () => radioGroupTheme,
|
|
40
41
|
prose: () => proseTheme,
|
|
42
|
+
progressGroup: () => progressGroupTheme,
|
|
41
43
|
progress: () => progressTheme,
|
|
42
44
|
popover: () => popoverTheme,
|
|
43
45
|
pinInput: () => pinInputTheme,
|
|
@@ -345,16 +347,21 @@ var carouselTheme = {
|
|
|
345
347
|
// packages/ui/src/themes/checkbox.ts
|
|
346
348
|
var checkboxTheme = {
|
|
347
349
|
slots: {
|
|
348
|
-
base: "rounded focus-visible:outline-
|
|
350
|
+
base: "rounded focus-visible:outline-offset-2"
|
|
349
351
|
},
|
|
350
352
|
variants: {
|
|
351
353
|
size: {
|
|
352
|
-
xs: { base: "coarse:size-3.5" },
|
|
353
|
-
sm: { base: "coarse:size-4", container: "coarse:h-5", wrapper: "coarse:text-sm" },
|
|
354
|
-
md: { base: "coarse:size-4.5" },
|
|
355
|
-
lg: { base: "coarse:size-5", container: "coarse:h-6", wrapper: "coarse:text-base" }
|
|
354
|
+
xs: { base: "coarse:size-3.5", icon: "coarse:size-3" },
|
|
355
|
+
sm: { base: "coarse:size-4", icon: "coarse:size-3.5", container: "coarse:h-5", wrapper: "coarse:text-sm" },
|
|
356
|
+
md: { base: "coarse:size-4.5", icon: "coarse:size-4" },
|
|
357
|
+
lg: { base: "coarse:size-5", icon: "coarse:size-4.5", container: "coarse:h-6", wrapper: "coarse:text-base" }
|
|
356
358
|
}
|
|
357
|
-
}
|
|
359
|
+
},
|
|
360
|
+
compoundVariants: [
|
|
361
|
+
{ variant: "list", indicator: ["start", "end"], class: { base: "focus-visible:outline-2" } },
|
|
362
|
+
{ variant: "card", class: { root: "has-focus-visible:outline-2 has-focus-visible:outline-offset-2" } },
|
|
363
|
+
{ variant: "list", indicator: "hidden", class: { root: "has-focus-visible:outline-2 has-focus-visible:outline-offset-2" } }
|
|
364
|
+
]
|
|
358
365
|
};
|
|
359
366
|
// packages/ui/src/themes/checkbox-group.ts
|
|
360
367
|
var checkboxGroupTheme = {
|
|
@@ -999,6 +1006,13 @@ var progressTheme = {
|
|
|
999
1006
|
indicator: "duration-(--motion-slow) ease-(--motion-ease-out) will-change-transform"
|
|
1000
1007
|
}
|
|
1001
1008
|
};
|
|
1009
|
+
// packages/ui/src/themes/progress-group.ts
|
|
1010
|
+
var progressGroupTheme = {
|
|
1011
|
+
slots: {
|
|
1012
|
+
segment: "duration-(--motion-slow) ease-(--motion-ease-out)",
|
|
1013
|
+
status: "duration-(--motion-slow) ease-(--motion-ease-out)"
|
|
1014
|
+
}
|
|
1015
|
+
};
|
|
1002
1016
|
// packages/ui/src/themes/prose.ts
|
|
1003
1017
|
var proseTheme = {
|
|
1004
1018
|
codePreview: {
|
|
@@ -1221,6 +1235,12 @@ var sliderTheme = {
|
|
|
1221
1235
|
}
|
|
1222
1236
|
}
|
|
1223
1237
|
};
|
|
1238
|
+
// packages/ui/src/themes/splitter.ts
|
|
1239
|
+
var splitterTheme = {
|
|
1240
|
+
slots: {
|
|
1241
|
+
handle: "focus-visible:outline-offset-2 focus-visible:outline-[var(--forest-500)]"
|
|
1242
|
+
}
|
|
1243
|
+
};
|
|
1224
1244
|
// packages/ui/src/themes/stepper.ts
|
|
1225
1245
|
var stepperTheme = {
|
|
1226
1246
|
slots: {
|
package/src/themes/checkbox.ts
CHANGED
|
@@ -7,16 +7,24 @@
|
|
|
7
7
|
* while aligning with Forest's overall soft-radius vocabulary (--ui-radius = 0.5 rem).
|
|
8
8
|
* - `label`: font-medium is already stock; we keep it and add nothing — the Nuxt UI default is
|
|
9
9
|
* correct here.
|
|
10
|
-
* -
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* - focus ring: Forest's house convention `outline-2 outline-offset-2` (an offset ring), over
|
|
11
|
+
* stock's flush `outline-3`. The outline COLOUR is stock's semantic value, resolved to the
|
|
12
|
+
* Forest OKLCH ramp via docs.css / forest-preset — no colour override needed, only the width
|
|
13
|
+
* and offset. Where that ring has to be written is the awkward part; see below.
|
|
14
14
|
* - `legend` (CheckboxGroup): font-bold — bold against the regular item labels, giving the
|
|
15
15
|
* group label a clear hierarchy above the individual checkbox labels.
|
|
16
16
|
*
|
|
17
|
-
*
|
|
17
|
+
* ★ THE RING IS A COMPOUND, NOT A SLOT. Stock paints it in two places and neither is a plain slot:
|
|
18
|
+
* on `base` for the `list` variant with a visible box, and on `root` (as `has-focus-visible:`) for
|
|
19
|
+
* `card`, and for `list` once the box is hidden. Both live in stock's compoundVariants, which tv
|
|
20
|
+
* appends AFTER the slots — so a `slots.base` override loses to them and silently does nothing.
|
|
21
|
+
* The width therefore has to be restated at the same layer, in the entries below.
|
|
18
22
|
*
|
|
19
|
-
*
|
|
23
|
+
* The offset is the exception: no stock rule sets `outline-offset`, so the one on `base` still
|
|
24
|
+
* reaches from `slots` and is not repeated. The root entries carry their own, `has-focus-visible:`
|
|
25
|
+
* being a different property from the bare `outline-offset-2` on base.
|
|
26
|
+
*
|
|
27
|
+
* Slots touched: base, root, icon.
|
|
20
28
|
* Slot names sourced from: apps/docs/node_modules/.nuxt-ui/ui/checkbox.ts
|
|
21
29
|
*
|
|
22
30
|
* The CheckboxGroup legend opinion lives in its own sibling fragment (checkbox-group.ts),
|
|
@@ -26,22 +34,32 @@
|
|
|
26
34
|
export const checkboxTheme = {
|
|
27
35
|
slots: {
|
|
28
36
|
// Deltas over stock: `rounded` (4px) for the Forest soft radius (stock rounded-sm/2px), and the
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
base: 'rounded focus-visible:outline-2 focus-visible:outline-offset-2',
|
|
37
|
+
// ring's offset. The WIDTH is not here — it would be overridden; see the compounds below.
|
|
38
|
+
base: 'rounded focus-visible:outline-offset-2',
|
|
32
39
|
},
|
|
33
40
|
// Touch: a checkbox's tap target is the whole label ROW, and at md that row is short (~20px). Under
|
|
34
|
-
// a coarse (finger) pointer each size grows one size up — box (base),
|
|
35
|
-
// label text (wrapper) — so the row becomes a taller, easier target. No sliding
|
|
36
|
-
// is clean (unlike the switch thumb). xl is the ceiling; each size lists only
|
|
41
|
+
// a coarse (finger) pointer each size grows one size up — box (base), tick (icon), row height
|
|
42
|
+
// (container) and label text (wrapper) — so the row becomes a taller, easier target. No sliding
|
|
43
|
+
// part, so the bump is clean (unlike the switch thumb). xl is the ceiling; each size lists only
|
|
44
|
+
// its deltas up, which is why container and wrapper appear on two rungs and the box on four.
|
|
45
|
+
//
|
|
46
|
+
// ★ THE TICK IS SIZED SEPARATELY FROM THE BOX. Stock pins `icon` per size rather than filling the
|
|
47
|
+
// box, so growing `base` alone leaves the tick at its fine-pointer size inside a bigger square.
|
|
37
48
|
// `coarse:` carries the `:not(.cut-fixed)` escape so the mobile cut (baked size lg) opts out.
|
|
38
49
|
variants: {
|
|
39
50
|
size: {
|
|
40
|
-
xs: { base: 'coarse:size-3.5' },
|
|
41
|
-
sm: { base: 'coarse:size-4', container: 'coarse:h-5', wrapper: 'coarse:text-sm' },
|
|
42
|
-
md: { base: 'coarse:size-4.5' },
|
|
43
|
-
lg: { base: 'coarse:size-5', container: 'coarse:h-6', wrapper: 'coarse:text-base' },
|
|
51
|
+
xs: { base: 'coarse:size-3.5', icon: 'coarse:size-3' },
|
|
52
|
+
sm: { base: 'coarse:size-4', icon: 'coarse:size-3.5', container: 'coarse:h-5', wrapper: 'coarse:text-sm' },
|
|
53
|
+
md: { base: 'coarse:size-4.5', icon: 'coarse:size-4' },
|
|
54
|
+
lg: { base: 'coarse:size-5', icon: 'coarse:size-4.5', container: 'coarse:h-6', wrapper: 'coarse:text-base' },
|
|
44
55
|
// xl: ceiling
|
|
45
56
|
},
|
|
46
57
|
},
|
|
58
|
+
// The house width, restated where stock's own compounds would otherwise win. Colour is left to
|
|
59
|
+
// stock, so these carry no `color` discriminator and apply across every ramp.
|
|
60
|
+
compoundVariants: [
|
|
61
|
+
{ variant: 'list', indicator: ['start', 'end'], class: { base: 'focus-visible:outline-2' } },
|
|
62
|
+
{ variant: 'card', class: { root: 'has-focus-visible:outline-2 has-focus-visible:outline-offset-2' } },
|
|
63
|
+
{ variant: 'list', indicator: 'hidden', class: { root: 'has-focus-visible:outline-2 has-focus-visible:outline-offset-2' } },
|
|
64
|
+
],
|
|
47
65
|
} as const;
|
package/src/themes/index.ts
CHANGED
|
@@ -73,6 +73,7 @@ export { paginationTheme as pagination } from './pagination';
|
|
|
73
73
|
export { pinInputTheme as pinInput } from './pin-input';
|
|
74
74
|
export { popoverTheme as popover } from './popover';
|
|
75
75
|
export { progressTheme as progress } from './progress';
|
|
76
|
+
export { progressGroupTheme as progressGroup } from './progress-group';
|
|
76
77
|
// Prose components are namespaced under ui.prose.* — the fragment runs stock, carrying only the
|
|
77
78
|
// `ed-demo` marker the docs editorial grid reads off a demo pane.
|
|
78
79
|
export { proseTheme as prose } from './prose';
|
|
@@ -85,6 +86,7 @@ export { sidebarTheme as sidebar } from './sidebar';
|
|
|
85
86
|
export { skeletonTheme as skeleton } from './skeleton';
|
|
86
87
|
export { slideoverTheme as slideover } from './slideover';
|
|
87
88
|
export { sliderTheme as slider } from './slider';
|
|
89
|
+
export { splitterTheme as splitter } from './splitter';
|
|
88
90
|
export { stepperTheme as stepper } from './stepper';
|
|
89
91
|
export { switchTheme as switch } from './switch';
|
|
90
92
|
export { tableTheme as table } from './table';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forest theme fragment — UProgressGroup (several values as segments of one track, plus a legend).
|
|
3
|
+
*
|
|
4
|
+
* New in Nuxt UI 4.11, and it reaches a Forest consumer whether or not we document it: the layer
|
|
5
|
+
* registers @nuxt/ui wholesale, so the component is typeable the moment the dependency resolves.
|
|
6
|
+
* This fragment exists so that what arrives is not off-brand, not because the system recommends it.
|
|
7
|
+
*
|
|
8
|
+
* Forest opinions applied:
|
|
9
|
+
*
|
|
10
|
+
* segment, status — the motion tokens, replacing stock's hard-coded `duration-200 ease-out`.
|
|
11
|
+
* UProgress next to it already animates on `--motion-slow` / `--motion-ease-out` (progress.ts),
|
|
12
|
+
* and two bars of the same family easing differently is the kind of seam that only ever shows
|
|
13
|
+
* up once both are on screen.
|
|
14
|
+
*
|
|
15
|
+
* We do NOT override:
|
|
16
|
+
* - `base` (the track) — stock's `bg-accented` groove and `rounded-full` are already the Forest
|
|
17
|
+
* reading; radius follows --ui-radius through the preset.
|
|
18
|
+
* - Colour variants — `bg-primary`, `bg-error` and the Forest ramps (forest/maple/river/warm/acid)
|
|
19
|
+
* are registered by forest-preset, and a per-item `color` that is not a registered name is
|
|
20
|
+
* passed through as an inline background, so `var(--dataviz-categorical-safe-N)` works as-is.
|
|
21
|
+
* - `motion-reduce:transition-none` — stock sets it, and the global reduced-motion floor in
|
|
22
|
+
* packages/tokens/motion supersedes it regardless. Restating it would be drift.
|
|
23
|
+
* - The size ladder, the legend slots, and `list`/`item` layout — no Forest opinion here.
|
|
24
|
+
*
|
|
25
|
+
* ★ NOT A REPLACEMENT FOR FDistributionBar. Its bar segments carry no slot, so nothing can hang a
|
|
26
|
+
* tooltip or a focus stop on them, and each segment is its own `role="progressbar"` — a five-part
|
|
27
|
+
* figure announces as five progress bars rather than one. FDistributionBar exists for the
|
|
28
|
+
* composition read: one `role="img"`, one sentence, and optional interactive segments.
|
|
29
|
+
*
|
|
30
|
+
* Slots touched: segment, status.
|
|
31
|
+
* Slot names sourced from: apps/docs/node_modules/.nuxt-ui/ui/progress-group.ts
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
export const progressGroupTheme = {
|
|
35
|
+
slots: {
|
|
36
|
+
segment: 'duration-(--motion-slow) ease-(--motion-ease-out)',
|
|
37
|
+
status: 'duration-(--motion-slow) ease-(--motion-ease-out)',
|
|
38
|
+
},
|
|
39
|
+
} as const;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forest theme fragment — USplitter (resizable panels with a draggable handle).
|
|
3
|
+
*
|
|
4
|
+
* New in Nuxt UI 4.11. Nothing in Forest uses it yet — FAppSidebar resizes through
|
|
5
|
+
* UDashboardSidebar's own `resizable`, and FSidePanel is deliberately two fixed rungs rather than a
|
|
6
|
+
* drag. The fragment is here for the same reason as progress-group's: the component is typeable by
|
|
7
|
+
* any consumer on the layer, so its focus ring should be the house one on arrival.
|
|
8
|
+
*
|
|
9
|
+
* Forest opinions applied:
|
|
10
|
+
*
|
|
11
|
+
* handle — the house focus ring: offset, and pinned to the ramp var rather than the semantic
|
|
12
|
+
* `outline-primary` token, matching link.ts, accordion.ts and carousel.ts. Stock's width is
|
|
13
|
+
* already `outline-2`, so only the offset and the colour are stated here.
|
|
14
|
+
*
|
|
15
|
+
* We do NOT override:
|
|
16
|
+
* - The handle's resting appearance. Stock paints nothing until focus, and that is right for a
|
|
17
|
+
* seam between two panels — see dashboard-resize-handle.ts for the case where Forest does draw
|
|
18
|
+
* a grab affordance, and note it does not port: that handle's box is zero-wide and sits ON the
|
|
19
|
+
* seam, so its pill is positioned against a zero-width edge. This one is a real 2-unit column.
|
|
20
|
+
* - `panel`, `root`, and the orientation cursors — layout and affordance, no Forest opinion.
|
|
21
|
+
*
|
|
22
|
+
* ★ If a split pane is ever built in Forest, it should be built on this rather than hand-rolled,
|
|
23
|
+
* and this fragment will need the resting/hover treatment that decision implies.
|
|
24
|
+
*
|
|
25
|
+
* Slots touched: handle.
|
|
26
|
+
* Slot names sourced from: apps/docs/node_modules/.nuxt-ui/ui/splitter.ts
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
export const splitterTheme = {
|
|
30
|
+
slots: {
|
|
31
|
+
handle: 'focus-visible:outline-offset-2 focus-visible:outline-[var(--forest-500)]',
|
|
32
|
+
},
|
|
33
|
+
} as const;
|