@thesage/ui 0.0.11 β 0.0.13
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 +92 -24
- package/dist/index.d.mts +21 -2
- package/dist/index.d.ts +21 -2
- package/dist/index.js +91 -51
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +91 -51
- package/dist/index.mjs.map +1 -1
- package/dist/providers.js +44 -0
- package/dist/providers.js.map +1 -1
- package/dist/providers.mjs +44 -0
- package/dist/providers.mjs.map +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -1,45 +1,113 @@
|
|
|
1
|
-
# Sage
|
|
1
|
+
# Sage Design Engine (@thesage/ui)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<div align="center">
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/@thesage/ui)
|
|
6
|
+
[](https://github.com/shalomormsby/ecosystem/blob/main/LICENSE)
|
|
7
|
+
[](https://www.npmjs.com/package/@thesage/ui)
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
**The Design Engine for the Solopreneur.**
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
- βΏ **Accessible**: rigorous adherence to WAI-ARIA standards (via Radix UI).
|
|
11
|
-
- π **Dark Mode**: First-class support for light and dark themes.
|
|
12
|
-
- π§© **Composable**: components are designed to be composed together to build complex interfaces.
|
|
13
|
-
- π **Performance**: Exported as tree-shakeable ESM modules.
|
|
11
|
+
[Documentation](https://thesage.dev) β’ [Components](https://thesage.dev/components) β’ [GitHub](https://github.com/shalomormsby/ecosystem)
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
**Sage Design Engine** is not just a component libraryβit's a systematic design engine built for speed, consistency, and beauty. Built on top of **Radix UI** for headless accessibility and **Tailwind CSS** for styling, it provides a comprehensive suite of 45+ polished components that work together seamlessly.
|
|
18
|
+
|
|
19
|
+
## β¨ Features
|
|
20
|
+
|
|
21
|
+
- **π¨ Systematic Design**: Powered by a robust design token system (colors, typography, spacing).
|
|
22
|
+
- **βΏ Fully Accessible**: Built on WAI-ARIA standards via Radix UI primitives.
|
|
23
|
+
- **π Mode Aware**: First-class support for light and dark modes with automatic color harmonization.
|
|
24
|
+
- **π§© Composable**: Components designed to fit together like LEGO blocks.
|
|
25
|
+
- **π οΈ Type Safe**: Written in TypeScript with full type inference.
|
|
26
|
+
|
|
27
|
+
## π Installation
|
|
28
|
+
|
|
29
|
+
### 1. Install Dependencies
|
|
30
|
+
Sage Design Engine is built on Tailwind CSS. You need to install the package and its peer dependencies.
|
|
16
31
|
|
|
17
32
|
```bash
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
33
|
+
pnpm add @thesage/ui @thesage/tokens @thesage/hooks lucide-react clsx tailwind-merge
|
|
34
|
+
pnpm add -D tailwindcss@^3.4 postcss autoprefixer
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### 2. Configure Tailwind
|
|
38
|
+
Update your `tailwind.config.js` contents to use the preset and scan the component definitions.
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
/** @type {import('tailwindcss').Config} */
|
|
42
|
+
module.exports = {
|
|
43
|
+
presets: [require('@thesage/config/tailwind')],
|
|
44
|
+
content: [
|
|
45
|
+
"./src/**/*.{ts,tsx}",
|
|
46
|
+
"./node_modules/@thesage/ui/dist/**/*.{js,ts,jsx,tsx}"
|
|
47
|
+
],
|
|
48
|
+
theme: {
|
|
49
|
+
extend: {},
|
|
50
|
+
},
|
|
51
|
+
plugins: [],
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 3. Import Styles
|
|
56
|
+
Import the global CSS file (which contains the theme variables) in your root entry file (e.g., `main.tsx` or `App.tsx`).
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import '@thesage/ui/globals.css';
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## π» Usage
|
|
63
|
+
|
|
64
|
+
Sage Design Engine components are designed to be dropped into any React application.
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
import { Button, Card, Text, Heading } from '@thesage/ui';
|
|
68
|
+
|
|
69
|
+
export default function WelcomeCard() {
|
|
70
|
+
return (
|
|
71
|
+
<Card className="max-w-md p-6">
|
|
72
|
+
<Heading level={3} className="mb-2">Welcome to Sage</Heading>
|
|
73
|
+
<Text variant="muted" className="mb-4">
|
|
74
|
+
Build faster with components that look premium out of the box.
|
|
75
|
+
</Text>
|
|
76
|
+
<div className="flex gap-2">
|
|
77
|
+
<Button variant="primary">Get Started</Button>
|
|
78
|
+
<Button variant="ghost">Documentation</Button>
|
|
79
|
+
</div>
|
|
80
|
+
</Card>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
23
83
|
```
|
|
24
84
|
|
|
25
|
-
##
|
|
85
|
+
## ποΈ Theming
|
|
86
|
+
|
|
87
|
+
Sage Design Engine uses a 4-layer token system. Changing a single primary color automatically updates buttons, focus rings, and chart colors across your entire application.
|
|
26
88
|
|
|
27
89
|
```tsx
|
|
28
|
-
|
|
90
|
+
// Example: Customizing the theme
|
|
91
|
+
import { ThemeProvider } from '@thesage/ui';
|
|
29
92
|
|
|
30
|
-
export default function
|
|
93
|
+
export default function App({ children }) {
|
|
31
94
|
return (
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
</
|
|
95
|
+
<ThemeProvider theme="sage" defaultMode="system">
|
|
96
|
+
{children}
|
|
97
|
+
</ThemeProvider>
|
|
35
98
|
);
|
|
36
99
|
}
|
|
37
100
|
```
|
|
38
101
|
|
|
39
|
-
##
|
|
102
|
+
## π¦ Component Categories
|
|
40
103
|
|
|
41
|
-
|
|
104
|
+
- **Actions**: Button, Toggle, ToggleGroup
|
|
105
|
+
- **Forms**: Input, Select, Checkbox, Switch, Slider, Form
|
|
106
|
+
- **Navigation**: Tabs, Menubar, Breadcrumb, Pagination
|
|
107
|
+
- **Overlays**: Dialog, Sheet, Popover, Tooltip, Toast
|
|
108
|
+
- **Data Display**: Card, Avatar, Badge, Table, ScrollArea
|
|
109
|
+
- **Feedback**: Alert, Progress, Skeleton, Sonner
|
|
42
110
|
|
|
43
|
-
## License
|
|
111
|
+
## π License
|
|
44
112
|
|
|
45
113
|
MIT Β© [Shalom Ormsby](https://github.com/shalomormsby)
|
package/dist/index.d.mts
CHANGED
|
@@ -54,7 +54,7 @@ export { F as FieldValidation, a as FormErrors, V as ValidationRule, h as hasErr
|
|
|
54
54
|
import 'clsx';
|
|
55
55
|
|
|
56
56
|
declare const buttonVariants: (props?: ({
|
|
57
|
-
variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link" | null | undefined;
|
|
57
|
+
variant?: "default" | "primary" | "destructive" | "outline" | "secondary" | "ghost" | "link" | null | undefined;
|
|
58
58
|
size?: "default" | "sm" | "lg" | "icon" | null | undefined;
|
|
59
59
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
60
60
|
interface ButtonProps extends React$1.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
@@ -2207,9 +2207,28 @@ interface OrbBackgroundProps {
|
|
|
2207
2207
|
hoverIntensity?: number;
|
|
2208
2208
|
rotateOnHover?: boolean;
|
|
2209
2209
|
forceHoverState?: boolean;
|
|
2210
|
+
/**
|
|
2211
|
+
* Background color (hex). If not provided, uses CSS variable --color-background.
|
|
2212
|
+
* @default undefined (reads from theme)
|
|
2213
|
+
*/
|
|
2210
2214
|
backgroundColor?: string;
|
|
2215
|
+
/**
|
|
2216
|
+
* Primary orb color (hex). Creates the main vibrant tone.
|
|
2217
|
+
* @default '#9c43fe' (purple)
|
|
2218
|
+
*/
|
|
2219
|
+
orbColor1?: string;
|
|
2220
|
+
/**
|
|
2221
|
+
* Secondary orb color (hex). Adds cool cyan tones.
|
|
2222
|
+
* @default '#4cc2e9' (cyan)
|
|
2223
|
+
*/
|
|
2224
|
+
orbColor2?: string;
|
|
2225
|
+
/**
|
|
2226
|
+
* Tertiary orb color (hex). Provides deep blue accent tones.
|
|
2227
|
+
* @default '#101499' (deep blue)
|
|
2228
|
+
*/
|
|
2229
|
+
orbColor3?: string;
|
|
2211
2230
|
}
|
|
2212
|
-
declare function OrbBackground({ className, hue, hoverIntensity, rotateOnHover, forceHoverState, backgroundColor }: OrbBackgroundProps): react_jsx_runtime.JSX.Element;
|
|
2231
|
+
declare function OrbBackground({ className, hue, hoverIntensity, rotateOnHover, forceHoverState, backgroundColor, orbColor1, orbColor2, orbColor3, }: OrbBackgroundProps): react_jsx_runtime.JSX.Element;
|
|
2213
2232
|
|
|
2214
2233
|
interface HeroBlockProps {
|
|
2215
2234
|
className?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -54,7 +54,7 @@ export { F as FieldValidation, a as FormErrors, V as ValidationRule, h as hasErr
|
|
|
54
54
|
import 'clsx';
|
|
55
55
|
|
|
56
56
|
declare const buttonVariants: (props?: ({
|
|
57
|
-
variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link" | null | undefined;
|
|
57
|
+
variant?: "default" | "primary" | "destructive" | "outline" | "secondary" | "ghost" | "link" | null | undefined;
|
|
58
58
|
size?: "default" | "sm" | "lg" | "icon" | null | undefined;
|
|
59
59
|
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
60
60
|
interface ButtonProps extends React$1.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
@@ -2207,9 +2207,28 @@ interface OrbBackgroundProps {
|
|
|
2207
2207
|
hoverIntensity?: number;
|
|
2208
2208
|
rotateOnHover?: boolean;
|
|
2209
2209
|
forceHoverState?: boolean;
|
|
2210
|
+
/**
|
|
2211
|
+
* Background color (hex). If not provided, uses CSS variable --color-background.
|
|
2212
|
+
* @default undefined (reads from theme)
|
|
2213
|
+
*/
|
|
2210
2214
|
backgroundColor?: string;
|
|
2215
|
+
/**
|
|
2216
|
+
* Primary orb color (hex). Creates the main vibrant tone.
|
|
2217
|
+
* @default '#9c43fe' (purple)
|
|
2218
|
+
*/
|
|
2219
|
+
orbColor1?: string;
|
|
2220
|
+
/**
|
|
2221
|
+
* Secondary orb color (hex). Adds cool cyan tones.
|
|
2222
|
+
* @default '#4cc2e9' (cyan)
|
|
2223
|
+
*/
|
|
2224
|
+
orbColor2?: string;
|
|
2225
|
+
/**
|
|
2226
|
+
* Tertiary orb color (hex). Provides deep blue accent tones.
|
|
2227
|
+
* @default '#101499' (deep blue)
|
|
2228
|
+
*/
|
|
2229
|
+
orbColor3?: string;
|
|
2211
2230
|
}
|
|
2212
|
-
declare function OrbBackground({ className, hue, hoverIntensity, rotateOnHover, forceHoverState, backgroundColor }: OrbBackgroundProps): react_jsx_runtime.JSX.Element;
|
|
2231
|
+
declare function OrbBackground({ className, hue, hoverIntensity, rotateOnHover, forceHoverState, backgroundColor, orbColor1, orbColor2, orbColor3, }: OrbBackgroundProps): react_jsx_runtime.JSX.Element;
|
|
2213
2232
|
|
|
2214
2233
|
interface HeroBlockProps {
|
|
2215
2234
|
className?: string;
|
package/dist/index.js
CHANGED
|
@@ -387,6 +387,8 @@ var buttonVariants = (0, import_class_variance_authority.cva)(
|
|
|
387
387
|
variants: {
|
|
388
388
|
variant: {
|
|
389
389
|
default: "bg-primary text-primary-foreground shadow",
|
|
390
|
+
primary: "bg-primary text-primary-foreground shadow",
|
|
391
|
+
// Alias for default
|
|
390
392
|
destructive: "bg-destructive text-destructive-foreground shadow-sm",
|
|
391
393
|
outline: "border border-input bg-transparent shadow-sm hover:bg-primary hover:text-primary-foreground hover:border-primary",
|
|
392
394
|
secondary: "bg-black/5 dark:bg-white/10 backdrop-blur-md border border-black/5 dark:border-white/10 text-secondary-foreground shadow-sm hover:bg-primary hover:text-primary-foreground dark:hover:bg-primary dark:hover:text-primary-foreground",
|
|
@@ -9878,13 +9880,30 @@ var AnimatedBeam = ({
|
|
|
9878
9880
|
var import_ogl3 = require("ogl");
|
|
9879
9881
|
var import_react36 = require("react");
|
|
9880
9882
|
var import_jsx_runtime85 = require("react/jsx-runtime");
|
|
9883
|
+
function hexToRgb4(hex) {
|
|
9884
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
9885
|
+
if (!result) return [0, 0, 0];
|
|
9886
|
+
return [
|
|
9887
|
+
parseInt(result[1], 16) / 255,
|
|
9888
|
+
parseInt(result[2], 16) / 255,
|
|
9889
|
+
parseInt(result[3], 16) / 255
|
|
9890
|
+
];
|
|
9891
|
+
}
|
|
9892
|
+
function getCSSVariable2(name, fallback) {
|
|
9893
|
+
if (typeof window === "undefined") return fallback;
|
|
9894
|
+
const value = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
9895
|
+
return value || fallback;
|
|
9896
|
+
}
|
|
9881
9897
|
function OrbBackground({
|
|
9882
9898
|
className,
|
|
9883
9899
|
hue = 0,
|
|
9884
9900
|
hoverIntensity = 0.2,
|
|
9885
9901
|
rotateOnHover = true,
|
|
9886
9902
|
forceHoverState = false,
|
|
9887
|
-
backgroundColor
|
|
9903
|
+
backgroundColor,
|
|
9904
|
+
orbColor1,
|
|
9905
|
+
orbColor2,
|
|
9906
|
+
orbColor3
|
|
9888
9907
|
}) {
|
|
9889
9908
|
const ctnDom = (0, import_react36.useRef)(null);
|
|
9890
9909
|
const vert = (
|
|
@@ -9912,6 +9931,10 @@ function OrbBackground({
|
|
|
9912
9931
|
uniform float rot;
|
|
9913
9932
|
uniform float hoverIntensity;
|
|
9914
9933
|
uniform vec3 backgroundColor;
|
|
9934
|
+
// THEME-AWARE: Orb colors from theme (to revert: change back to const)
|
|
9935
|
+
uniform vec3 orbColor1;
|
|
9936
|
+
uniform vec3 orbColor2;
|
|
9937
|
+
uniform vec3 orbColor3;
|
|
9915
9938
|
varying vec2 vUv;
|
|
9916
9939
|
|
|
9917
9940
|
vec3 rgb2yiq(vec3 c) {
|
|
@@ -9980,10 +10003,12 @@ function OrbBackground({
|
|
|
9980
10003
|
float a = max(max(colorIn.r, colorIn.g), colorIn.b);
|
|
9981
10004
|
return vec4(colorIn.rgb / (a + 1e-5), a);
|
|
9982
10005
|
}
|
|
9983
|
-
|
|
9984
|
-
|
|
9985
|
-
|
|
9986
|
-
const vec3
|
|
10006
|
+
|
|
10007
|
+
// THEME-AWARE: Colors now come from uniforms (orbColor1, orbColor2, orbColor3)
|
|
10008
|
+
// To revert to hardcoded: uncomment these and remove uniforms above
|
|
10009
|
+
// const vec3 baseColor1 = vec3(0.611765, 0.262745, 0.996078);
|
|
10010
|
+
// const vec3 baseColor2 = vec3(0.298039, 0.760784, 0.913725);
|
|
10011
|
+
// const vec3 baseColor3 = vec3(0.062745, 0.078431, 0.600000);
|
|
9987
10012
|
const float innerRadius = 0.6;
|
|
9988
10013
|
const float noiseScale = 0.65;
|
|
9989
10014
|
|
|
@@ -9996,9 +10021,10 @@ function OrbBackground({
|
|
|
9996
10021
|
}
|
|
9997
10022
|
|
|
9998
10023
|
vec4 draw(vec2 uv) {
|
|
9999
|
-
|
|
10000
|
-
vec3
|
|
10001
|
-
vec3
|
|
10024
|
+
// THEME-AWARE: Use uniform colors instead of hardcoded consts
|
|
10025
|
+
vec3 color1 = adjustHue(orbColor1, hue);
|
|
10026
|
+
vec3 color2 = adjustHue(orbColor2, hue);
|
|
10027
|
+
vec3 color3 = adjustHue(orbColor3, hue);
|
|
10002
10028
|
|
|
10003
10029
|
float ang = atan(uv.y, uv.x);
|
|
10004
10030
|
float len = length(uv);
|
|
@@ -10067,6 +10093,14 @@ function OrbBackground({
|
|
|
10067
10093
|
(0, import_react36.useEffect)(() => {
|
|
10068
10094
|
const container = ctnDom.current;
|
|
10069
10095
|
if (!container) return;
|
|
10096
|
+
const bgColor = backgroundColor || getCSSVariable2("--color-background", "#000000");
|
|
10097
|
+
const color1 = orbColor1 || "#9c43fe";
|
|
10098
|
+
const color2 = orbColor2 || "#4cc2e9";
|
|
10099
|
+
const color3 = orbColor3 || "#101499";
|
|
10100
|
+
const bgRgb = hexToRgb4(bgColor);
|
|
10101
|
+
const color1Rgb = hexToRgb4(color1);
|
|
10102
|
+
const color2Rgb = hexToRgb4(color2);
|
|
10103
|
+
const color3Rgb = hexToRgb4(color3);
|
|
10070
10104
|
const renderer = new import_ogl3.Renderer({ alpha: true, premultipliedAlpha: false });
|
|
10071
10105
|
const gl = renderer.gl;
|
|
10072
10106
|
gl.clearColor(0, 0, 0, 0);
|
|
@@ -10084,7 +10118,11 @@ function OrbBackground({
|
|
|
10084
10118
|
hover: { value: 0 },
|
|
10085
10119
|
rot: { value: 0 },
|
|
10086
10120
|
hoverIntensity: { value: hoverIntensity },
|
|
10087
|
-
|
|
10121
|
+
// THEME-AWARE: Pass theme colors to shader
|
|
10122
|
+
backgroundColor: { value: new import_ogl3.Vec3(...bgRgb) },
|
|
10123
|
+
orbColor1: { value: new import_ogl3.Vec3(...color1Rgb) },
|
|
10124
|
+
orbColor2: { value: new import_ogl3.Vec3(...color2Rgb) },
|
|
10125
|
+
orbColor3: { value: new import_ogl3.Vec3(...color3Rgb) }
|
|
10088
10126
|
}
|
|
10089
10127
|
});
|
|
10090
10128
|
const mesh = new import_ogl3.Mesh(gl, { geometry, program });
|
|
@@ -10141,7 +10179,6 @@ function OrbBackground({
|
|
|
10141
10179
|
currentRot += dt * rotationSpeed;
|
|
10142
10180
|
}
|
|
10143
10181
|
program.uniforms.rot.value = currentRot;
|
|
10144
|
-
program.uniforms.backgroundColor.value = hexToVec3(backgroundColor);
|
|
10145
10182
|
renderer.render({ scene: mesh });
|
|
10146
10183
|
};
|
|
10147
10184
|
rafId = requestAnimationFrame(update);
|
|
@@ -10158,47 +10195,6 @@ function OrbBackground({
|
|
|
10158
10195
|
}, [hue, hoverIntensity, rotateOnHover, forceHoverState, backgroundColor]);
|
|
10159
10196
|
return /* @__PURE__ */ (0, import_jsx_runtime85.jsx)("div", { ref: ctnDom, className: cn("w-full h-full pointer-events-auto", className) });
|
|
10160
10197
|
}
|
|
10161
|
-
function hslToRgb(h, s, l) {
|
|
10162
|
-
let r, g, b;
|
|
10163
|
-
if (s === 0) {
|
|
10164
|
-
r = g = b = l;
|
|
10165
|
-
} else {
|
|
10166
|
-
const hue2rgb = (p2, q2, t) => {
|
|
10167
|
-
if (t < 0) t += 1;
|
|
10168
|
-
if (t > 1) t -= 1;
|
|
10169
|
-
if (t < 1 / 6) return p2 + (q2 - p2) * 6 * t;
|
|
10170
|
-
if (t < 1 / 2) return q2;
|
|
10171
|
-
if (t < 2 / 3) return p2 + (q2 - p2) * (2 / 3 - t) * 6;
|
|
10172
|
-
return p2;
|
|
10173
|
-
};
|
|
10174
|
-
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
10175
|
-
const p = 2 * l - q;
|
|
10176
|
-
r = hue2rgb(p, q, h + 1 / 3);
|
|
10177
|
-
g = hue2rgb(p, q, h);
|
|
10178
|
-
b = hue2rgb(p, q, h - 1 / 3);
|
|
10179
|
-
}
|
|
10180
|
-
return new import_ogl3.Vec3(r, g, b);
|
|
10181
|
-
}
|
|
10182
|
-
function hexToVec3(color) {
|
|
10183
|
-
if (color.startsWith("#")) {
|
|
10184
|
-
const r = parseInt(color.slice(1, 3), 16) / 255;
|
|
10185
|
-
const g = parseInt(color.slice(3, 5), 16) / 255;
|
|
10186
|
-
const b = parseInt(color.slice(5, 7), 16) / 255;
|
|
10187
|
-
return new import_ogl3.Vec3(r, g, b);
|
|
10188
|
-
}
|
|
10189
|
-
const rgbMatch = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
|
|
10190
|
-
if (rgbMatch) {
|
|
10191
|
-
return new import_ogl3.Vec3(parseInt(rgbMatch[1]) / 255, parseInt(rgbMatch[2]) / 255, parseInt(rgbMatch[3]) / 255);
|
|
10192
|
-
}
|
|
10193
|
-
const hslMatch = color.match(/hsla?\((\d+),\s*(\d+)%,\s*(\d+)%/);
|
|
10194
|
-
if (hslMatch) {
|
|
10195
|
-
const h = parseInt(hslMatch[1]) / 360;
|
|
10196
|
-
const s = parseInt(hslMatch[2]) / 100;
|
|
10197
|
-
const l = parseInt(hslMatch[3]) / 100;
|
|
10198
|
-
return hslToRgb(h, s, l);
|
|
10199
|
-
}
|
|
10200
|
-
return new import_ogl3.Vec3(0, 0, 0);
|
|
10201
|
-
}
|
|
10202
10198
|
|
|
10203
10199
|
// src/components/blocks/Hero.tsx
|
|
10204
10200
|
var import_lucide_react22 = require("lucide-react");
|
|
@@ -11677,6 +11673,49 @@ function mergeCustomColorTokens(baseTokens2, customPalette) {
|
|
|
11677
11673
|
...customPalette.derivedTokens
|
|
11678
11674
|
};
|
|
11679
11675
|
}
|
|
11676
|
+
function validateThemeTokens(theme, mode) {
|
|
11677
|
+
if (typeof process !== "undefined" && process.env?.NODE_ENV === "production") return;
|
|
11678
|
+
const root = document.documentElement;
|
|
11679
|
+
const style = getComputedStyle(root);
|
|
11680
|
+
const requiredTokens = [
|
|
11681
|
+
"--color-background",
|
|
11682
|
+
"--color-foreground",
|
|
11683
|
+
"--color-primary",
|
|
11684
|
+
"--color-primary-foreground",
|
|
11685
|
+
"--color-border",
|
|
11686
|
+
"--color-ring",
|
|
11687
|
+
"--font-heading",
|
|
11688
|
+
"--font-body",
|
|
11689
|
+
"--font-mono"
|
|
11690
|
+
];
|
|
11691
|
+
const missingTokens = [];
|
|
11692
|
+
const invalidTokens = [];
|
|
11693
|
+
requiredTokens.forEach((token) => {
|
|
11694
|
+
const value = style.getPropertyValue(token).trim();
|
|
11695
|
+
if (!value) {
|
|
11696
|
+
missingTokens.push(token);
|
|
11697
|
+
} else if (token.startsWith("--color-") && !value.match(/^(#|rgb|hsl|var\()/)) {
|
|
11698
|
+
invalidTokens.push(`${token} = "${value}"`);
|
|
11699
|
+
} else if (token.startsWith("--font-") && value === "") {
|
|
11700
|
+
invalidTokens.push(`${token} = empty`);
|
|
11701
|
+
}
|
|
11702
|
+
});
|
|
11703
|
+
if (missingTokens.length > 0) {
|
|
11704
|
+
console.warn(
|
|
11705
|
+
`[ThemeProvider] Missing CSS variables for theme "${theme}" (${mode} mode):`,
|
|
11706
|
+
missingTokens
|
|
11707
|
+
);
|
|
11708
|
+
}
|
|
11709
|
+
if (invalidTokens.length > 0) {
|
|
11710
|
+
console.warn(
|
|
11711
|
+
`[ThemeProvider] Invalid CSS variable values for theme "${theme}" (${mode} mode):`,
|
|
11712
|
+
invalidTokens
|
|
11713
|
+
);
|
|
11714
|
+
}
|
|
11715
|
+
if (missingTokens.length === 0 && invalidTokens.length === 0) {
|
|
11716
|
+
console.log(`[ThemeProvider] \u2713 Theme validation passed for "${theme}" (${mode} mode)`);
|
|
11717
|
+
}
|
|
11718
|
+
}
|
|
11680
11719
|
function ThemeProvider({ children }) {
|
|
11681
11720
|
const { theme, mode } = useThemeStore();
|
|
11682
11721
|
const customPalette = useCustomizer((state) => state.customColors?.[theme]?.[mode]);
|
|
@@ -11710,6 +11749,7 @@ function ThemeProvider({ children }) {
|
|
|
11710
11749
|
} else {
|
|
11711
11750
|
root.classList.remove("dark");
|
|
11712
11751
|
}
|
|
11752
|
+
validateThemeTokens(theme, mode);
|
|
11713
11753
|
const timeout = setTimeout(() => {
|
|
11714
11754
|
root.classList.remove("theme-transitioning");
|
|
11715
11755
|
setIsTransitioning(false);
|