@glasshome/ui 0.5.4 → 1.0.1

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.
Files changed (87) hide show
  1. package/README.md +59 -25
  2. package/dist/index.d.ts +10 -3
  3. package/dist/index.js +9 -9
  4. package/dist/lib/alert-tones.d.ts +18 -0
  5. package/dist/lib/badge-tone.d.ts +2 -0
  6. package/dist/lib/button-variants.d.ts +6 -16
  7. package/dist/lib/card-classes.d.ts +19 -0
  8. package/dist/lib/glass-tone.d.ts +2 -0
  9. package/dist/lib/input-classes.d.ts +2 -0
  10. package/dist/lib/logo-lockup.d.ts +38 -0
  11. package/dist/lib/overlay-classes.d.ts +2 -0
  12. package/dist/lib/section-tokens.d.ts +4 -0
  13. package/dist/lib/tier-badge.d.ts +13 -0
  14. package/dist/solid/alert-dialog.d.ts +1 -3
  15. package/dist/solid/alert.d.ts +2 -11
  16. package/dist/solid/badge.d.ts +11 -46
  17. package/dist/solid/bottom-sheet/bottom-sheet.d.ts +1 -1
  18. package/dist/solid/bottom-sheet/constants.d.ts +0 -1
  19. package/dist/solid/bottom-sheet/drag-controller.d.ts +1 -1
  20. package/dist/solid/bottom-sheet/index.d.ts +1 -2
  21. package/dist/solid/breadcrumb.d.ts +1 -1
  22. package/dist/solid/button-group.d.ts +2 -2
  23. package/dist/solid/button.d.ts +1 -1
  24. package/dist/solid/card.d.ts +10 -2
  25. package/dist/solid/carousel.d.ts +1 -1
  26. package/dist/solid/charts.d.ts +45 -0
  27. package/dist/solid/count-pill.d.ts +10 -0
  28. package/dist/solid/data-table.d.ts +66 -0
  29. package/dist/solid/dock.d.ts +3 -0
  30. package/dist/solid/entity-data.d.ts +52 -0
  31. package/dist/solid/field.d.ts +1 -1
  32. package/dist/solid/form.d.ts +1 -1
  33. package/dist/solid/index.d.ts +74 -64
  34. package/dist/solid/index.js +11050 -10848
  35. package/dist/solid/input-group.d.ts +1 -1
  36. package/dist/solid/item.d.ts +3 -2
  37. package/dist/solid/logo.d.ts +23 -0
  38. package/dist/solid/number-field.d.ts +3 -0
  39. package/dist/solid/overlay.d.ts +3 -0
  40. package/dist/solid/page-header.d.ts +18 -0
  41. package/dist/solid/popover.d.ts +7 -1
  42. package/dist/solid/scope-indicator.d.ts +8 -0
  43. package/dist/solid/section-card.d.ts +54 -0
  44. package/dist/solid/select.d.ts +1 -18
  45. package/dist/solid/settings-row.d.ts +34 -0
  46. package/dist/solid/sliding-indicator.d.ts +32 -0
  47. package/dist/solid/sonner.d.ts +35 -4
  48. package/dist/solid/spinner.d.ts +2 -1
  49. package/dist/solid/switch.d.ts +1 -1
  50. package/dist/solid/toggle-group.d.ts +1 -1
  51. package/dist/solid/widget-card.d.ts +29 -0
  52. package/dist/solid/widget-identity.d.ts +47 -0
  53. package/dist/solid/widget-trust-badge.d.ts +16 -0
  54. package/dist/tokens/index.d.ts +3 -3
  55. package/dist/tokens/index.js +11 -11
  56. package/dist/tokens/presets.d.ts +1 -1
  57. package/dist/{utils-JJqwPy_d.js → utils-gbdcvo14.js} +598 -506
  58. package/package.json +39 -17
  59. package/scripts/check-tokens.ts +60 -0
  60. package/scripts/check-ui-drift.mjs +116 -0
  61. package/src/astro/Alert.astro +63 -0
  62. package/src/astro/Badge.astro +16 -0
  63. package/src/astro/Button.astro +43 -0
  64. package/src/astro/Card.astro +39 -0
  65. package/src/astro/Logo.astro +69 -0
  66. package/src/astro/TierBadge.astro +33 -0
  67. package/src/lib/alert-tones.ts +65 -0
  68. package/src/lib/badge-tone.ts +4 -0
  69. package/src/lib/button-variants.ts +44 -0
  70. package/src/lib/card-classes.ts +37 -0
  71. package/src/lib/glass-tone.ts +23 -0
  72. package/src/lib/input-classes.ts +5 -0
  73. package/src/lib/logo-lockup.ts +49 -0
  74. package/src/lib/overlay-classes.ts +7 -0
  75. package/src/lib/section-tokens.ts +8 -0
  76. package/src/lib/tier-badge.ts +31 -0
  77. package/src/lib/use-is-mobile.ts +17 -0
  78. package/src/lib/utils.ts +6 -0
  79. package/src/styles/globals.css +157 -0
  80. package/src/styles/theme.css +68 -12
  81. package/dist/solid/calendar.d.ts +0 -17
  82. package/dist/solid/command.d.ts +0 -24
  83. package/dist/solid/drawer.d.ts +0 -22
  84. package/dist/solid/glass-effect.d.ts +0 -6
  85. package/dist/solid/menubar.d.ts +0 -25
  86. package/dist/solid/navigation-menu.d.ts +0 -19
  87. package/dist/solid/sidebar.d.ts +0 -108
@@ -0,0 +1,37 @@
1
+ const CARD_KNOBS =
2
+ "glass [--glass-wash:20%] [--glass-rim:0.3] [--glass-lift:0.45] [--glass-shade:0.05] dark:[--glass-shade:0]";
3
+
4
+ export const CARD_SURFACE_BASE = `${CARD_KNOBS} [--glass-base:color-mix(in_srgb,var(--card)_60%,transparent)]`;
5
+
6
+ /* Split from the surface so dash's performant-blur engine can gate it off and
7
+ * paint a precomputed frost instead. */
8
+ export const CARD_BLUR = "backdrop-blur-[var(--glass-blur,24px)] backdrop-saturate-[1.8]";
9
+
10
+ export const CARD_SURFACE = `${CARD_SURFACE_BASE} ${CARD_BLUR}`;
11
+
12
+ /* Opaque fill, no blur: for busy/dark backgrounds and transform-animated
13
+ * contexts where backdrop-blur can't run. */
14
+ export const CARD_SURFACE_OPAQUE = CARD_KNOBS;
15
+
16
+ /* Recessed track frame (tabs, segmented controls). */
17
+ export const TRACK_SURFACE = "border border-border/50 bg-card/40 backdrop-blur-sm";
18
+
19
+ export const CARD_PADDING = {
20
+ slots: "flex flex-col gap-3 py-3 md:gap-4 md:py-4",
21
+ md: "p-4",
22
+ sm: "p-3",
23
+ none: "",
24
+ } as const;
25
+
26
+ export type CardPadding = keyof typeof CARD_PADDING;
27
+
28
+ export const CARD_INTERACTIVE =
29
+ "cursor-pointer transition-colors duration-200 hover:border-border hover:bg-foreground/[0.03] focus-within:border-border";
30
+
31
+ export const CARD_HEADER_CLASS =
32
+ "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] md:px-4";
33
+ export const CARD_TITLE_CLASS = "font-semibold leading-none";
34
+ export const CARD_DESCRIPTION_CLASS = "text-muted-foreground text-sm";
35
+ export const CARD_ACTION_CLASS = "col-start-2 row-span-2 row-start-1 self-start justify-self-end";
36
+ export const CARD_CONTENT_CLASS = "px-3 md:px-4";
37
+ export const CARD_FOOTER_CLASS = "flex items-center px-3 md:px-4 [.border-t]:pt-6";
@@ -0,0 +1,23 @@
1
+ /* Per-tone label contrast: light tones (amber, lime) mix further toward
2
+ * --foreground so they read as strongly as dark ones. */
3
+ const TONE_L: Record<string, number> = {
4
+ "var(--primary)": 0.48,
5
+ "var(--accent)": 0.6,
6
+ "var(--success)": 0.68,
7
+ "var(--warning)": 0.76,
8
+ "var(--destructive)": 0.66,
9
+ "var(--muted-foreground)": 0.55,
10
+ };
11
+
12
+ export function toneTextMix(color: string): number {
13
+ const known = TONE_L[color];
14
+ const raw = color.match(/oklch\(\s*([0-9.]+)/)?.[1];
15
+ const l = known ?? (raw ? Number.parseFloat(raw) : 0.6);
16
+ return Math.min(70, Math.max(46, Math.round(71 - (l - 0.5) * 60)));
17
+ }
18
+
19
+ /* oklab, not oklch: no hue channel, so a hued tone desaturates toward neutral
20
+ * instead of swinging toward yellow. */
21
+ export function glassToneText(color: string): string {
22
+ return `color-mix(in oklab, ${color} ${toneTextMix(color)}%, var(--foreground))`;
23
+ }
@@ -0,0 +1,5 @@
1
+ /* Recessed (concave) glass field. Opaque --input fill: a translucent field
2
+ * shows whatever sits behind it (worst inside the overlapping Select listbox). */
3
+ export const INPUT_SURFACE = "glass glass-sink [--glass-base:var(--input)] [--glass-light:0.04]";
4
+
5
+ export const INPUT_CLASS = `flex h-9 w-full min-w-0 rounded-md ${INPUT_SURFACE} px-3 py-1 text-base outline-none transition-[color,box-shadow] selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:font-medium file:text-foreground file:text-sm placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40`;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * The GlassHome lockup spec, pure — shared by the Solid <Logo>, the Astro
3
+ * <Logo>, and any `.astro` consumer that needs the class strings without
4
+ * hydrating a component.
5
+ *
6
+ * One ratio everywhere (audit L2): the sub-brand line is the dominant element,
7
+ * the wordmark sits above it as a smaller eyebrow. Sub-line spec (audit L1):
8
+ * text-primary, uppercase, bold, tracking-[0.18em] — never restyled per
9
+ * surface, never recolored, and the mark is never stretched or used as an
10
+ * avatar.
11
+ */
12
+
13
+ export type LogoSize = "sm" | "md" | "lg";
14
+
15
+ export const LOGO_DEFAULT_SRC = "/assets/glasshome_logo.png";
16
+ export const LOGO_DEFAULT_SUB = "Dash";
17
+ export const LOGO_DEFAULT_SIZE: LogoSize = "md";
18
+
19
+ /** Rendered pixel size of the mark per size, for width/height attrs (CLS). */
20
+ export const LOGO_MARK_PX: Record<LogoSize, number> = {
21
+ sm: 32,
22
+ md: 48,
23
+ lg: 64,
24
+ };
25
+
26
+ export const LOGO_SIZES: Record<
27
+ LogoSize,
28
+ { mark: string; name: string; sub: string; gap: string }
29
+ > = {
30
+ sm: { mark: "h-8 w-8", name: "text-xs", sub: "text-base", gap: "gap-1.5" },
31
+ md: { mark: "h-12 w-12", name: "text-base", sub: "text-2xl", gap: "gap-2" },
32
+ lg: { mark: "h-16 w-16", name: "text-lg", sub: "text-3xl", gap: "gap-2" },
33
+ };
34
+
35
+ /**
36
+ * The stack sits on the mark's baseline (items-end), so the sub-brand line
37
+ * bottoms out level with the mark rather than floating centred against it.
38
+ */
39
+ export const LOGO_ROOT_CLASS = "inline-flex items-end";
40
+ /** The two lines are pinned by an explicit gap, never by line-height slack. */
41
+ export const LOGO_STACK_CLASS = "flex flex-col gap-0.5";
42
+ /**
43
+ * Compose as cn(SIZE, BASE), not cn(BASE, SIZE): tailwind-merge treats a
44
+ * font-size class as carrying a line-height, so a trailing `text-2xl` would
45
+ * strip the `leading-none` these rely on.
46
+ */
47
+ export const LOGO_MARK_CLASS = "shrink-0";
48
+ export const LOGO_NAME_CLASS = "whitespace-nowrap font-bold leading-none tracking-tight";
49
+ export const LOGO_SUB_CLASS = "font-bold text-primary uppercase leading-none tracking-[0.18em]";
@@ -0,0 +1,7 @@
1
+ /* Floating-panel glass: opaque popover fill so menus stay legible over anything. */
2
+ export const OVERLAY_SURFACE =
3
+ "glass [--glass-base:var(--popover)] [--glass-rim:0.3] [--glass-lift:0.55]";
4
+
5
+ /* Modal scrim behind dialogs/sheets. BottomSheet keeps its own unblurred scrim
6
+ * (backdrop-blur is too slow on mobile). */
7
+ export const SCRIM_CLASS = "bg-background/70 backdrop-blur-sm";
@@ -0,0 +1,8 @@
1
+ /* Concentric radius: inner = outer - padding, clamped for tiny --radius themes. */
2
+ export const SECTION_OUTER_RADIUS = "rounded-[var(--radius)]";
3
+ export const SECTION_INNER_RADIUS = "rounded-[max(0px,calc(var(--radius)-0.75rem))]";
4
+ export const SECTION_PADDING = "p-3";
5
+
6
+ const SECTION_ROW_SURFACE = `${SECTION_INNER_RADIUS} border border-border/60 bg-card/60`;
7
+
8
+ export const SECTION_ROW_CLASS = `${SECTION_ROW_SURFACE} ${SECTION_PADDING}`;
@@ -0,0 +1,31 @@
1
+ /* Metallic tier chip: 135deg brushed sweep, bright ends, dip in the middle. */
2
+
3
+ export const TIER_BADGE_CLASS =
4
+ "inline-flex cursor-default select-none items-center rounded-full px-2 py-0.5 font-black text-[10px] uppercase tracking-wider";
5
+
6
+ /** Optional sweep shaping. Default is the symmetric brushed look (both ends
7
+ * `hi`, dipping to `lo` at the midpoint). The collectible/license golds are a
8
+ * lit bevel instead: they dip early and land on a darker `end`. */
9
+ export interface TierSweep {
10
+ /** Far end of the sweep. Default: `hi` (symmetric). */
11
+ end?: string;
12
+ /** Where `lo` sits, in percent. Default: 50. */
13
+ stop?: number;
14
+ }
15
+
16
+ /** Inline style for the metallic pill body. Keys are CSS property names (works
17
+ * as a Solid `style` object and, joined, as an Astro `style` string). */
18
+ export function tierBadgeStyle(
19
+ hi: string,
20
+ lo: string,
21
+ text: string,
22
+ sweep: TierSweep = {},
23
+ ): Record<string, string> {
24
+ const end = sweep.end ?? hi;
25
+ const stop = sweep.stop ?? 50;
26
+ return {
27
+ background: `linear-gradient(135deg, ${hi}, ${lo} ${stop}%, ${end})`,
28
+ color: text,
29
+ "box-shadow": "inset 0 1px 0 oklch(1 0 0 / 0.45), 0 1px 2px oklch(0 0 0 / 0.25)",
30
+ };
31
+ }
@@ -0,0 +1,17 @@
1
+ import { createSignal, onCleanup, onMount } from "solid-js";
2
+
3
+ const MOBILE_BREAKPOINT = 768;
4
+
5
+ export function createIsMobile() {
6
+ const [isMobile, setIsMobile] = createSignal<boolean | undefined>(undefined);
7
+
8
+ onMount(() => {
9
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
10
+ const onChange = () => setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
11
+ mql.addEventListener("change", onChange);
12
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
13
+ onCleanup(() => mql.removeEventListener("change", onChange));
14
+ });
15
+
16
+ return isMobile;
17
+ }
@@ -0,0 +1,6 @@
1
+ import { type ClassValue, clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -3,5 +3,162 @@
3
3
  @import "./theme.css";
4
4
 
5
5
  @source "../";
6
+ /* npm consumers resolve components from dist/, not src/; without this their
7
+ * Tailwind build never sees the class names used inside the components. */
8
+ @source "../../dist";
6
9
 
7
10
  @custom-variant dark (&:is(.dark *));
11
+
12
+ /* Typed + inherits:false: a knob never leaks into a nested glass element, each
13
+ * element's calc() resolves against its own value, and typed knobs animate. */
14
+ @property --glass-tone {
15
+ syntax: "<color>";
16
+ inherits: false;
17
+ initial-value: transparent;
18
+ }
19
+ @property --glass-wash {
20
+ syntax: "<percentage>";
21
+ inherits: false;
22
+ initial-value: 28%;
23
+ }
24
+ @property --glass-light {
25
+ syntax: "<number>";
26
+ inherits: false;
27
+ initial-value: 0.05;
28
+ }
29
+ @property --glass-shade {
30
+ syntax: "<number>";
31
+ inherits: false;
32
+ initial-value: 0;
33
+ }
34
+ @property --glass-glow {
35
+ syntax: "<percentage>";
36
+ inherits: false;
37
+ initial-value: 16%;
38
+ }
39
+ @property --glass-drop {
40
+ syntax: "<percentage>";
41
+ inherits: false;
42
+ initial-value: 20%;
43
+ }
44
+ @property --glass-lift {
45
+ syntax: "<number>";
46
+ inherits: false;
47
+ initial-value: 0;
48
+ }
49
+ @property --glass-text {
50
+ syntax: "<percentage>";
51
+ inherits: false;
52
+ initial-value: 65%;
53
+ }
54
+ @property --glass-rim {
55
+ syntax: "<number>";
56
+ inherits: false;
57
+ initial-value: 1;
58
+ }
59
+ /* Frost slot: one full background layer (image + position / size + repeat),
60
+ * host-supplied, composited at the BOTTOM of the formula — the static
61
+ * equivalent of what backdrop-filter shows behind the element. Dash's
62
+ * performant-blur engine sets it inline per surface; unset -> the layer falls
63
+ * back to `none`. inherits:false so a surface's viewport-aligned slice never
64
+ * leaks into nested glass (inputs inside a frosted card). */
65
+ @property --glass-frost {
66
+ syntax: "*";
67
+ inherits: false;
68
+ }
69
+
70
+ /* --glass-base and --glass-edge need var() defaults, which @property forbids,
71
+ * so they reset here instead. MUST stay in @layer base so [--glass-*] utilities
72
+ * and inline style knobs still win. Rim shadows are declared on .glass (not
73
+ * :root) so calc(var(--glass-rim)) resolves per element, not frozen at :root. */
74
+ @layer base {
75
+ :where(.glass) {
76
+ --glass-base: var(--card);
77
+ --glass-edge: color-mix(in srgb, var(--border) 60%, transparent);
78
+ --glass-rim-raise:
79
+ inset 0 1px 0 oklch(1 0 0 / calc(0.22 * var(--glass-rim))),
80
+ inset 2px 2px 5px -3px oklch(1 0 0 / calc(0.5 * var(--glass-rim))),
81
+ inset -2px -2px 4px -2px oklch(0 0 0 / calc(0.12 * var(--glass-rim)));
82
+ --glass-rim-sink:
83
+ inset 0 2px 3px -1px oklch(0 0 0 / calc(0.28 * var(--glass-rim))),
84
+ inset 2px 2px 5px -3px oklch(0 0 0 / calc(0.22 * var(--glass-rim))),
85
+ inset 0 -1px 0 oklch(1 0 0 / calc(0.12 * var(--glass-rim)));
86
+ --glass-rim-shine: var(--glass-rim-raise);
87
+ }
88
+ :where(.glass-tint) {
89
+ --glass-light: 0.16;
90
+ --glass-edge: oklch(from var(--glass-tone) l c h / 0.45);
91
+ color: color-mix(in oklab, var(--glass-tone) var(--glass-text), var(--foreground));
92
+ }
93
+ :where(.glass-sink) {
94
+ --glass-rim-shine: var(--glass-rim-sink);
95
+ }
96
+ }
97
+
98
+ /* The ONE glass formula. Neutral by default (--glass-tone transparent makes
99
+ * every tone term inert); add .glass-tint for the tinted badge/alert look and
100
+ * .glass-sink for a recessed field. Tone mixes are srgb: oklch drags a tone
101
+ * mixed toward transparent-black down to mud on light surfaces. Deliberately
102
+ * unlayered so the material owns border/background/box-shadow; tune via knobs. */
103
+ :where(.glass) {
104
+ border: 1px solid var(--glass-edge);
105
+ /* Base is a gradient layer (not background-color) so the frost slice can sit
106
+ * BELOW it — background-color always paints under every image layer, which
107
+ * would put the frost above the fill. Layer order top→bottom: light, shade,
108
+ * tone wash, base fill, frost. */
109
+ background:
110
+ radial-gradient(120% 120% at 22% 8%, oklch(1 0 0 / var(--glass-light)), transparent 58%),
111
+ radial-gradient(120% 120% at 82% 100%, oklch(0 0 0 / var(--glass-shade)), transparent 58%),
112
+ linear-gradient(
113
+ 135deg,
114
+ color-mix(in srgb, var(--glass-tone) var(--glass-wash), transparent),
115
+ color-mix(in srgb, var(--glass-tone) calc(var(--glass-wash) / 3), transparent)
116
+ ),
117
+ linear-gradient(var(--glass-base), var(--glass-base)), var(--glass-frost, none);
118
+ box-shadow:
119
+ var(--glass-rim-shine, 0 0 #0000),
120
+ inset 0 -2px 4px color-mix(in srgb, var(--glass-tone) var(--glass-glow), transparent),
121
+ 0 1px 3px color-mix(in srgb, var(--glass-tone) var(--glass-drop), transparent),
122
+ 0 10px 30px -16px oklch(0 0 0 / var(--glass-lift));
123
+ }
124
+
125
+ /* PageHeader banner surface: bespoke three-stop card gradient (its material
126
+ * predates the one-glass formula) over the same frost slot .glass uses, so
127
+ * hosts wire the performant-blur engine identically on both. Frost defaults
128
+ * to none → plain gradient banner. */
129
+ :where(.glass-banner) {
130
+ background:
131
+ linear-gradient(
132
+ to bottom right,
133
+ color-mix(in srgb, var(--card) 55%, transparent),
134
+ color-mix(in srgb, var(--card) 25%, transparent),
135
+ color-mix(in srgb, var(--card) 10%, transparent)
136
+ ),
137
+ var(--glass-frost, none);
138
+ }
139
+
140
+ /**
141
+ * Consistent, theme-aware thin scrollbar across browsers. Firefox uses
142
+ * scrollbar-width/color; Chromium/WebKit use the ::-webkit-scrollbar pseudos.
143
+ * Applied by the <ScrollArea> component so every scroll surface reads the same.
144
+ */
145
+ .gh-scroll {
146
+ scrollbar-width: thin;
147
+ scrollbar-color: var(--border) transparent;
148
+ &::-webkit-scrollbar {
149
+ width: 10px;
150
+ height: 10px;
151
+ }
152
+ &::-webkit-scrollbar-track {
153
+ background: transparent;
154
+ }
155
+ &::-webkit-scrollbar-thumb {
156
+ background: var(--border);
157
+ border-radius: 9999px;
158
+ border: 2px solid transparent;
159
+ background-clip: padding-box;
160
+ }
161
+ &::-webkit-scrollbar-thumb:hover {
162
+ background: var(--muted-foreground);
163
+ }
164
+ }
@@ -49,6 +49,13 @@
49
49
 
50
50
  --animate-collapsible-down: collapsible-down 0.2s ease-out;
51
51
  --animate-collapsible-up: collapsible-up 0.2s ease-out;
52
+
53
+ /* Select "native expand": the panel unfolds out of the trigger edge. Kobalte
54
+ * exposes no data-side, only --kb-select-content-transform-origin (set to the
55
+ * trigger edge per placement), so a scaleY from that origin grows the panel
56
+ * down when it opens below and up when above, no direction class needed. */
57
+ --animate-select-in: select-in 0.18s cubic-bezier(0.16, 1, 0.3, 1) both;
58
+ --animate-select-out: select-out 0.12s ease-in both;
52
59
  }
53
60
 
54
61
  @keyframes collapsible-down {
@@ -69,19 +76,54 @@
69
76
  }
70
77
  }
71
78
 
79
+ /* The listbox opens overlapping the trigger (overlap + gutter 0), so its top row
80
+ * covers the input. clip-path reveals it top-down: the covering row shows first,
81
+ * then the rest of the options are revealed downward, so the input appears to
82
+ * expand to the bottom rather than a separate box dropping in. No scale, so the
83
+ * option text never squishes; opacity fades in slightly so it isn't a hard wipe. */
84
+ @keyframes select-in {
85
+ /* The trigger opens on pointerdown and the panel appears under the cursor, so
86
+ * the same click's pointerup would land on an option (press-drag-select) and
87
+ * instantly pick/close. Ignore pointer events for the open animation so that
88
+ * release passes through and the panel stays open; a fresh click then selects. */
89
+ from {
90
+ opacity: 0;
91
+ clip-path: inset(0 0 100% 0 round var(--radius-md));
92
+ pointer-events: none;
93
+ }
94
+ 90% {
95
+ pointer-events: none;
96
+ }
97
+ to {
98
+ opacity: 1;
99
+ clip-path: inset(0 0 0 0 round var(--radius-md));
100
+ pointer-events: auto;
101
+ }
102
+ }
103
+ @keyframes select-out {
104
+ from {
105
+ opacity: 1;
106
+ clip-path: inset(0 0 0 0 round var(--radius-md));
107
+ }
108
+ to {
109
+ opacity: 0;
110
+ clip-path: inset(0 0 100% 0 round var(--radius-md));
111
+ }
112
+ }
113
+
72
114
  :root {
73
- --background: oklch(0.985 0.002 247.8763);
115
+ --background: oklch(0.975 0.003 250);
74
116
  --foreground: oklch(0 0 0);
75
- --card: oklch(0.99 0.008 285);
117
+ --card: oklch(0.995 0.004 250);
76
118
  --card-foreground: oklch(0 0 0);
77
- --popover: oklch(0.9911 0 0);
119
+ --popover: oklch(0.995 0.003 250);
78
120
  --popover-foreground: oklch(0 0 0);
79
121
  --primary: oklch(0.48 0.2 215.221);
80
122
  --primary-foreground: oklch(1 0 0);
81
123
  --secondary: oklch(0.96 0.01 250);
82
- --secondary-foreground: oklch(0.9551 0 0);
83
- --muted: oklch(0.9702 0 0);
84
- --muted-foreground: oklch(0.4386 0 0);
124
+ --secondary-foreground: oklch(0.25 0.01 250);
125
+ --muted: oklch(0.9702 0.008 250);
126
+ --muted-foreground: oklch(0.34 0 0);
85
127
  --accent: oklch(0.6 0.2 195);
86
128
  --accent-foreground: oklch(1 0 0);
87
129
  --success: oklch(0.65 0.17 145);
@@ -90,8 +132,8 @@
90
132
  --accent-tint-foreground: oklch(0.46 0.16 195);
91
133
  --destructive: oklch(0.629 0.1902 23.0704);
92
134
  --destructive-foreground: oklch(1 0 0);
93
- --border: oklch(0.93 0.0094 286.2156);
94
- --input: oklch(0.9401 0 0);
135
+ --border: oklch(0.9 0.01 250);
136
+ --input: oklch(0.9 0.012 250);
95
137
  --ring: oklch(0.6 0.2 195);
96
138
  --chart-1: oklch(0.7459 0.1483 156.4499);
97
139
  --chart-2: oklch(0.48 0.2 215.221);
@@ -125,14 +167,14 @@
125
167
  --foreground: oklch(0.9551 0 0);
126
168
  --card: oklch(0.17 0.01 250);
127
169
  --card-foreground: oklch(0.9551 0 0);
128
- --popover: oklch(0.16 0.005 0);
170
+ --popover: oklch(0.19 0.008 250);
129
171
  --popover-foreground: oklch(0.9551 0 0);
130
172
  --primary: oklch(0.48 0.2 215.221);
131
173
  --primary-foreground: oklch(1 0 0);
132
174
  --secondary: oklch(0.14 0.01 250);
133
175
  --secondary-foreground: oklch(0.9551 0 0);
134
- --muted: oklch(0.22 0.006 0);
135
- --muted-foreground: oklch(0.7058 0 0);
176
+ --muted: oklch(0.22 0.02 250);
177
+ --muted-foreground: oklch(0.81 0 0);
136
178
  --accent: oklch(0.6 0.2 195);
137
179
  --accent-foreground: oklch(1 0 0);
138
180
  --success: oklch(0.7 0.17 145);
@@ -142,7 +184,10 @@
142
184
  --destructive: oklch(0.7106 0.1661 22.2162);
143
185
  --destructive-foreground: oklch(1 0 0);
144
186
  --border: oklch(0.26 0.012 250);
145
- --input: oklch(0.25 0.005 0);
187
+ /* Recessed fields read DARKER than the card they're dug out of (card is 0.17),
188
+ * so a field surface sits below it, matching the slider track. A hair of the
189
+ * card's cool tint (chroma 0.01) keeps it from going flat gray. */
190
+ --input: oklch(0.19 0.01 250);
146
191
  --ring: oklch(0.6 0.2 195);
147
192
  --chart-1: oklch(0.8003 0.1821 151.711);
148
193
  --chart-2: oklch(0.48 0.2 215.221);
@@ -176,6 +221,17 @@
176
221
  body {
177
222
  @apply bg-background text-foreground min-w-[320px] min-h-screen relative m-0;
178
223
  }
224
+ /* Tailwind v4 Preflight drops the old cursor:pointer default on buttons, so
225
+ * native interactive elements (raw <button>, role=button, <summary>, control
226
+ * labels, <select>) show the arrow. Restore the pointer affordance for every
227
+ * clickable, minus disabled ones. Component primitives set their own cursor. */
228
+ button:not(:disabled),
229
+ [role="button"]:not([aria-disabled="true"]),
230
+ summary,
231
+ select:not(:disabled),
232
+ label:has(> input:is([type="checkbox"], [type="radio"]):not(:disabled)) {
233
+ cursor: pointer;
234
+ }
179
235
  }
180
236
 
181
237
  /* Utility classes */
@@ -1,17 +0,0 @@
1
- import { type Component, type ComponentProps } from "solid-js";
2
- type DateRange = {
3
- from: Date;
4
- to?: Date;
5
- };
6
- interface CalendarProps extends Omit<ComponentProps<"div">, "onSelect"> {
7
- mode?: "single" | "range";
8
- selected?: Date | DateRange;
9
- onSelect?: (date: Date | DateRange | undefined) => void;
10
- showOutsideDays?: boolean;
11
- month?: Date;
12
- onMonthChange?: (month: Date) => void;
13
- disabled?: (date: Date) => boolean;
14
- }
15
- declare const Calendar: Component<CalendarProps>;
16
- export type { CalendarProps, DateRange };
17
- export { Calendar };
@@ -1,24 +0,0 @@
1
- import { type Component, type ComponentProps, type ParentComponent } from "solid-js";
2
- import { Dialog } from "./dialog";
3
- declare const Command: ParentComponent<ComponentProps<"div">>;
4
- declare const CommandDialog: ParentComponent<ComponentProps<typeof Dialog> & {
5
- title?: string;
6
- description?: string;
7
- contentClass?: string;
8
- showCloseButton?: boolean;
9
- }>;
10
- declare const CommandInput: Component<ComponentProps<"input">>;
11
- declare const CommandList: ParentComponent<ComponentProps<"div">>;
12
- declare const CommandEmpty: ParentComponent<ComponentProps<"div">>;
13
- declare const CommandGroup: ParentComponent<ComponentProps<"div"> & {
14
- heading?: string;
15
- }>;
16
- declare const CommandItem: ParentComponent<ComponentProps<"div"> & {
17
- value?: string;
18
- keywords?: string;
19
- onSelect?: () => void;
20
- disabled?: boolean;
21
- }>;
22
- declare const CommandSeparator: Component<ComponentProps<"div">>;
23
- declare const CommandShortcut: Component<ComponentProps<"span">>;
24
- export { Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, };
@@ -1,22 +0,0 @@
1
- import { Dialog as DialogPrimitive } from "@kobalte/core/dialog";
2
- import { type Component, type ComponentProps, type ParentComponent } from "solid-js";
3
- declare const Drawer: typeof import("@kobalte/core/alert-dialog").Root & {
4
- CloseButton: typeof import("@kobalte/core/alert-dialog").CloseButton;
5
- Content: typeof import("@kobalte/core/dialog").Content;
6
- Description: typeof import("@kobalte/core/alert-dialog").Description;
7
- Overlay: typeof import("@kobalte/core/alert-dialog").Overlay;
8
- Portal: typeof import("@kobalte/core/alert-dialog").Portal;
9
- Title: typeof import("@kobalte/core/alert-dialog").Title;
10
- Trigger: typeof import("@kobalte/core/alert-dialog").Trigger;
11
- };
12
- declare const DrawerTrigger: Component<ComponentProps<typeof DialogPrimitive.Trigger>>;
13
- declare const DrawerClose: typeof import("@kobalte/core/alert-dialog").CloseButton;
14
- declare const DrawerOverlay: Component<ComponentProps<typeof DialogPrimitive.Overlay>>;
15
- declare const DrawerContent: ParentComponent<ComponentProps<typeof DialogPrimitive.Content> & {
16
- direction?: "top" | "right" | "bottom" | "left";
17
- }>;
18
- declare const DrawerHeader: Component<ComponentProps<"div">>;
19
- declare const DrawerFooter: Component<ComponentProps<"div">>;
20
- declare const DrawerTitle: Component<ComponentProps<typeof DialogPrimitive.Title>>;
21
- declare const DrawerDescription: Component<ComponentProps<typeof DialogPrimitive.Description>>;
22
- export { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerTitle, DrawerTrigger, };
@@ -1,6 +0,0 @@
1
- import { type ParentComponent } from "solid-js";
2
- declare const GlassEffect: ParentComponent<{
3
- class?: string;
4
- }>;
5
- declare const GlassFilter: () => import("solid-js").JSX.Element;
6
- export { GlassEffect, GlassFilter };
@@ -1,25 +0,0 @@
1
- import { Menubar as MenubarPrimitive } from "@kobalte/core/menubar";
2
- import { type Component, type ComponentProps, type ParentComponent } from "solid-js";
3
- declare const MenubarMenu: typeof import("@kobalte/core/menubar").Menu;
4
- declare const MenubarGroup: typeof import("@kobalte/core/context-menu").Group;
5
- declare const MenubarRadioGroup: typeof import("@kobalte/core/context-menu").RadioGroup;
6
- declare const MenubarSub: typeof import("@kobalte/core/context-menu").Sub;
7
- declare const Menubar: Component<ComponentProps<typeof MenubarPrimitive>>;
8
- declare const MenubarTrigger: Component<ComponentProps<typeof MenubarPrimitive.Trigger>>;
9
- declare const MenubarContent: Component<ComponentProps<typeof MenubarPrimitive.Content>>;
10
- declare const MenubarItem: Component<ComponentProps<typeof MenubarPrimitive.Item> & {
11
- inset?: boolean;
12
- variant?: "default" | "destructive";
13
- }>;
14
- declare const MenubarCheckboxItem: ParentComponent<ComponentProps<typeof MenubarPrimitive.CheckboxItem>>;
15
- declare const MenubarRadioItem: ParentComponent<ComponentProps<typeof MenubarPrimitive.RadioItem>>;
16
- declare const MenubarLabel: Component<ComponentProps<typeof MenubarPrimitive.GroupLabel> & {
17
- inset?: boolean;
18
- }>;
19
- declare const MenubarSeparator: Component<ComponentProps<typeof MenubarPrimitive.Separator>>;
20
- declare const MenubarShortcut: Component<ComponentProps<"span">>;
21
- declare const MenubarSubTrigger: ParentComponent<ComponentProps<typeof MenubarPrimitive.SubTrigger> & {
22
- inset?: boolean;
23
- }>;
24
- declare const MenubarSubContent: Component<ComponentProps<typeof MenubarPrimitive.SubContent>>;
25
- export { Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, };
@@ -1,19 +0,0 @@
1
- import { NavigationMenu as NavMenuPrimitive } from "@kobalte/core/navigation-menu";
2
- import { type Component, type ComponentProps, type ParentComponent } from "solid-js";
3
- declare const NavigationMenu: ParentComponent<ComponentProps<typeof NavMenuPrimitive> & {
4
- viewport?: boolean;
5
- }>;
6
- declare const NavigationMenuList: Component<ComponentProps<"ul">>;
7
- declare const NavigationMenuItem: Component<ComponentProps<"li">>;
8
- declare const navigationMenuTriggerStyle: import("cva").CVAComponent<Omit<{
9
- base: string;
10
- }, "defaultVariants"> & {
11
- variants: unknown;
12
- defaultVariants: Omit<{}, never>;
13
- }, unknown>;
14
- declare const NavigationMenuTrigger: ParentComponent<ComponentProps<typeof NavMenuPrimitive.Trigger>>;
15
- declare const NavigationMenuContent: Component<ComponentProps<typeof NavMenuPrimitive.Content>>;
16
- declare const NavigationMenuViewport: Component<ComponentProps<typeof NavMenuPrimitive.Viewport>>;
17
- declare const NavigationMenuLink: Component<ComponentProps<"a">>;
18
- declare const NavigationMenuIndicator: ParentComponent<ComponentProps<"div">>;
19
- export { NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, navigationMenuTriggerStyle, };