@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 CHANGED
@@ -1,45 +1,113 @@
1
- # Sage UI
1
+ # Sage Design Engine (@thesage/ui)
2
2
 
3
- **The Solopreneur's Development Stack.**
3
+ <div align="center">
4
4
 
5
- `@thesage/ui` is the core component library for the Sage ecosystem. It provides a comprehensive set of accessible, reusable, and composable UI components built on top of Radix UI and Tailwind CSS.
5
+ [![npm version](https://img.shields.io/npm/v/@thesage/ui?color=indigo&style=flat-square)](https://www.npmjs.com/package/@thesage/ui)
6
+ [![License](https://img.shields.io/npm/l/@thesage/ui?color=blue&style=flat-square)](https://github.com/shalomormsby/ecosystem/blob/main/LICENSE)
7
+ [![Downloads](https://img.shields.io/npm/dt/@thesage/ui?color=teal&style=flat-square)](https://www.npmjs.com/package/@thesage/ui)
6
8
 
7
- ## Features
9
+ **The Design Engine for the Solopreneur.**
8
10
 
9
- - 🎨 **Systematic Design**: Built on a robust token system for consistent implementation.
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
- ## Installation
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
- npm install @thesage/ui
19
- # or
20
- pnpm add @thesage/ui
21
- # or
22
- yarn add @thesage/ui
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
- ## Usage
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
- import { Button } from '@thesage/ui';
90
+ // Example: Customizing the theme
91
+ import { ThemeProvider } from '@thesage/ui';
29
92
 
30
- export default function MyComponent() {
93
+ export default function App({ children }) {
31
94
  return (
32
- <Button variant="primary" onClick={() => console.log('Clicked!')}>
33
- Hello World
34
- </Button>
95
+ <ThemeProvider theme="sage" defaultMode="system">
96
+ {children}
97
+ </ThemeProvider>
35
98
  );
36
99
  }
37
100
  ```
38
101
 
39
- ## Documentation
102
+ ## πŸ“¦ Component Categories
40
103
 
41
- For full documentation, component examples, and guides, visit **[thesage.dev](https://thesage.dev)**.
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 = "#000000"
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
- const vec3 baseColor1 = vec3(0.611765, 0.262745, 0.996078);
9985
- const vec3 baseColor2 = vec3(0.298039, 0.760784, 0.913725);
9986
- const vec3 baseColor3 = vec3(0.062745, 0.078431, 0.600000);
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
- vec3 color1 = adjustHue(baseColor1, hue);
10000
- vec3 color2 = adjustHue(baseColor2, hue);
10001
- vec3 color3 = adjustHue(baseColor3, hue);
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
- backgroundColor: { value: hexToVec3(backgroundColor) }
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);