@opencosmos/ui 1.3.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.
- package/.claude/CLAUDE.md +239 -0
- package/README.md +161 -0
- package/dist/cli.mjs +151 -0
- package/dist/dates.d.mts +20 -0
- package/dist/dates.d.ts +20 -0
- package/dist/dates.js +240 -0
- package/dist/dates.js.map +1 -0
- package/dist/dates.mjs +203 -0
- package/dist/dates.mjs.map +1 -0
- package/dist/dnd.d.mts +126 -0
- package/dist/dnd.d.ts +126 -0
- package/dist/dnd.js +274 -0
- package/dist/dnd.js.map +1 -0
- package/dist/dnd.mjs +250 -0
- package/dist/dnd.mjs.map +1 -0
- package/dist/fontThemes-Dh8mtXES.d.mts +868 -0
- package/dist/fontThemes-Dh8mtXES.d.ts +868 -0
- package/dist/forms.d.mts +38 -0
- package/dist/forms.d.ts +38 -0
- package/dist/forms.js +198 -0
- package/dist/forms.js.map +1 -0
- package/dist/forms.mjs +159 -0
- package/dist/forms.mjs.map +1 -0
- package/dist/hooks-1b8WaQf1.d.mts +225 -0
- package/dist/hooks-CKW8vE9H.d.ts +225 -0
- package/dist/hooks.d.mts +3 -0
- package/dist/hooks.d.ts +3 -0
- package/dist/hooks.js +971 -0
- package/dist/hooks.js.map +1 -0
- package/dist/hooks.mjs +943 -0
- package/dist/hooks.mjs.map +1 -0
- package/dist/index-DscTIrZ2.d.mts +29 -0
- package/dist/index-DscTIrZ2.d.ts +29 -0
- package/dist/index.d.mts +3382 -0
- package/dist/index.d.ts +3382 -0
- package/dist/index.js +15146 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +14802 -0
- package/dist/index.mjs.map +1 -0
- package/dist/providers-CXPDMsl7.d.mts +30 -0
- package/dist/providers-Dn_Msjvz.d.ts +30 -0
- package/dist/providers.d.mts +3 -0
- package/dist/providers.d.ts +3 -0
- package/dist/providers.js +1885 -0
- package/dist/providers.js.map +1 -0
- package/dist/providers.mjs +1859 -0
- package/dist/providers.mjs.map +1 -0
- package/dist/tables.d.mts +10 -0
- package/dist/tables.d.ts +10 -0
- package/dist/tables.js +248 -0
- package/dist/tables.js.map +1 -0
- package/dist/tables.mjs +218 -0
- package/dist/tables.mjs.map +1 -0
- package/dist/tokens.d.mts +1065 -0
- package/dist/tokens.d.ts +1065 -0
- package/dist/tokens.js +2637 -0
- package/dist/tokens.js.map +1 -0
- package/dist/tokens.mjs +2555 -0
- package/dist/tokens.mjs.map +1 -0
- package/dist/utils-CIIM7dAC.d.ts +986 -0
- package/dist/utils-Cs04sxth.d.mts +986 -0
- package/dist/utils.d.mts +4 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.js +874 -0
- package/dist/utils.js.map +1 -0
- package/dist/utils.mjs +806 -0
- package/dist/utils.mjs.map +1 -0
- package/dist/validation-Bj1ye-v_.d.mts +114 -0
- package/dist/validation-Bj1ye-v_.d.ts +114 -0
- package/dist/webgl.d.mts +104 -0
- package/dist/webgl.d.ts +104 -0
- package/dist/webgl.js +226 -0
- package/dist/webgl.js.map +1 -0
- package/dist/webgl.mjs +195 -0
- package/dist/webgl.mjs.map +1 -0
- package/package.json +267 -0
- package/src/cli.ts +206 -0
- package/src/component-registry.ts +183 -0
- package/src/components/actions/Button.test.tsx +61 -0
- package/src/components/actions/Button.tsx +70 -0
- package/src/components/actions/Link.tsx +78 -0
- package/src/components/actions/Magnetic.tsx +68 -0
- package/src/components/actions/Toggle.test.tsx +40 -0
- package/src/components/actions/Toggle.tsx +47 -0
- package/src/components/actions/ToggleGroup.tsx +70 -0
- package/src/components/actions/index.ts +5 -0
- package/src/components/backgrounds/FaultyTerminal.tsx +426 -0
- package/src/components/backgrounds/OrbBackground.tsx +424 -0
- package/src/components/backgrounds/WarpBackground.tsx +358 -0
- package/src/components/backgrounds/index.ts +3 -0
- package/src/components/blocks/Hero.tsx +142 -0
- package/src/components/blocks/social/OpenGraphCard.tsx +243 -0
- package/src/components/cursor/SplashCursor.tsx +1315 -0
- package/src/components/cursor/TargetCursor.tsx +187 -0
- package/src/components/cursor/index.ts +2 -0
- package/src/components/data-display/AspectImage.tsx +73 -0
- package/src/components/data-display/Avatar.test.tsx +35 -0
- package/src/components/data-display/Avatar.tsx +55 -0
- package/src/components/data-display/Badge.test.tsx +43 -0
- package/src/components/data-display/Badge.tsx +84 -0
- package/src/components/data-display/Brand.tsx +123 -0
- package/src/components/data-display/Calendar.tsx +70 -0
- package/src/components/data-display/Card.test.tsx +92 -0
- package/src/components/data-display/Card.tsx +115 -0
- package/src/components/data-display/Code.tsx +210 -0
- package/src/components/data-display/CollapsibleCodeBlock.tsx +238 -0
- package/src/components/data-display/DataTable.tsx +119 -0
- package/src/components/data-display/DescriptionList.tsx +41 -0
- package/src/components/data-display/GitHubIcon.tsx +44 -0
- package/src/components/data-display/Heading.test.tsx +36 -0
- package/src/components/data-display/Heading.tsx +83 -0
- package/src/components/data-display/StatCard.tsx +195 -0
- package/src/components/data-display/Table.tsx +133 -0
- package/src/components/data-display/Text.test.tsx +48 -0
- package/src/components/data-display/Text.tsx +144 -0
- package/src/components/data-display/Timeline.tsx +194 -0
- package/src/components/data-display/TreeView.tsx +226 -0
- package/src/components/data-display/Typewriter.tsx +119 -0
- package/src/components/data-display/VariableWeightText.tsx +130 -0
- package/src/components/data-display/index.ts +19 -0
- package/src/components/feedback/Alert.test.tsx +44 -0
- package/src/components/feedback/Alert.tsx +65 -0
- package/src/components/feedback/EmptyState.tsx +113 -0
- package/src/components/feedback/Progress.test.tsx +60 -0
- package/src/components/feedback/Progress.tsx +30 -0
- package/src/components/feedback/ProgressBar.tsx +158 -0
- package/src/components/feedback/Skeleton.test.tsx +39 -0
- package/src/components/feedback/Skeleton.tsx +45 -0
- package/src/components/feedback/Sonner.tsx +28 -0
- package/src/components/feedback/Spinner.test.tsx +33 -0
- package/src/components/feedback/Spinner.tsx +99 -0
- package/src/components/feedback/Stepper.tsx +307 -0
- package/src/components/feedback/Toast/Toast.tsx +243 -0
- package/src/components/feedback/Toast/index.ts +2 -0
- package/src/components/feedback/index.ts +9 -0
- package/src/components/forms/Checkbox.test.tsx +40 -0
- package/src/components/forms/Checkbox.tsx +31 -0
- package/src/components/forms/ColorPicker.tsx +118 -0
- package/src/components/forms/Combobox.tsx +96 -0
- package/src/components/forms/DragDrop.tsx +440 -0
- package/src/components/forms/FileUpload.tsx +252 -0
- package/src/components/forms/FilterButton.tsx +65 -0
- package/src/components/forms/Form.tsx +197 -0
- package/src/components/forms/Input.test.tsx +46 -0
- package/src/components/forms/Input.tsx +43 -0
- package/src/components/forms/InputOTP.tsx +81 -0
- package/src/components/forms/Label.test.tsx +20 -0
- package/src/components/forms/Label.tsx +25 -0
- package/src/components/forms/RadioGroup.tsx +51 -0
- package/src/components/forms/SearchBar.tsx +215 -0
- package/src/components/forms/Select.test.tsx +118 -0
- package/src/components/forms/Select.tsx +274 -0
- package/src/components/forms/Slider.tsx +29 -0
- package/src/components/forms/Switch.test.tsx +76 -0
- package/src/components/forms/Switch.tsx +30 -0
- package/src/components/forms/TextField.tsx +152 -0
- package/src/components/forms/Textarea.test.tsx +41 -0
- package/src/components/forms/Textarea.tsx +29 -0
- package/src/components/forms/ThemeSwitcher.tsx +290 -0
- package/src/components/forms/ThemeToggle.tsx +151 -0
- package/src/components/forms/index.ts +19 -0
- package/src/components/layout/Accordion.test.tsx +66 -0
- package/src/components/layout/Accordion.tsx +64 -0
- package/src/components/layout/AspectRatio.tsx +7 -0
- package/src/components/layout/Carousel.tsx +277 -0
- package/src/components/layout/Collapsible.test.tsx +40 -0
- package/src/components/layout/Collapsible.tsx +31 -0
- package/src/components/layout/Container.test.tsx +45 -0
- package/src/components/layout/Container.tsx +99 -0
- package/src/components/layout/CustomizerPanel.tsx +400 -0
- package/src/components/layout/DatePicker.tsx +57 -0
- package/src/components/layout/Footer/Footer.tsx +175 -0
- package/src/components/layout/Footer/index.ts +2 -0
- package/src/components/layout/GlassSurface.tsx +82 -0
- package/src/components/layout/Grid.test.tsx +31 -0
- package/src/components/layout/Grid.tsx +130 -0
- package/src/components/layout/Header/Header.tsx +450 -0
- package/src/components/layout/Header/index.ts +2 -0
- package/src/components/layout/PageLayout.tsx +180 -0
- package/src/components/layout/PageTemplate.tsx +158 -0
- package/src/components/layout/Resizable.tsx +48 -0
- package/src/components/layout/ScrollArea.tsx +53 -0
- package/src/components/layout/Separator.test.tsx +28 -0
- package/src/components/layout/Separator.tsx +29 -0
- package/src/components/layout/Sidebar.tsx +171 -0
- package/src/components/layout/Stack.test.tsx +41 -0
- package/src/components/layout/Stack.tsx +89 -0
- package/src/components/layout/glass-surface.css +60 -0
- package/src/components/layout/index.ts +18 -0
- package/src/components/motion/AnimatedBeam.tsx +159 -0
- package/src/components/navigation/Breadcrumb.test.tsx +57 -0
- package/src/components/navigation/Breadcrumb.tsx +119 -0
- package/src/components/navigation/Breadcrumbs.tsx +221 -0
- package/src/components/navigation/Command.tsx +159 -0
- package/src/components/navigation/Menubar.tsx +115 -0
- package/src/components/navigation/NavLink.tsx +55 -0
- package/src/components/navigation/NavigationMenu.tsx +125 -0
- package/src/components/navigation/Pagination.tsx +121 -0
- package/src/components/navigation/SecondaryNav.tsx +100 -0
- package/src/components/navigation/Tabs.test.tsx +47 -0
- package/src/components/navigation/Tabs.tsx +60 -0
- package/src/components/navigation/TertiaryNav.tsx +90 -0
- package/src/components/navigation/index.ts +10 -0
- package/src/components/overlays/AlertDialog.test.tsx +69 -0
- package/src/components/overlays/AlertDialog.tsx +166 -0
- package/src/components/overlays/ContextMenu.tsx +243 -0
- package/src/components/overlays/Dialog.test.tsx +79 -0
- package/src/components/overlays/Dialog.tsx +158 -0
- package/src/components/overlays/Drawer.tsx +128 -0
- package/src/components/overlays/Dropdown.tsx +253 -0
- package/src/components/overlays/DropdownMenu.tsx +242 -0
- package/src/components/overlays/HoverCard.tsx +32 -0
- package/src/components/overlays/Modal.tsx +250 -0
- package/src/components/overlays/NotificationCenter.tsx +364 -0
- package/src/components/overlays/Popover.test.tsx +40 -0
- package/src/components/overlays/Popover.tsx +46 -0
- package/src/components/overlays/Sheet.tsx +163 -0
- package/src/components/overlays/Tooltip.test.tsx +33 -0
- package/src/components/overlays/Tooltip.tsx +32 -0
- package/src/components/overlays/index.ts +12 -0
- package/src/dates.ts +2 -0
- package/src/dnd.ts +1 -0
- package/src/forms.ts +1 -0
- package/src/globals.css +187 -0
- package/src/hooks/index.ts +6 -0
- package/src/hooks/useForm.ts +247 -0
- package/src/hooks/useMotionPreference.test.ts +102 -0
- package/src/hooks/useMotionPreference.ts +78 -0
- package/src/hooks/useTheme.ts +58 -0
- package/src/hooks.ts +9 -0
- package/src/index.ts +168 -0
- package/src/lib/animations.ts +356 -0
- package/src/lib/breadcrumbs.ts +94 -0
- package/src/lib/colors.ts +493 -0
- package/src/lib/store/customizer.ts +482 -0
- package/src/lib/store/index.ts +3 -0
- package/src/lib/store/theme.ts +55 -0
- package/src/lib/syntax-parser/index.ts +50 -0
- package/src/lib/syntax-parser/patterns.ts +64 -0
- package/src/lib/syntax-parser/tokenizer.ts +117 -0
- package/src/lib/syntax-parser/types.ts +27 -0
- package/src/lib/utils.ts +6 -0
- package/src/lib/validation.ts +204 -0
- package/src/lib/webgl/Color.ts +11 -0
- package/src/lib/webgl/Mesh.ts +41 -0
- package/src/lib/webgl/Program.ts +118 -0
- package/src/lib/webgl/Renderer.ts +51 -0
- package/src/lib/webgl/Triangle.ts +27 -0
- package/src/lib/webgl/Vec3.ts +18 -0
- package/src/lib/webgl/index.ts +13 -0
- package/src/nativewind-env.d.ts +1 -0
- package/src/providers/ThemeProvider.tsx +461 -0
- package/src/providers/index.ts +1 -0
- package/src/providers.ts +7 -0
- package/src/tables.ts +1 -0
- package/src/test/setup.ts +39 -0
- package/src/theme.css +158 -0
- package/src/tokens.ts +7 -0
- package/src/utils.ts +12 -0
- package/src/webgl.ts +1 -0
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Theme Provider
|
|
5
|
+
* Applies theme tokens as CSS variables and manages transitions
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { useEffect, useState } from 'react';
|
|
9
|
+
import { useThemeStore } from '../lib/store/theme';
|
|
10
|
+
import { useCustomizer, type ColorPalette } from '../lib/store/customizer';
|
|
11
|
+
import { studioTokens, terraTokens, voltTokens, speedboatTokens, syntaxColors, codeColors } from '@thesage/tokens';
|
|
12
|
+
import type { ThemeName, ColorMode } from '@thesage/tokens';
|
|
13
|
+
|
|
14
|
+
// ── Type-safe token access ──────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
interface ThemeTokenColors {
|
|
17
|
+
background: string;
|
|
18
|
+
backgroundSecondary: string;
|
|
19
|
+
backgroundTertiary: string;
|
|
20
|
+
foreground: string;
|
|
21
|
+
foregroundSecondary: string;
|
|
22
|
+
foregroundTertiary: string;
|
|
23
|
+
primary: string;
|
|
24
|
+
primaryForeground: string;
|
|
25
|
+
secondary: string;
|
|
26
|
+
secondaryForeground: string;
|
|
27
|
+
accent: string;
|
|
28
|
+
accentForeground: string;
|
|
29
|
+
border: string;
|
|
30
|
+
borderSubtle: string;
|
|
31
|
+
hover: string;
|
|
32
|
+
active: string;
|
|
33
|
+
linkHover: string;
|
|
34
|
+
linkHoverForeground: string;
|
|
35
|
+
success: string;
|
|
36
|
+
successForeground: string;
|
|
37
|
+
warning: string;
|
|
38
|
+
warningForeground: string;
|
|
39
|
+
error: string;
|
|
40
|
+
errorForeground: string;
|
|
41
|
+
info: string;
|
|
42
|
+
infoForeground: string;
|
|
43
|
+
glass: string;
|
|
44
|
+
glassBorder: string;
|
|
45
|
+
card?: string;
|
|
46
|
+
cardForeground?: string;
|
|
47
|
+
popover?: string;
|
|
48
|
+
popoverForeground?: string;
|
|
49
|
+
muted?: string;
|
|
50
|
+
mutedForeground?: string;
|
|
51
|
+
destructive?: string;
|
|
52
|
+
destructiveForeground?: string;
|
|
53
|
+
input?: string;
|
|
54
|
+
ring?: string;
|
|
55
|
+
surface?: string;
|
|
56
|
+
link?: string;
|
|
57
|
+
primaryHover?: string;
|
|
58
|
+
accentHover?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface ThemeTokenEffects {
|
|
62
|
+
blur: { sm: string; md: string; lg: string; xl: string };
|
|
63
|
+
shadow: { sm: string; md: string; lg: string; xl: string; '2xl': string };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface InteractionTokens {
|
|
67
|
+
hover: { overlayColor: { light: string; dark: string }; opacity: number };
|
|
68
|
+
active: { scale: number };
|
|
69
|
+
focus: { ringWidth: string; ringOffset: string };
|
|
70
|
+
disabled: { opacity: number };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
interface ThemeMotion {
|
|
74
|
+
getDuration: (intensity: number) => string;
|
|
75
|
+
ease: { default: string; in: string; out: string; spring: string };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
interface ThemeModeTokens {
|
|
79
|
+
colors: ThemeTokenColors;
|
|
80
|
+
effects: ThemeTokenEffects;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ── Theme token map ─────────────────────────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
const themeTokens = {
|
|
86
|
+
studio: studioTokens,
|
|
87
|
+
terra: terraTokens,
|
|
88
|
+
volt: voltTokens,
|
|
89
|
+
speedboat: speedboatTokens,
|
|
90
|
+
} satisfies Record<ThemeName, any>;
|
|
91
|
+
|
|
92
|
+
// Font family map (CSS variables defined in layout)
|
|
93
|
+
const fontFamilies: Record<ThemeName, Record<string, string>> = {
|
|
94
|
+
studio: {
|
|
95
|
+
heading: 'var(--font-studio-heading)',
|
|
96
|
+
body: 'var(--font-studio-body)',
|
|
97
|
+
mono: 'var(--font-mono)',
|
|
98
|
+
},
|
|
99
|
+
terra: {
|
|
100
|
+
sans: 'var(--font-terra-body)',
|
|
101
|
+
serif: 'var(--font-terra-heading)',
|
|
102
|
+
mono: 'var(--font-mono)',
|
|
103
|
+
},
|
|
104
|
+
volt: {
|
|
105
|
+
sans: 'var(--font-volt-heading)',
|
|
106
|
+
mono: 'var(--font-mono)',
|
|
107
|
+
},
|
|
108
|
+
speedboat: {
|
|
109
|
+
heading: 'var(--font-montserrat)',
|
|
110
|
+
body: 'var(--font-roboto)',
|
|
111
|
+
mono: 'var(--font-mono)',
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
116
|
+
|
|
117
|
+
/** Extract raw pixel value from blur() CSS function */
|
|
118
|
+
function extractBlurValue(blurFunction: string): string {
|
|
119
|
+
const match = blurFunction.match(/blur\(([^)]+)\)/);
|
|
120
|
+
return match ? match[1] : '8px';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ── Token → CSS variable mapping ────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Convert theme tokens to CSS variables
|
|
127
|
+
*/
|
|
128
|
+
function getThemeVars(theme: ThemeName, mode: ColorMode, motionIntensity: number): Record<string, string> {
|
|
129
|
+
const tokens = themeTokens[theme];
|
|
130
|
+
const modeTokens = tokens[mode] as ThemeModeTokens;
|
|
131
|
+
const colors = modeTokens?.colors;
|
|
132
|
+
const effects = modeTokens?.effects;
|
|
133
|
+
const fonts = fontFamilies[theme];
|
|
134
|
+
const motion = tokens.motion as ThemeMotion | undefined;
|
|
135
|
+
const interactions = (tokens as any).interactions as InteractionTokens | undefined;
|
|
136
|
+
|
|
137
|
+
// Compute motion duration from theme + user preference
|
|
138
|
+
const duration = motion?.getDuration?.(motionIntensity) || '300ms';
|
|
139
|
+
const durationMs = parseInt(duration) || 300;
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
// Colors - Base
|
|
143
|
+
'--color-background': colors?.background || '#ffffff',
|
|
144
|
+
'--color-background-secondary': colors?.backgroundSecondary || colors?.background || '#fafafa',
|
|
145
|
+
'--color-background-tertiary': colors?.backgroundTertiary || colors?.backgroundSecondary || colors?.background || '#f5f5f5',
|
|
146
|
+
'--color-foreground': colors?.foreground || '#0a0a0a',
|
|
147
|
+
'--color-primary': colors?.primary || '#0a0a0a',
|
|
148
|
+
'--color-primary-foreground': colors?.primaryForeground || '#ffffff',
|
|
149
|
+
'--color-secondary': colors?.secondary || '#f5f5f5',
|
|
150
|
+
'--color-secondary-foreground': colors?.secondaryForeground || '#0a0a0a',
|
|
151
|
+
'--color-accent': colors?.accent || colors?.primary || '#0070f3',
|
|
152
|
+
'--color-accent-foreground': colors?.accentForeground || '#ffffff',
|
|
153
|
+
'--color-success': colors?.success || '#00a86b',
|
|
154
|
+
'--color-success-foreground': colors?.successForeground || '#ffffff',
|
|
155
|
+
'--color-warning': colors?.warning || '#f59e0b',
|
|
156
|
+
'--color-warning-foreground': colors?.warningForeground || '#ffffff',
|
|
157
|
+
'--color-error': colors?.error || '#ef4444',
|
|
158
|
+
'--color-error-foreground': colors?.errorForeground || '#ffffff',
|
|
159
|
+
'--color-info': colors?.info || colors?.accent || '#0070f3',
|
|
160
|
+
'--color-info-foreground': colors?.infoForeground || '#ffffff',
|
|
161
|
+
'--color-glass': colors?.glass || 'rgba(255, 255, 255, 0.7)',
|
|
162
|
+
'--color-glass-border': colors?.glassBorder || 'rgba(0, 0, 0, 0.1)',
|
|
163
|
+
|
|
164
|
+
// Semantic color aliases (matching README examples)
|
|
165
|
+
'--color-text-primary': colors?.foreground || '#0a0a0a',
|
|
166
|
+
'--color-text-secondary': colors?.foregroundSecondary || '#525252',
|
|
167
|
+
'--color-text-muted': colors?.foregroundTertiary || '#a3a3a3',
|
|
168
|
+
'--color-surface': colors?.backgroundSecondary || colors?.background || '#fafafa',
|
|
169
|
+
'--color-border': colors?.border || colors?.glassBorder || 'rgba(0, 0, 0, 0.1)',
|
|
170
|
+
'--color-focus': colors?.accent || colors?.primary || '#0070f3',
|
|
171
|
+
|
|
172
|
+
// Links and focus rings (can be overridden by derived tokens)
|
|
173
|
+
'--color-link': colors?.link || colors?.primary || '#0a0a0a',
|
|
174
|
+
'--color-ring': colors?.ring || colors?.primary || '#0a0a0a',
|
|
175
|
+
|
|
176
|
+
// Interactive states
|
|
177
|
+
'--color-hover': colors?.hover || colors?.backgroundSecondary || '#fafafa',
|
|
178
|
+
'--color-active': colors?.active || colors?.backgroundTertiary || '#f0f0f0',
|
|
179
|
+
'--color-link-hover': colors?.linkHover || colors?.primary || '#0a0a0a',
|
|
180
|
+
'--color-link-hover-foreground': colors?.linkHoverForeground || colors?.background || '#ffffff',
|
|
181
|
+
|
|
182
|
+
// Component-specific (previously only set in globals.css defaults)
|
|
183
|
+
'--color-card': colors?.card || colors?.background || '#ffffff',
|
|
184
|
+
'--color-card-foreground': colors?.cardForeground || colors?.foreground || '#0a0a0a',
|
|
185
|
+
'--color-popover': colors?.popover || colors?.background || '#ffffff',
|
|
186
|
+
'--color-popover-foreground': colors?.popoverForeground || colors?.foreground || '#0a0a0a',
|
|
187
|
+
'--color-muted': colors?.muted || colors?.backgroundSecondary || '#f5f5f5',
|
|
188
|
+
'--color-muted-foreground': colors?.mutedForeground || colors?.foregroundTertiary || '#737373',
|
|
189
|
+
'--color-destructive': colors?.destructive || colors?.error || '#ef4444',
|
|
190
|
+
'--color-destructive-foreground': colors?.destructiveForeground || '#ffffff',
|
|
191
|
+
'--color-input': colors?.input || colors?.border || '#d4d4d4',
|
|
192
|
+
|
|
193
|
+
// Effects - Blur (full function for style attributes)
|
|
194
|
+
'--effect-blur-sm': effects?.blur?.sm || 'blur(4px)',
|
|
195
|
+
'--effect-blur-md': effects?.blur?.md || 'blur(8px)',
|
|
196
|
+
'--effect-blur-lg': effects?.blur?.lg || 'blur(16px)',
|
|
197
|
+
'--effect-blur-xl': effects?.blur?.xl || effects?.blur?.lg || 'blur(24px)',
|
|
198
|
+
|
|
199
|
+
// Effects - Blur (raw values for Tailwind blur-*/backdrop-blur-* utilities)
|
|
200
|
+
'--blur-sm': extractBlurValue(effects?.blur?.sm || 'blur(4px)'),
|
|
201
|
+
'--blur-md': extractBlurValue(effects?.blur?.md || 'blur(8px)'),
|
|
202
|
+
'--blur-lg': extractBlurValue(effects?.blur?.lg || 'blur(16px)'),
|
|
203
|
+
'--blur-xl': extractBlurValue(effects?.blur?.xl || 'blur(24px)'),
|
|
204
|
+
|
|
205
|
+
// Effects - Shadow (complete set)
|
|
206
|
+
'--effect-shadow-sm': effects?.shadow?.sm || '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
|
|
207
|
+
'--effect-shadow-md': effects?.shadow?.md || '0 4px 6px -1px rgba(0, 0, 0, 0.1)',
|
|
208
|
+
'--effect-shadow-lg': effects?.shadow?.lg || '0 10px 15px -3px rgba(0, 0, 0, 0.1)',
|
|
209
|
+
'--effect-shadow-xl': effects?.shadow?.xl || '0 20px 25px -5px rgba(0, 0, 0, 0.1)',
|
|
210
|
+
'--effect-shadow-2xl': effects?.shadow?.['2xl'] || '0 25px 50px -12px rgba(0, 0, 0, 0.25)',
|
|
211
|
+
|
|
212
|
+
// Interaction tokens (theme-aware)
|
|
213
|
+
'--color-interaction-overlay': interactions?.hover?.overlayColor?.[mode] || (mode === 'dark' ? '#ffffff' : '#000000'),
|
|
214
|
+
'--opacity-interaction-hover': String(interactions?.hover?.opacity ?? 0.08),
|
|
215
|
+
'--scale-interaction-active': String(interactions?.active?.scale ?? 0.98),
|
|
216
|
+
'--color-interaction-focus-ring': colors?.ring || colors?.primary || '#0a0a0a',
|
|
217
|
+
'--width-interaction-focus-ring': interactions?.focus?.ringWidth || '2px',
|
|
218
|
+
'--width-interaction-focus-offset': interactions?.focus?.ringOffset || '2px',
|
|
219
|
+
'--opacity-interaction-disabled': String(interactions?.disabled?.opacity ?? 0.5),
|
|
220
|
+
|
|
221
|
+
// Typography - Font Families
|
|
222
|
+
'--font-heading': fonts?.heading || (theme === 'terra' && fonts?.serif ? fonts.serif : fonts?.sans) || 'var(--font-studio-heading)',
|
|
223
|
+
'--font-body': fonts?.body || fonts?.sans || 'var(--font-studio-body)',
|
|
224
|
+
'--font-mono': fonts?.mono || 'var(--font-studio-mono)',
|
|
225
|
+
|
|
226
|
+
// Motion - Easing (complete set)
|
|
227
|
+
'--ease-default': motion?.ease?.default || 'cubic-bezier(0.4, 0, 0.2, 1)',
|
|
228
|
+
'--ease-in': motion?.ease?.in || 'cubic-bezier(0.4, 0, 1, 1)',
|
|
229
|
+
'--ease-out': motion?.ease?.out || 'cubic-bezier(0, 0, 0.2, 1)',
|
|
230
|
+
'--ease-spring': motion?.ease?.spring || 'cubic-bezier(0.16, 1, 0.3, 1)',
|
|
231
|
+
|
|
232
|
+
// Motion - Duration (computed from theme + user motion preference)
|
|
233
|
+
'--duration-default': duration,
|
|
234
|
+
'--duration-fast': `${Math.max(0, Math.round(durationMs * 0.5))}ms`,
|
|
235
|
+
'--duration-slow': `${Math.min(1000, Math.round(durationMs * 1.5))}ms`,
|
|
236
|
+
|
|
237
|
+
// Syntax Highlighting - Based on VS Code Dark+ theme
|
|
238
|
+
'--syntax-comment': mode === 'light' ? syntaxColors.light.comment : syntaxColors.dark.comment,
|
|
239
|
+
'--syntax-keyword': mode === 'light' ? syntaxColors.light.keyword : syntaxColors.dark.keyword,
|
|
240
|
+
'--syntax-function': mode === 'light' ? syntaxColors.light.function : syntaxColors.dark.function,
|
|
241
|
+
'--syntax-string': mode === 'light' ? syntaxColors.light.string : syntaxColors.dark.string,
|
|
242
|
+
'--syntax-number': mode === 'light' ? syntaxColors.light.number : syntaxColors.dark.number,
|
|
243
|
+
'--syntax-boolean': mode === 'light' ? syntaxColors.light.boolean : syntaxColors.dark.boolean,
|
|
244
|
+
'--syntax-operator': mode === 'light' ? syntaxColors.light.operator : syntaxColors.dark.operator,
|
|
245
|
+
'--syntax-property': mode === 'light' ? syntaxColors.light.property : syntaxColors.dark.property,
|
|
246
|
+
'--syntax-className': mode === 'light' ? syntaxColors.light.className : syntaxColors.dark.className,
|
|
247
|
+
'--syntax-tag': mode === 'light' ? syntaxColors.light.tag : syntaxColors.dark.tag,
|
|
248
|
+
'--syntax-attribute': mode === 'light' ? syntaxColors.light.attribute : syntaxColors.dark.attribute,
|
|
249
|
+
'--syntax-variable': mode === 'light' ? syntaxColors.light.variable : syntaxColors.dark.variable,
|
|
250
|
+
'--syntax-punctuation': mode === 'light' ? syntaxColors.light.punctuation : syntaxColors.dark.punctuation,
|
|
251
|
+
'--syntax-plain': mode === 'light' ? syntaxColors.light.plain : syntaxColors.dark.plain,
|
|
252
|
+
|
|
253
|
+
// Code Block Backgrounds and Borders - Accessible contrast (WCAG AA 4.5:1)
|
|
254
|
+
'--code-block-bg': mode === 'light' ? codeColors.light.blockBackground : codeColors.dark.blockBackground,
|
|
255
|
+
'--code-inline-bg': mode === 'light' ? codeColors.light.inlineBackground : codeColors.dark.inlineBackground,
|
|
256
|
+
'--code-border': mode === 'light' ? codeColors.light.border : codeColors.dark.border,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Merge custom color palette with base theme tokens
|
|
262
|
+
* This is where "change once, ripple everywhere" happens!
|
|
263
|
+
*/
|
|
264
|
+
function mergeCustomColorTokens(
|
|
265
|
+
baseTokens: Record<string, string>,
|
|
266
|
+
customPalette: ColorPalette
|
|
267
|
+
): Record<string, string> {
|
|
268
|
+
return {
|
|
269
|
+
...baseTokens,
|
|
270
|
+
|
|
271
|
+
// Override primary color
|
|
272
|
+
'--color-primary': customPalette.primary,
|
|
273
|
+
'--color-primary-foreground': customPalette.primaryForeground,
|
|
274
|
+
|
|
275
|
+
// Apply color scale (for utilities like bg-primary-200)
|
|
276
|
+
'--color-primary-50': customPalette.scale[50],
|
|
277
|
+
'--color-primary-100': customPalette.scale[100],
|
|
278
|
+
'--color-primary-200': customPalette.scale[200],
|
|
279
|
+
'--color-primary-300': customPalette.scale[300],
|
|
280
|
+
'--color-primary-400': customPalette.scale[400],
|
|
281
|
+
'--color-primary-500': customPalette.scale[500],
|
|
282
|
+
'--color-primary-600': customPalette.scale[600],
|
|
283
|
+
'--color-primary-700': customPalette.scale[700],
|
|
284
|
+
'--color-primary-800': customPalette.scale[800],
|
|
285
|
+
'--color-primary-900': customPalette.scale[900],
|
|
286
|
+
|
|
287
|
+
// Override secondary color if provided (advanced mode)
|
|
288
|
+
...(customPalette.secondary && {
|
|
289
|
+
'--color-secondary': customPalette.secondary,
|
|
290
|
+
'--color-secondary-foreground': customPalette.secondaryForeground || baseTokens['--color-secondary-foreground'],
|
|
291
|
+
}),
|
|
292
|
+
|
|
293
|
+
// Override accent color if provided (advanced mode)
|
|
294
|
+
...(customPalette.accent && {
|
|
295
|
+
'--color-accent': customPalette.accent,
|
|
296
|
+
'--color-accent-foreground': customPalette.accentForeground || baseTokens['--color-accent-foreground'],
|
|
297
|
+
}),
|
|
298
|
+
|
|
299
|
+
// Apply ALL derived tokens from dependency graph
|
|
300
|
+
...customPalette.derivedTokens,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Validate theme tokens in development mode
|
|
306
|
+
*/
|
|
307
|
+
function validateThemeTokens(theme: ThemeName, mode: ColorMode): void {
|
|
308
|
+
// @ts-expect-error - process.env is injected by bundler
|
|
309
|
+
if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'production') return;
|
|
310
|
+
|
|
311
|
+
const root = document.documentElement;
|
|
312
|
+
const style = getComputedStyle(root);
|
|
313
|
+
|
|
314
|
+
const requiredTokens = [
|
|
315
|
+
'--color-background',
|
|
316
|
+
'--color-foreground',
|
|
317
|
+
'--color-primary',
|
|
318
|
+
'--color-primary-foreground',
|
|
319
|
+
'--color-border',
|
|
320
|
+
'--color-ring',
|
|
321
|
+
'--font-heading',
|
|
322
|
+
'--font-body',
|
|
323
|
+
'--font-mono',
|
|
324
|
+
];
|
|
325
|
+
|
|
326
|
+
const missingTokens: string[] = [];
|
|
327
|
+
const invalidTokens: string[] = [];
|
|
328
|
+
|
|
329
|
+
requiredTokens.forEach((token) => {
|
|
330
|
+
const value = style.getPropertyValue(token).trim();
|
|
331
|
+
|
|
332
|
+
if (!value) {
|
|
333
|
+
missingTokens.push(token);
|
|
334
|
+
} else if (token.startsWith('--color-') && !value.match(/^(#|rgb|hsl|var\()/)) {
|
|
335
|
+
invalidTokens.push(`${token} = "${value}"`);
|
|
336
|
+
} else if (token.startsWith('--font-') && value === '') {
|
|
337
|
+
invalidTokens.push(`${token} = empty`);
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
if (missingTokens.length > 0) {
|
|
342
|
+
console.warn(
|
|
343
|
+
`[ThemeProvider] Missing CSS variables for theme "${theme}" (${mode} mode):`,
|
|
344
|
+
missingTokens
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (invalidTokens.length > 0) {
|
|
349
|
+
console.warn(
|
|
350
|
+
`[ThemeProvider] Invalid CSS variable values for theme "${theme}" (${mode} mode):`,
|
|
351
|
+
invalidTokens
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (missingTokens.length === 0 && invalidTokens.length === 0) {
|
|
356
|
+
console.log(`[ThemeProvider] ✓ Theme validation passed for "${theme}" (${mode} mode)`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// ── Component ───────────────────────────────────────────────────────────────
|
|
361
|
+
|
|
362
|
+
export interface ThemeProviderProps {
|
|
363
|
+
children: React.ReactNode;
|
|
364
|
+
/**
|
|
365
|
+
* Default theme to use on first load (before localStorage).
|
|
366
|
+
* Does NOT override a previously persisted theme.
|
|
367
|
+
*/
|
|
368
|
+
defaultTheme?: ThemeName;
|
|
369
|
+
/**
|
|
370
|
+
* Default color mode to use on first load (before localStorage).
|
|
371
|
+
* Does NOT override a previously persisted mode.
|
|
372
|
+
*/
|
|
373
|
+
defaultMode?: ColorMode;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
export function ThemeProvider({ children, defaultTheme, defaultMode }: ThemeProviderProps) {
|
|
377
|
+
const { theme, mode, setTheme, setMode } = useThemeStore();
|
|
378
|
+
const customPalette = useCustomizer((state) => state.customColors?.[theme]?.[mode]);
|
|
379
|
+
const motionIntensity = useCustomizer((state) => state.motion);
|
|
380
|
+
|
|
381
|
+
const [isTransitioning, setIsTransitioning] = useState(false);
|
|
382
|
+
const [mounted, setMounted] = useState(false);
|
|
383
|
+
|
|
384
|
+
// Apply defaults on first mount if no persisted preference exists
|
|
385
|
+
useEffect(() => {
|
|
386
|
+
if (!defaultTheme && !defaultMode) return;
|
|
387
|
+
const persisted = typeof window !== 'undefined' && localStorage.getItem('ecosystem-theme');
|
|
388
|
+
if (persisted) return;
|
|
389
|
+
if (defaultTheme) setTheme(defaultTheme);
|
|
390
|
+
if (defaultMode) setMode(defaultMode);
|
|
391
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
392
|
+
}, []);
|
|
393
|
+
|
|
394
|
+
// Prevent hydration mismatch
|
|
395
|
+
useEffect(() => {
|
|
396
|
+
setMounted(true);
|
|
397
|
+
}, []);
|
|
398
|
+
|
|
399
|
+
// Apply theme variables with transition
|
|
400
|
+
useEffect(() => {
|
|
401
|
+
if (!mounted) return;
|
|
402
|
+
|
|
403
|
+
setIsTransitioning(true);
|
|
404
|
+
|
|
405
|
+
const root = document.documentElement;
|
|
406
|
+
|
|
407
|
+
// 1. Get base theme tokens (including motion-aware durations)
|
|
408
|
+
const baseTokens = getThemeVars(theme, mode, motionIntensity);
|
|
409
|
+
|
|
410
|
+
// 2. Debug logging (development only)
|
|
411
|
+
// @ts-expect-error - process.env is injected by bundler
|
|
412
|
+
if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
|
|
413
|
+
console.log('[ThemeProvider] Update:', {
|
|
414
|
+
theme,
|
|
415
|
+
mode,
|
|
416
|
+
motionIntensity,
|
|
417
|
+
hasCustomPalette: !!customPalette,
|
|
418
|
+
customPrimary: customPalette?.primary,
|
|
419
|
+
timestamp: new Date().toISOString()
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// 3. Merge tokens (custom overrides base)
|
|
424
|
+
const finalTokens = customPalette
|
|
425
|
+
? mergeCustomColorTokens(baseTokens, customPalette)
|
|
426
|
+
: baseTokens;
|
|
427
|
+
|
|
428
|
+
// Apply transition class
|
|
429
|
+
root.classList.add('theme-transitioning');
|
|
430
|
+
|
|
431
|
+
// Apply CSS variables IMMEDIATELY
|
|
432
|
+
Object.entries(finalTokens).forEach(([key, value]) => {
|
|
433
|
+
root.style.setProperty(key, value);
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
// Set data attributes for theme and mode
|
|
437
|
+
root.setAttribute('data-theme', theme);
|
|
438
|
+
root.setAttribute('data-mode', mode);
|
|
439
|
+
root.setAttribute('data-custom-colors', customPalette ? 'active' : 'default');
|
|
440
|
+
|
|
441
|
+
// Toggle 'dark' class for Tailwind dark: modifier support
|
|
442
|
+
if (mode === 'dark') {
|
|
443
|
+
root.classList.add('dark');
|
|
444
|
+
} else {
|
|
445
|
+
root.classList.remove('dark');
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Validate theme tokens in development mode
|
|
449
|
+
validateThemeTokens(theme, mode);
|
|
450
|
+
|
|
451
|
+
// End transition after animation completes
|
|
452
|
+
const timeout = setTimeout(() => {
|
|
453
|
+
root.classList.remove('theme-transitioning');
|
|
454
|
+
setIsTransitioning(false);
|
|
455
|
+
}, 400);
|
|
456
|
+
|
|
457
|
+
return () => clearTimeout(timeout);
|
|
458
|
+
}, [theme, mode, mounted, customPalette, motionIntensity]);
|
|
459
|
+
|
|
460
|
+
return <>{children}</>;
|
|
461
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ThemeProvider } from './ThemeProvider';
|
package/src/providers.ts
ADDED
package/src/tables.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './components/data-display/DataTable';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import '@testing-library/jest-dom/vitest';
|
|
2
|
+
|
|
3
|
+
// Mock window.matchMedia (used by useMotionPreference)
|
|
4
|
+
Object.defineProperty(window, 'matchMedia', {
|
|
5
|
+
writable: true,
|
|
6
|
+
value: vi.fn().mockImplementation((query: string) => ({
|
|
7
|
+
matches: false,
|
|
8
|
+
media: query,
|
|
9
|
+
onchange: null,
|
|
10
|
+
addListener: vi.fn(),
|
|
11
|
+
removeListener: vi.fn(),
|
|
12
|
+
addEventListener: vi.fn(),
|
|
13
|
+
removeEventListener: vi.fn(),
|
|
14
|
+
dispatchEvent: vi.fn(),
|
|
15
|
+
})),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Mock ResizeObserver (used by various Radix UI components and @floating-ui)
|
|
19
|
+
global.ResizeObserver = class ResizeObserver {
|
|
20
|
+
observe = vi.fn();
|
|
21
|
+
unobserve = vi.fn();
|
|
22
|
+
disconnect = vi.fn();
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Mock pointer capture methods (used by Radix UI Select and other primitives)
|
|
26
|
+
if (!Element.prototype.hasPointerCapture) {
|
|
27
|
+
Element.prototype.hasPointerCapture = vi.fn().mockReturnValue(false);
|
|
28
|
+
}
|
|
29
|
+
if (!Element.prototype.setPointerCapture) {
|
|
30
|
+
Element.prototype.setPointerCapture = vi.fn();
|
|
31
|
+
}
|
|
32
|
+
if (!Element.prototype.releasePointerCapture) {
|
|
33
|
+
Element.prototype.releasePointerCapture = vi.fn();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Mock scrollIntoView (used by Radix UI Select for scroll behavior)
|
|
37
|
+
if (!Element.prototype.scrollIntoView) {
|
|
38
|
+
Element.prototype.scrollIntoView = vi.fn();
|
|
39
|
+
}
|
package/src/theme.css
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tailwind v4 Theme Extension
|
|
3
|
+
*
|
|
4
|
+
* Registers design token CSS variables as Tailwind utilities.
|
|
5
|
+
* Uses @theme (not @theme inline) to EXTEND Tailwind defaults,
|
|
6
|
+
* preserving classes like bg-black, text-white, etc.
|
|
7
|
+
*
|
|
8
|
+
* Import this in your app's globals.css:
|
|
9
|
+
* @import "@thesage/ui/theme.css";
|
|
10
|
+
*
|
|
11
|
+
* See: packages/ui/src/globals.css for CSS variable defaults
|
|
12
|
+
* See: packages/ui/src/providers/ThemeProvider.tsx for runtime injection
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
@theme {
|
|
16
|
+
/* ── Colors ─────────────────────────────────────────────────────── */
|
|
17
|
+
--color-background: var(--color-background);
|
|
18
|
+
--color-background-secondary: var(--color-background-secondary);
|
|
19
|
+
--color-background-tertiary: var(--color-background-tertiary);
|
|
20
|
+
|
|
21
|
+
--color-foreground: var(--color-foreground);
|
|
22
|
+
--color-foreground-secondary: var(--color-text-secondary);
|
|
23
|
+
--color-foreground-tertiary: var(--color-text-muted);
|
|
24
|
+
|
|
25
|
+
--color-primary: var(--color-primary);
|
|
26
|
+
--color-primary-foreground: var(--color-primary-foreground);
|
|
27
|
+
|
|
28
|
+
--color-secondary: var(--color-secondary);
|
|
29
|
+
--color-secondary-foreground: var(--color-secondary-foreground);
|
|
30
|
+
|
|
31
|
+
--color-accent: var(--color-accent);
|
|
32
|
+
--color-accent-foreground: var(--color-accent-foreground);
|
|
33
|
+
|
|
34
|
+
--color-destructive: var(--color-destructive);
|
|
35
|
+
--color-destructive-foreground: var(--color-destructive-foreground);
|
|
36
|
+
|
|
37
|
+
--color-muted: var(--color-muted);
|
|
38
|
+
--color-muted-foreground: var(--color-muted-foreground);
|
|
39
|
+
|
|
40
|
+
--color-popover: var(--color-popover);
|
|
41
|
+
--color-popover-foreground: var(--color-popover-foreground);
|
|
42
|
+
|
|
43
|
+
--color-card: var(--color-card);
|
|
44
|
+
--color-card-foreground: var(--color-card-foreground);
|
|
45
|
+
|
|
46
|
+
--color-success: var(--color-success);
|
|
47
|
+
--color-success-foreground: var(--color-success-foreground);
|
|
48
|
+
|
|
49
|
+
--color-warning: var(--color-warning);
|
|
50
|
+
--color-warning-foreground: var(--color-warning-foreground);
|
|
51
|
+
|
|
52
|
+
--color-error: var(--color-error);
|
|
53
|
+
--color-error-foreground: var(--color-error-foreground);
|
|
54
|
+
|
|
55
|
+
--color-info: var(--color-info);
|
|
56
|
+
--color-info-foreground: var(--color-info-foreground);
|
|
57
|
+
|
|
58
|
+
--color-surface: var(--color-surface);
|
|
59
|
+
--color-border: var(--color-border);
|
|
60
|
+
--color-input: var(--color-input);
|
|
61
|
+
--color-ring: var(--color-ring);
|
|
62
|
+
|
|
63
|
+
--color-glass: var(--color-glass);
|
|
64
|
+
--color-glass-border: var(--color-glass-border);
|
|
65
|
+
|
|
66
|
+
/* ── Primary Scale (populated by Customizer) ───────────────────── */
|
|
67
|
+
--color-primary-50: var(--color-primary-50);
|
|
68
|
+
--color-primary-100: var(--color-primary-100);
|
|
69
|
+
--color-primary-200: var(--color-primary-200);
|
|
70
|
+
--color-primary-300: var(--color-primary-300);
|
|
71
|
+
--color-primary-400: var(--color-primary-400);
|
|
72
|
+
--color-primary-500: var(--color-primary-500);
|
|
73
|
+
--color-primary-600: var(--color-primary-600);
|
|
74
|
+
--color-primary-700: var(--color-primary-700);
|
|
75
|
+
--color-primary-800: var(--color-primary-800);
|
|
76
|
+
--color-primary-900: var(--color-primary-900);
|
|
77
|
+
|
|
78
|
+
/* ── Shadows (theme-aware) ─────────────────────────────────────── */
|
|
79
|
+
--shadow-sm: var(--effect-shadow-sm);
|
|
80
|
+
--shadow-md: var(--effect-shadow-md);
|
|
81
|
+
--shadow-lg: var(--effect-shadow-lg);
|
|
82
|
+
--shadow-xl: var(--effect-shadow-xl);
|
|
83
|
+
--shadow-2xl: var(--effect-shadow-2xl);
|
|
84
|
+
|
|
85
|
+
/* ── Blur (raw values — enables blur-sm, backdrop-blur-sm, etc.) ─ */
|
|
86
|
+
--blur-sm: var(--blur-sm);
|
|
87
|
+
--blur-md: var(--blur-md);
|
|
88
|
+
--blur-lg: var(--blur-lg);
|
|
89
|
+
--blur-xl: var(--blur-xl);
|
|
90
|
+
|
|
91
|
+
/* ── Typography ─────────────────────────────────────────────────── */
|
|
92
|
+
--font-heading: var(--font-heading);
|
|
93
|
+
--font-body: var(--font-body);
|
|
94
|
+
--font-mono: var(--font-mono);
|
|
95
|
+
|
|
96
|
+
/* ── Border Radius ──────────────────────────────────────────────── */
|
|
97
|
+
--radius-lg: var(--radius);
|
|
98
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
99
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* ── Keyframes ──────────────────────────────────────────────────────── */
|
|
103
|
+
|
|
104
|
+
@keyframes accordion-down {
|
|
105
|
+
from { height: 0; }
|
|
106
|
+
to { height: var(--radix-accordion-content-height); }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@keyframes accordion-up {
|
|
110
|
+
from { height: var(--radix-accordion-content-height); }
|
|
111
|
+
to { height: 0; }
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@keyframes collapsible-down {
|
|
115
|
+
from { height: 0; }
|
|
116
|
+
to { height: var(--radix-collapsible-content-height); }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
@keyframes collapsible-up {
|
|
120
|
+
from { height: var(--radix-collapsible-content-height); }
|
|
121
|
+
to { height: 0; }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* ── Animation Utilities (motion-preference-aware durations) ────────── */
|
|
125
|
+
|
|
126
|
+
@utility animate-accordion-down {
|
|
127
|
+
animation: accordion-down var(--duration-default, 0.2s) var(--ease-default, ease-out);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@utility animate-accordion-up {
|
|
131
|
+
animation: accordion-up var(--duration-default, 0.2s) var(--ease-default, ease-out);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@utility animate-collapsible-down {
|
|
135
|
+
animation: collapsible-down var(--duration-default, 0.2s) var(--ease-default, ease-out);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@utility animate-collapsible-up {
|
|
139
|
+
animation: collapsible-up var(--duration-default, 0.2s) var(--ease-default, ease-out);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* ── Font Utilities ─────────────────────────────────────────────────── */
|
|
143
|
+
|
|
144
|
+
@utility font-heading {
|
|
145
|
+
font-family: var(--font-heading);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@utility font-body {
|
|
149
|
+
font-family: var(--font-body);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@utility font-mono {
|
|
153
|
+
font-family: var(--font-mono);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
@utility text-balance {
|
|
157
|
+
text-wrap: balance;
|
|
158
|
+
}
|