@glasshome/ui 0.3.3 → 0.4.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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  SolidJS component library for GlassHome, built on [Kobalte](https://kobalte.dev).
4
4
 
5
- 56 accessible, theme-aware components plus utility functions and a Tailwind v4 CSS preset — everything you need to build smart-home dashboards.
5
+ 56 accessible, theme-aware components plus utility functions and a Tailwind v4 CSS preset — everything you need to build smart-home dashboards. Powers [GlassHome](https://glasshome.app), the local-first dashboard for Home Assistant.
6
6
 
7
7
  ## Install
8
8
 
@@ -0,0 +1,6 @@
1
+ /**
2
+ * oklch() string -> sRGB hex, matching browser rendering: out-of-gamut colors
3
+ * are gamut-mapped by chroma reduction (CSS Color 4), not channel-clipped.
4
+ * For renderers that cannot parse oklch (librsvg, sharp SVG, canvas fills).
5
+ */
6
+ export declare function oklchToHex(oklch: string): string;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Design tokens as data, framework-free. Must stay in sync with
3
+ * styles/theme.css (presets.ts is the machine-readable form of that palette).
4
+ */
5
+ export * from "./hex";
6
+ export * from "./presets";
7
+ export * from "./theme-colors";
@@ -0,0 +1,223 @@
1
+ //#region src/tokens/theme-colors.ts
2
+ var e = /^oklch\(\s*([+-]?(?:\d+\.?\d*|\.\d+))\s+([+-]?(?:\d+\.?\d*|\.\d+))\s+([+-]?(?:\d+\.?\d*|\.\d+))\s*\)$/i;
3
+ function t(e, t, n) {
4
+ return Math.min(Math.max(e, t), n);
5
+ }
6
+ function n(e) {
7
+ return String(Number.isInteger(e) ? e : Number(e.toFixed(6)));
8
+ }
9
+ function r(t) {
10
+ let n = e.exec(t.trim());
11
+ return n ? {
12
+ l: Number(n[1]),
13
+ c: Number(n[2]),
14
+ h: Number(n[3])
15
+ } : null;
16
+ }
17
+ function i(e, t, r) {
18
+ return `oklch(${n(e)} ${n(t)} ${n(r)})`;
19
+ }
20
+ function a(e, n) {
21
+ let a = r(e);
22
+ return a ? i(t(a.l + n, 0, 1), a.c, a.h) : e;
23
+ }
24
+ function o(e, n) {
25
+ let a = r(e);
26
+ return a ? i(t(n, 0, 1), t(a.c * .8, 0, .4), a.h) : e;
27
+ }
28
+ function s(e, t) {
29
+ return {
30
+ ...e,
31
+ popover: e.card,
32
+ muted: t === "light" ? a(e.background, -.015) : a(e.background, .1),
33
+ mutedForeground: t === "light" ? "oklch(0.44 0 0)" : "oklch(0.71 0 0)",
34
+ input: t === "light" ? a(e.border, .01) : a(e.border, .04),
35
+ ring: e.accent,
36
+ destructive: t === "light" ? "oklch(0.629 0.1902 23.0704)" : "oklch(0.7106 0.1661 22.2162)"
37
+ };
38
+ }
39
+ function c(e) {
40
+ return s({
41
+ primary: a(e.primary, .15),
42
+ accent: a(e.accent, .1),
43
+ secondary: o(e.secondary, .14),
44
+ border: o(e.border, .26),
45
+ card: o(e.card, .17),
46
+ background: o(e.background, .12)
47
+ }, "dark");
48
+ }
49
+ var l = [
50
+ "primary",
51
+ "accent",
52
+ "secondary",
53
+ "border",
54
+ "card",
55
+ "background",
56
+ "muted",
57
+ "mutedForeground",
58
+ "input",
59
+ "ring",
60
+ "destructive",
61
+ "popover"
62
+ ];
63
+ function u(e, t) {
64
+ let n = { ...s(e, t) };
65
+ for (let t of l) e[t] && (n[t] = e[t]);
66
+ return n;
67
+ }
68
+ //#endregion
69
+ //#region src/tokens/hex.ts
70
+ function d(e) {
71
+ let t = r(e);
72
+ if (!t) throw Error(`oklchToHex: cannot parse "${e}"`);
73
+ let n = f(t.l, t.c, t.h);
74
+ if (!p(n)) {
75
+ let e = 0, r = t.c;
76
+ for (let n = 0; n < 40; n++) {
77
+ let n = (e + r) / 2;
78
+ p(f(t.l, n, t.h)) ? e = n : r = n;
79
+ }
80
+ n = f(t.l, e, t.h);
81
+ }
82
+ return `#${n.map(m).map(h).join("")}`;
83
+ }
84
+ function f(e, t, n) {
85
+ let r = n * Math.PI / 180, i = t * Math.cos(r), a = t * Math.sin(r), o = (e + .3963377774 * i + .2158037573 * a) ** 3, s = (e - .1055613458 * i - .0638541728 * a) ** 3, c = (e - .0894841775 * i - 1.291485548 * a) ** 3;
86
+ return [
87
+ 4.0767416621 * o - 3.3077115913 * s + .2309699292 * c,
88
+ -1.2684380046 * o + 2.6097574011 * s - .3413193965 * c,
89
+ -.0041960863 * o - .7034186147 * s + 1.707614701 * c
90
+ ];
91
+ }
92
+ function p(e) {
93
+ return e.every((e) => e >= -1e-6 && e <= 1.000001);
94
+ }
95
+ function m(e) {
96
+ let t = e <= .0031308 ? 12.92 * e : 1.055 * Math.max(0, e) ** (1 / 2.4) - .055;
97
+ return Math.round(Math.min(1, Math.max(0, t)) * 255);
98
+ }
99
+ function h(e) {
100
+ return e.toString(16).padStart(2, "0");
101
+ }
102
+ //#endregion
103
+ //#region src/tokens/presets.ts
104
+ var g = "midnight-glass", _ = "\"Geist Variable\", ui-sans-serif, system-ui, sans-serif", v = "\"GeistMono Variable\", \"JetBrains Mono\", \"Fira Code\", Consolas, monospace";
105
+ function y(e, t) {
106
+ return {
107
+ type: e === "default" ? "solid" : "static",
108
+ id: e,
109
+ themed: t?.themed ?? !0,
110
+ url: t?.url
111
+ };
112
+ }
113
+ function b(e, t, n, r, i, a, o) {
114
+ return {
115
+ id: e,
116
+ name: t,
117
+ radius: n,
118
+ swatch: r,
119
+ colors: {
120
+ light: s(i, "light"),
121
+ dark: o ? s(o, "dark") : c(i)
122
+ },
123
+ background: a
124
+ };
125
+ }
126
+ var x = [
127
+ {
128
+ id: "midnight-glass",
129
+ name: "Midnight Glass",
130
+ radius: 1.4,
131
+ swatch: ["#1a6baa", "#22b8cf"],
132
+ colors: {
133
+ light: {
134
+ primary: "oklch(0.48 0.2 215.221)",
135
+ accent: "oklch(0.6 0.2 195)",
136
+ secondary: "oklch(0.96 0.01 250)",
137
+ border: "oklch(0.93 0.0094 286.2156)",
138
+ card: "oklch(0.99 0.008 285)",
139
+ background: "oklch(0.985 0.002 247.8763)",
140
+ popover: "oklch(0.9911 0 0)",
141
+ muted: "oklch(0.9702 0 0)",
142
+ mutedForeground: "oklch(0.4386 0 0)",
143
+ input: "oklch(0.9401 0 0)",
144
+ ring: "oklch(0.6132 0.2294 291.7437)",
145
+ destructive: "oklch(0.629 0.1902 23.0704)"
146
+ },
147
+ dark: {
148
+ primary: "oklch(0.48 0.2 215.221)",
149
+ accent: "oklch(0.6 0.2 195)",
150
+ secondary: "oklch(0.14 0.01 250)",
151
+ border: "oklch(0.26 0.012 250)",
152
+ card: "oklch(0.17 0.01 250)",
153
+ background: "oklch(0.12 0.01 250)",
154
+ popover: "oklch(0.16 0.005 0)",
155
+ muted: "oklch(0.22 0.006 0)",
156
+ mutedForeground: "oklch(0.7058 0 0)",
157
+ input: "oklch(0.25 0.005 0)",
158
+ ring: "oklch(0.6132 0.2294 291.7437)",
159
+ destructive: "oklch(0.7106 0.1661 22.2162)"
160
+ }
161
+ },
162
+ background: y("geometric-houses", { url: "/backgrounds/geometric-houses.svg" })
163
+ },
164
+ b("sunrise-studio", "Sunrise Studio", 1.2, ["#e8590c", "#fab005"], {
165
+ primary: "oklch(0.68 0.16 50)",
166
+ accent: "oklch(0.74 0.14 28)",
167
+ secondary: "oklch(0.92 0.04 82)",
168
+ border: "oklch(0.84 0.03 78)",
169
+ card: "oklch(0.96 0.02 85)",
170
+ background: "oklch(0.985 0.01 90)"
171
+ }, y("diagonal-stripes")),
172
+ b("forest-zen", "Forest Zen", 1.5, ["#2f9e44", "#12b886"], {
173
+ primary: "oklch(0.58 0.13 145)",
174
+ accent: "oklch(0.66 0.1 165)",
175
+ secondary: "oklch(0.9 0.03 150)",
176
+ border: "oklch(0.82 0.02 155)",
177
+ card: "oklch(0.95 0.01 150)",
178
+ background: "oklch(0.98 0.01 160)"
179
+ }, y("trees")),
180
+ b("lavender-dreams", "Lavender Dreams", 1.6, ["#9c36b5", "#e64980"], {
181
+ primary: "oklch(0.62 0.14 300)",
182
+ accent: "oklch(0.7 0.12 320)",
183
+ secondary: "oklch(0.93 0.03 290)",
184
+ border: "oklch(0.85 0.03 295)",
185
+ card: "oklch(0.97 0.02 300)",
186
+ background: "oklch(0.99 0.01 305)"
187
+ }, y("streets", { url: "/backgrounds/streets.svg" })),
188
+ b("coral-reef", "Coral Reef", 1.3, ["#e03131", "#fd7e14"], {
189
+ primary: "oklch(0.66 0.19 20)",
190
+ accent: "oklch(0.7 0.16 45)",
191
+ secondary: "oklch(0.91 0.04 35)",
192
+ border: "oklch(0.83 0.03 28)",
193
+ card: "oklch(0.96 0.02 25)",
194
+ background: "oklch(0.985 0.01 30)"
195
+ }, y("beauty-over-cliff", {
196
+ url: "/backgrounds/beauty-over-cliff.jpg",
197
+ themed: !1
198
+ })),
199
+ b("monochrome-pro", "Monochrome Pro", 0, ["#868e96", "#495057"], {
200
+ primary: "oklch(0.4 0.01 260)",
201
+ accent: "oklch(0.55 0.01 260)",
202
+ secondary: "oklch(0.94 0.005 260)",
203
+ border: "oklch(0.86 0.005 260)",
204
+ card: "oklch(0.98 0.003 260)",
205
+ background: "oklch(0.995 0.002 260)"
206
+ }, y("grid")),
207
+ b("ocean-breeze", "Ocean Breeze", 1.45, ["#1098ad", "#3b5bdb"], {
208
+ primary: "oklch(0.6 0.16 235)",
209
+ accent: "oklch(0.68 0.11 205)",
210
+ secondary: "oklch(0.92 0.03 225)",
211
+ border: "oklch(0.84 0.02 230)",
212
+ card: "oklch(0.97 0.01 230)",
213
+ background: "oklch(0.99 0.01 235)"
214
+ }, y("ride-the-wave", { url: "/backgrounds/ride-the-wave.svg" }))
215
+ ];
216
+ function S() {
217
+ return x;
218
+ }
219
+ function C(e) {
220
+ return x.find((t) => t.id === e);
221
+ }
222
+ //#endregion
223
+ export { g as DEFAULT_THEME_ID, v as FONT_MONO, _ as FONT_SANS, x as THEME_PRESETS, t as clamp, c as deriveDarkFromLight, u as ensureFullThemeColors, S as getAllPresets, C as getPreset, d as oklchToHex, r as parseOklch, s as resolveThemeColors, i as toOklch };
@@ -0,0 +1,17 @@
1
+ import { type BackgroundConfig, type ThemeColorsConfig } from "./theme-colors";
2
+ export interface ThemePreset {
3
+ id: string;
4
+ name: string;
5
+ radius: number;
6
+ colors: ThemeColorsConfig;
7
+ background: BackgroundConfig;
8
+ /** Curated two-stop gradient used for theme picker dots and marketing swatches. */
9
+ swatch: [string, string];
10
+ }
11
+ export declare const DEFAULT_THEME_ID = "midnight-glass";
12
+ /** Font stacks as shipped in styles/theme.css. */
13
+ export declare const FONT_SANS = "\"Geist Variable\", ui-sans-serif, system-ui, sans-serif";
14
+ export declare const FONT_MONO = "\"GeistMono Variable\", \"JetBrains Mono\", \"Fira Code\", Consolas, monospace";
15
+ export declare const THEME_PRESETS: ThemePreset[];
16
+ export declare function getAllPresets(): ThemePreset[];
17
+ export declare function getPreset(id: string): ThemePreset | undefined;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Theme color model + derivation. Framework-free: safe to import from servers,
3
+ * build scripts, and non-Solid renderers.
4
+ */
5
+ export interface ThemeColors {
6
+ primary: string;
7
+ accent: string;
8
+ secondary: string;
9
+ border: string;
10
+ card: string;
11
+ background: string;
12
+ muted: string;
13
+ mutedForeground: string;
14
+ input: string;
15
+ ring: string;
16
+ destructive: string;
17
+ popover: string;
18
+ }
19
+ export interface ThemeColorsConfig {
20
+ light: ThemeColors;
21
+ dark: ThemeColors;
22
+ }
23
+ export interface BackgroundConfig {
24
+ type: "solid" | "static" | "custom";
25
+ id: string;
26
+ themed: boolean;
27
+ url?: string;
28
+ fit?: "cover" | "contain" | "tile";
29
+ overlay?: number;
30
+ blur?: number;
31
+ }
32
+ /** The 6 base colors used as input for resolving a full ThemeColors */
33
+ export type ThemeBaseColors = Pick<ThemeColors, "primary" | "accent" | "secondary" | "border" | "card" | "background">;
34
+ export declare function clamp(value: number, min: number, max: number): number;
35
+ export declare function parseOklch(input: string): {
36
+ l: number;
37
+ c: number;
38
+ h: number;
39
+ } | null;
40
+ export declare function toOklch(l: number, c: number, h: number): string;
41
+ /**
42
+ * Resolve the 6 base colors into a full ThemeColors.
43
+ * Used for presets and for backwards-compat with old DB data.
44
+ */
45
+ export declare function resolveThemeColors(base: ThemeBaseColors, mode: "light" | "dark"): ThemeColors;
46
+ export declare function deriveDarkFromLight(light: ThemeBaseColors): ThemeColors;
47
+ /**
48
+ * Ensure a potentially partial colors object (from old DB data) has all fields.
49
+ * Existing values are preserved; missing ones are resolved from the base colors.
50
+ */
51
+ export declare function ensureFullThemeColors(partial: ThemeBaseColors & Partial<ThemeColors>, mode: "light" | "dark"): ThemeColors;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glasshome/ui",
3
- "version": "0.3.3",
3
+ "version": "0.4.0",
4
4
  "description": "SolidJS component library for GlassHome, built on Kobalte",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -37,6 +37,11 @@
37
37
  "types": "./dist/solid/index.d.ts",
38
38
  "import": "./dist/solid/index.js"
39
39
  },
40
+ "./tokens": {
41
+ "@glasshome/source": "./src/tokens/index.ts",
42
+ "types": "./dist/tokens/index.d.ts",
43
+ "import": "./dist/tokens/index.js"
44
+ },
40
45
  "./styles": "./src/styles/globals.css",
41
46
  "./package.json": "./package.json",
42
47
  "./styles/theme": "./src/styles/theme.css"
@@ -47,6 +52,7 @@
47
52
  ],
48
53
  "scripts": {
49
54
  "build": "vite build && tsc",
55
+ "check:tokens": "bun scripts/check-tokens.ts",
50
56
  "dev": "vite build --watch",
51
57
  "lint": "biome check ."
52
58
  },