aura-glass
Version:
A comprehensive glassmorphism design system for React applications with 142+ production-ready components
813 lines (809 loc) • 27.1 kB
JavaScript
'use client';
import { jsx, jsxs } from 'react/jsx-runtime';
import { useContext, createContext, useRef, useState, useEffect, useCallback, useMemo, useId } from 'react';
import { THEME_VARIANTS, BLUR_STRENGTHS } from './themeConstants.js';
import { AURA_GLASS } from '../tokens/glass.js';
import { safeMatchMedia, isBrowser, getSafeWindow, getSafeDocument } from '../utils/env.js';
const ColorModeContext = /*#__PURE__*/createContext({
colorMode: "system",
setColorMode: mode => {
if (process.env.NODE_ENV === "development") {
console.warn("setColorMode was called before ThemeProvider was initialized");
}
},
isDarkMode: false,
toggleColorMode: () => {
if (process.env.NODE_ENV === "development") {
console.warn("toggleColorMode was called before ThemeProvider was initialized");
}
},
systemPrefersDark: false
});
const ThemeVariantContext = /*#__PURE__*/createContext({
themeVariant: THEME_VARIANTS[0] ,
setThemeVariant: variant => {
if (process.env.NODE_ENV === "development") {
console.warn("setThemeVariant was called before ThemeProvider was initialized");
}
},
availableThemes: [...THEME_VARIANTS]
});
const StyleUtilsContext = /*#__PURE__*/createContext({
getColor: () => "",
getSpacing: () => "",
getShadow: () => "",
getBorderRadius: () => "",
getZIndex: () => 0,
getTypography: () => ({})
});
const GlassEffectsContext = /*#__PURE__*/createContext({
qualityTier: "high",
setQualityTier: tier => {
if (process.env.NODE_ENV === "development") {
console.warn("setQualityTier was called before ThemeProvider was initialized");
}
},
getBlurStrength: () => "",
getBackgroundOpacity: () => 0,
getBorderOpacity: () => 0,
getGlowIntensity: () => 0,
createSurface: () => "",
GlassSurface: () => null
});
const PreferencesContext = /*#__PURE__*/createContext({
reducedMotion: false,
reducedTransparency: false,
highContrastMode: false,
setPreference: (key, value) => {
if (process.env.NODE_ENV === "development") {
console.warn("setPreference was called before ThemeProvider was initialized");
}
},
getUserPreference: () => false
});
const ResponsiveContext = /*#__PURE__*/createContext({
breakpoints: {
xs: 0,
sm: 600,
md: 960,
lg: 1280,
xl: 1920
},
currentBreakpoint: "md",
isMobile: false,
isTablet: false,
isDesktop: true,
mediaQuery: () => ""
});
// ------ ThemeProvider Presence Context ------
const ThemeProviderPresenceContext = /*#__PURE__*/createContext(false);
const DEFAULT_PERSONA_ID = "auraglass-default";
const useThemeProviderPresence = () => useContext(ThemeProviderPresenceContext);
/**
* Unified Theme Provider Component
*
* Provides a comprehensive theme context for Glass UI components.
*/
const UnifiedThemeProvider = ({
children,
initialColorMode = "system",
initialTheme = THEME_VARIANTS[0] ,
enableAutoDetection = true,
respectSystemPreference = true,
forceColorMode,
disableTransitions = false,
enableScrollOptimization = true,
initialQualityTier = "high",
isolateTheme = false,
enableOptimizations = true,
debug = false,
performanceMonitoring = false,
contextUpdateThrottle = 0,
updateOnlyOnCommit = false,
onColorModeChange,
onThemeChange
}) => {
// ------ Color Mode State ------
const themeHostRef = useRef(null);
const resolvedPersonaId = DEFAULT_PERSONA_ID;
const [colorMode, setColorModeState] = useState(initialColorMode);
// State for whether the system prefers dark mode
const [systemPrefersDark, setSystemPrefersDark] = useState(() => safeMatchMedia("(prefers-color-scheme: dark)")?.matches ?? false);
// ------ Theme Variant State ------
const [themeVariant, setThemeVariantState] = useState(initialTheme);
// ------ Glass Effects State ------
const [qualityTier, setQualityTierState] = useState(initialQualityTier);
// ------ Preferences State ------
const [preferences, setPreferences] = useState({
reducedMotion: false,
reducedTransparency: false,
highContrastMode: false
});
// ------ Responsive State ------
const [currentBreakpoint, setCurrentBreakpoint] = useState("md");
// ------ Performance Tracking ------
const renderCount = useRef(0);
const lastUpdateTime = useRef(Date.now());
const pendingUpdates = useRef({});
const commitTimerRef = useRef(null);
// ------ Initialize System Preferences ------
useEffect(() => {
if (!enableAutoDetection || !isBrowser()) return;
const darkModeMediaQuery = safeMatchMedia("(prefers-color-scheme: dark)");
const motionMediaQuery = safeMatchMedia("(prefers-reduced-motion: reduce)");
const handleDarkModeChange = event => {
setSystemPrefersDark(event.matches);
};
const handleMotionChange = event => {
setPreferences(prev => ({
...prev,
reducedMotion: event.matches
}));
};
if (darkModeMediaQuery) {
setSystemPrefersDark(darkModeMediaQuery.matches);
darkModeMediaQuery.addEventListener("change", handleDarkModeChange);
}
if (motionMediaQuery) {
setPreferences(prev => ({
...prev,
reducedMotion: motionMediaQuery.matches
}));
motionMediaQuery.addEventListener("change", handleMotionChange);
}
return () => {
if (darkModeMediaQuery) {
darkModeMediaQuery.removeEventListener("change", handleDarkModeChange);
}
if (motionMediaQuery) {
motionMediaQuery.removeEventListener("change", handleMotionChange);
}
};
}, [enableAutoDetection]);
// ------ Load Saved Preferences ------
useEffect(() => {
if (!isBrowser()) return;
try {
const storage = getSafeWindow()?.localStorage;
if (!storage) return;
const savedColorMode = storage.getItem("glass-ui-color-mode");
if (savedColorMode && !forceColorMode) {
setColorModeState(savedColorMode);
}
const savedThemeVariant = storage.getItem("glass-ui-theme-variant");
if (savedThemeVariant) {
setThemeVariantState(savedThemeVariant);
}
const savedQualityTier = storage.getItem("glass-ui-quality-tier");
if (savedQualityTier) {
setQualityTierState(savedQualityTier);
}
const savedPreferences = storage.getItem("glass-ui-preferences");
if (savedPreferences) {
try {
const parsedPreferences = JSON.parse(savedPreferences);
setPreferences(prev => ({
...prev,
...parsedPreferences
}));
} catch (e) {
if (process.env.NODE_ENV === "development") {
console.error("Failed to parse saved preferences", e);
}
}
}
} catch (error) {
if (process.env.NODE_ENV === "development") {
console.warn("ThemeProvider: failed to load saved preferences", error);
}
}
}, [forceColorMode]);
// ------ Initialize Responsive Breakpoints ------
useEffect(() => {
if (!isBrowser()) return;
const win = getSafeWindow();
if (!win) return;
const breakpoints = {
xs: 0,
sm: 600,
md: 960,
lg: 1280,
xl: 1920
};
const handleResize = () => {
const width = win.innerWidth;
let newBreakpoint = "xs";
if (width >= breakpoints.xl) {
newBreakpoint = "xl";
} else if (width >= breakpoints.lg) {
newBreakpoint = "lg";
} else if (width >= breakpoints.md) {
newBreakpoint = "md";
} else if (width >= breakpoints.sm) {
newBreakpoint = "sm";
}
setCurrentBreakpoint(newBreakpoint);
};
win.addEventListener("resize", handleResize);
handleResize(); // Initial calculation
return () => {
win.removeEventListener("resize", handleResize);
};
}, []);
// ------ Optimized Update Handlers ------
// Handle color mode change with throttling
const setColorMode = useCallback(mode => {
if (forceColorMode) return; // Don't change if force mode is active
const storage = getSafeWindow()?.localStorage;
if (contextUpdateThrottle > 0) {
pendingUpdates.current.colorMode = mode;
if (!commitTimerRef.current) {
commitTimerRef.current = setTimeout(() => {
setColorModeState(pendingUpdates.current.colorMode);
if (onColorModeChange) onColorModeChange(pendingUpdates.current.colorMode);
storage?.setItem("glass-ui-color-mode", pendingUpdates.current.colorMode);
commitTimerRef.current = null;
}, contextUpdateThrottle);
}
} else {
setColorModeState(mode);
if (onColorModeChange) onColorModeChange(mode);
storage?.setItem("glass-ui-color-mode", mode);
}
}, [forceColorMode, contextUpdateThrottle, onColorModeChange]);
// Toggle between light and dark mode
const toggleColorMode = useCallback(() => {
if (forceColorMode) return; // Don't toggle if force mode is active
setColorMode(colorMode === "light" ? "dark" : colorMode === "dark" ? "light" : systemPrefersDark ? "light" : "dark");
}, [colorMode, forceColorMode, systemPrefersDark, setColorMode]);
// Handle theme variant change
const setThemeVariant = useCallback(variant => {
setThemeVariantState(variant);
if (onThemeChange) onThemeChange(variant);
const storage = getSafeWindow()?.localStorage;
storage?.setItem("glass-ui-theme-variant", variant);
}, [onThemeChange]);
// Handle quality tier change
const setQualityTier = useCallback(tier => {
setQualityTierState(tier);
const storage = getSafeWindow()?.localStorage;
storage?.setItem("glass-ui-quality-tier", tier);
}, []);
// Handle preference changes
const setPreference = useCallback((key, value) => {
setPreferences(prev => {
const newPreferences = {
...prev,
[key]: value
};
const storage = getSafeWindow()?.localStorage;
storage?.setItem("glass-ui-preferences", JSON.stringify(newPreferences));
return newPreferences;
});
}, []);
// Get user preference
const getUserPreference = useCallback(key => {
return preferences[key] || false;
}, [preferences]);
// ------ Theme Utilities ------
// Determine if dark mode is active based on all factors
const isDarkMode = useMemo(() => {
if (forceColorMode) {
return forceColorMode === "dark";
}
return colorMode === "dark" || colorMode === "system" && respectSystemPreference && systemPrefersDark;
}, [colorMode, forceColorMode, respectSystemPreference, systemPrefersDark]);
const resolvedMode = isDarkMode ? "dark" : "light";
useEffect(() => {
if (isolateTheme) {
const host = themeHostRef.current;
if (host) {
host.setAttribute("data-aura-theme", resolvedPersonaId);
host.setAttribute("data-aura-mode", resolvedMode);
}
return;
}
const doc = getSafeDocument();
const el = doc?.documentElement;
if (!el) {
return;
}
const previousTheme = el.getAttribute("data-aura-theme");
const previousMode = el.getAttribute("data-aura-mode");
el.setAttribute("data-aura-theme", resolvedPersonaId);
el.setAttribute("data-aura-mode", resolvedMode);
return () => {
if (previousTheme) {
el.setAttribute("data-aura-theme", previousTheme);
} else {
el.removeAttribute("data-aura-theme");
}
if (previousMode) {
el.setAttribute("data-aura-mode", previousMode);
} else {
el.removeAttribute("data-aura-mode");
}
};
}, [isolateTheme, resolvedPersonaId, resolvedMode]);
// Create style utility functions
const getColor = useCallback((path, fallback = "") => {
const parts = path.split(".");
let value = AURA_GLASS.surfaces;
for (const part of parts) {
if (value && typeof value === "object" && part in value) {
value = value[part];
} else {
return fallback;
}
}
return typeof value === "string" ? value : fallback;
}, []);
// Create glass effect utilities
const getBlurStrength = useCallback(strength => {
if (typeof strength === "number") {
return `${strength}px`;
}
return BLUR_STRENGTHS.includes(strength) ? strength : "standard";
}, []);
const getBackgroundOpacity = useCallback(opacity => {
if (typeof opacity === "number") {
return Math.max(0, Math.min(1, opacity));
}
const opacityMap = {
transparent: 0,
lightest: 0.05,
light: 0.1,
medium: 0.2,
high: 0.5,
solid: 1
};
return opacityMap[opacity] || 0.2;
}, []);
const getBorderOpacity = useCallback(opacity => {
if (typeof opacity === "number") {
return Math.max(0, Math.min(1, opacity));
}
const opacityMap = {
none: 0,
minimal: 0.05,
subtle: 0.1,
medium: 0.2,
high: 0.4
};
return opacityMap[opacity] || 0.2;
}, []);
const getGlowIntensity = useCallback(intensity => {
if (typeof intensity === "number") {
return Math.max(0, Math.min(1, intensity));
}
const intensityMap = {
minimal: 0.02,
light: 0.05,
medium: 0.1,
strong: 0.15,
extreme: 0.25
};
return intensityMap[intensity] || 0.1;
}, []);
// Helper to create glass surface styles
const createSurface = useCallback(props => {
const {
variant = "standard",
elevation: rawElevation = 1,
interactive = false
} = props;
// Ensure elevation is a number
const elevation = typeof rawElevation === "string" ? rawElevation === "level1" ? 1 : rawElevation === "level2" ? 2 : rawElevation === "level3" ? 3 : rawElevation === "level4" ? 4 : 1 : Number(rawElevation);
// Get glass-specific color values
const backgroundColor = isDarkMode ? "rgba(0, 0, 0, 0.2)" : "rgba(255, 255, 255, 0.1)";
const borderColor = isDarkMode ? "rgba(255, 255, 255, 0.1)" : "rgba(0, 0, 0, 0.1)";
const shadowColor = isDarkMode ? "rgba(0, 0, 0, 0.3)" : "rgba(0, 0, 0, 0.1)";
const glowColor = isDarkMode ? "var(--glass-color-primary)" : "#6366f1";
// Get opacity and blur values from qualityTier
const bgOpacity = getBackgroundOpacity("medium");
const borderOpacityValue = getBorderOpacity("medium");
const blurValue = getBlurStrength("medium");
const glowValue = getGlowIntensity("medium");
// Build styles based on glass variant
const baseStyles = `
position: relative;
background-color: ${backgroundColor};
backdrop-filter: blur(${blurValue});
-webkit-backdrop-filter: blur(${blurValue});
border: 1px solid ${borderColor};
box-shadow: 0 4px 12px ${shadowColor};
transition: transform 0.2s ease, box-shadow 0.2s ease;
`;
// Add variant-specific styles
let variantStyles = "";
// Variables for all cases
let blurNumber;
let bgOpacityAdjusted;
let blurAdjusted;
let dimBgOpacity;
switch (variant) {
case "frosted":
// Parse blur value as number if it's a string
blurNumber = typeof blurValue === "string" ? parseInt(blurValue.replace("px", ""), 10) : Number(blurValue);
bgOpacityAdjusted = bgOpacity * 0.7;
blurAdjusted = blurNumber * 1.5;
variantStyles = `
background-color: rgba(255, 255, 255, ${bgOpacityAdjusted});
backdrop-filter: blur(${blurAdjusted}px);
-webkit-backdrop-filter: blur(${blurAdjusted}px);
border-width: 1px;
`;
break;
case "crystal":
dimBgOpacity = bgOpacity * 0.6;
const dimElev2 = elevation * 2;
const dimElev6 = elevation * 6;
const dimElev05 = elevation * 0.5;
const dimElev1 = elevation * 1;
variantStyles = `
background-color: rgba(255, 255, 255, ${dimBgOpacity});
box-shadow:
0 ${dimElev2}px ${dimElev6}px ${shadowColor},
0 ${dimElev05}px ${dimElev1}px rgba(0, 0, 0, 0.03),
inset 0 0 0 1px rgba(255, 255, 255, ${borderOpacityValue});
border-width: 0;
`;
break;
case "metallic":
const accentColor = isDarkMode ? "var(--glass-color-primary)" : "#6366f1";
const bgOpacityTop = bgOpacity * 0.6;
const bgOpacityBottom = bgOpacity * 0.4;
const elevationDouble = elevation * 2;
const elevationSix = elevation * 6;
const elevationFive = elevation * 5;
variantStyles = `
background: linear-gradient(
135deg,
rgba(255, 255, 255, ${bgOpacityTop}) 0%,
rgba(${accentColor}, ${bgOpacityBottom}) 100%
);
box-shadow:
0 ${elevationDouble}px ${elevationSix}px ${shadowColor},
0 0 ${elevationFive}px rgba(${accentColor}, ${glowValue});
`;
break;
case "standard":
default:
const stdElev2 = elevation * 2;
const stdElev6 = elevation * 6;
variantStyles = `
background-color: rgba(255, 255, 255, ${bgOpacity});
box-shadow:
0 ${stdElev2}px ${stdElev6}px ${shadowColor},
inset 0 0 0 1px rgba(255, 255, 255, ${borderOpacityValue});
`;
break;
}
// Add interactive states if required
const hoverElev3 = elevation * 3;
const hoverElev8 = elevation * 8;
const hoverElev2 = elevation * 2;
const activeElev1 = elevation * 1;
const activeElev3 = elevation * 3;
const interactiveStyles = interactive ? `
&:hover {
transform: translateY(-2px);
box-shadow:
0 ${hoverElev3}px ${hoverElev8}px ${shadowColor},
0 0 ${hoverElev2}px ${glowColor};
}
&:active {
transform: translateY(0);
box-shadow:
0 ${activeElev1}px ${activeElev3}px ${shadowColor};
}
` : "";
return `
${baseStyles}
${variantStyles}
${interactiveStyles}
`;
}, [isDarkMode, getBackgroundOpacity, getBorderOpacity, getBlurStrength, getGlowIntensity]);
/**
* GlassSurface Component - A component for rendering glass surfaces with configurable properties
*/
function GlassSurfaceComponent(props) {
const {
variant = "frosted",
elevation = "level1",
interactive = false,
children,
...rest
} = props;
// Generate a unique ID for this surface
const uniqueId = useId();
const surfaceId = useMemo(() => `glass-surface-${uniqueId.replace(/:/g, "-")}`, [uniqueId]);
// Get the glass styles
const cssString = createSurface({
variant,
elevation,
interactive
});
return jsxs("div", {
id: surfaceId,
...rest,
children: [jsx("style", {
dangerouslySetInnerHTML: {
__html: `
#${surfaceId} {
${cssString}
}
`
}
}), children]
});
}
// Use the component directly
const GlassSurface = GlassSurfaceComponent;
// ------ Create Responsive Utilities ------
const breakpoints = useMemo(() => {
return {
xs: 0,
sm: 600,
md: 960,
lg: 1280,
xl: 1920
};
}, []);
const mediaQuery = useCallback(breakpoint => {
const width = breakpoints[breakpoint] || 0;
return `@media (min-width: ${width}px)`;
}, [breakpoints]);
const isMobile = useMemo(() => {
return ["xs", "sm"].includes(currentBreakpoint);
}, [currentBreakpoint]);
const isTablet = useMemo(() => {
return currentBreakpoint === "md";
}, [currentBreakpoint]);
const isDesktop = useMemo(() => {
return ["lg", "xl"].includes(currentBreakpoint);
}, [currentBreakpoint]);
// ------ Create Context Values ------
// ColorMode context
const colorModeContextValue = useMemo(() => ({
colorMode: forceColorMode || colorMode,
setColorMode,
isDarkMode,
toggleColorMode,
systemPrefersDark
}), [forceColorMode, colorMode, setColorMode, isDarkMode, toggleColorMode, systemPrefersDark]);
// ThemeVariant context
const themeVariantContextValue = useMemo(() => ({
themeVariant,
setThemeVariant,
availableThemes: [...THEME_VARIANTS]
}), [themeVariant, setThemeVariant]);
// Theme utility functions
const getSpacing = useCallback(size => {
const spacingMap = {
xs: "0.25rem",
sm: "0.5rem",
md: "1rem",
lg: "1.5rem",
xl: "2rem"
};
if (typeof size === "number") return `${size * 8}px`;
return spacingMap[size] || "0";
}, []);
const getShadow = useCallback((level, color) => {
const shadowMap = {
none: "none",
sm: "0 1px 2px rgba(0,0,0,0.05)",
md: "0 4px 6px rgba(0,0,0,0.07)",
lg: "0 10px 15px rgba(0,0,0,0.1)"
};
const key = level < 1 ? "none" : level === 1 ? "sm" : level === 2 ? "md" : "lg";
return shadowMap[key] || shadowMap.md;
}, []);
const getBorderRadius = useCallback(size => {
const borderRadiusMap = {
none: "0",
sm: "0.25rem",
md: "0.5rem",
lg: "1rem"
};
return borderRadiusMap[size] || "0";
}, []);
const getZIndex = useCallback(layer => {
const zIndexMap = {
base: 0,
modal: 1000,
tooltip: 1100
};
return zIndexMap[layer] || 0;
}, []);
const getTypography = useCallback(variant => {
const typographyMap = {
h1: {
fontSize: "2.5rem",
fontWeight: 600
},
h2: {
fontSize: "2rem",
fontWeight: 600
},
h3: {
fontSize: "1.5rem",
fontWeight: 600
},
body: {
fontSize: "1rem",
fontWeight: 400
}
};
return typographyMap[variant] || {};
}, []);
// StyleUtils context
const styleUtilsContextValue = useMemo(() => ({
getColor,
getSpacing,
getShadow,
getBorderRadius,
getZIndex,
getTypography
}), [getColor, getSpacing, getShadow, getBorderRadius, getZIndex, getTypography]);
// GlassEffects context including the component
const glassEffectsContextValue = useMemo(() => ({
qualityTier,
setQualityTier,
getBlurStrength,
getBackgroundOpacity,
getBorderOpacity,
getGlowIntensity,
createSurface,
GlassSurface
}), [qualityTier, setQualityTier, getBlurStrength, getBackgroundOpacity, getBorderOpacity, getGlowIntensity, createSurface, GlassSurface]);
// Preferences context
const preferencesContextValue = useMemo(() => ({
reducedMotion: preferences.reducedMotion,
reducedTransparency: preferences.reducedTransparency,
highContrastMode: preferences.highContrastMode,
setPreference,
getUserPreference
}), [preferences, setPreference, getUserPreference]);
// Responsive context
const responsiveContextValue = useMemo(() => ({
breakpoints,
currentBreakpoint,
isMobile,
isTablet,
isDesktop,
mediaQuery
}), [breakpoints, currentBreakpoint, isMobile, isTablet, isDesktop, mediaQuery]);
// Performance debugging
useEffect(() => {
if (debug && performanceMonitoring) {
renderCount.current++;
const renderTime = Date.now() - lastUpdateTime.current;
if (process.env.NODE_ENV === "development") {
console.log(`[ThemeProvider] Render #${renderCount.current} took ${renderTime}ms`);
}
lastUpdateTime.current = Date.now();
}
});
// Prevent transitions during theme changes
useEffect(() => {
if (!disableTransitions) {
return;
}
const doc = getSafeDocument();
if (!doc) {
return;
}
doc.documentElement.classList.add("disable-transitions");
const timeout = setTimeout(() => {
doc.documentElement.classList.remove("disable-transitions");
}, 100);
return () => clearTimeout(timeout);
}, [isDarkMode, themeVariant, disableTransitions]);
// Apply scroll optimization
useEffect(() => {
if (!enableScrollOptimization || !isBrowser()) {
return;
}
const doc = getSafeDocument();
const win = getSafeWindow();
if (!doc || !win) {
return;
}
let scrollTimer = null;
let isScrolling = false;
const handleScroll = () => {
if (!isScrolling) {
isScrolling = true;
doc.documentElement.classList.add("is-scrolling");
}
if (scrollTimer) {
clearTimeout(scrollTimer);
}
scrollTimer = setTimeout(() => {
isScrolling = false;
doc.documentElement.classList.remove("is-scrolling");
}, 150);
};
win.addEventListener("scroll", handleScroll, {
passive: true
});
return () => {
win.removeEventListener("scroll", handleScroll);
if (scrollTimer) {
clearTimeout(scrollTimer);
}
};
}, [enableScrollOptimization]);
// Render multi-context provider
const themedChildren = isolateTheme ? jsx("div", {
ref: themeHostRef,
"data-aura-theme": resolvedPersonaId,
"data-aura-mode": resolvedMode,
children: children
}) : children;
return jsx(ThemeProviderPresenceContext.Provider, {
value: true,
children: jsx(ColorModeContext.Provider, {
value: colorModeContextValue,
children: jsx(ThemeVariantContext.Provider, {
value: themeVariantContextValue,
children: jsx(StyleUtilsContext.Provider, {
value: styleUtilsContextValue,
children: jsx(GlassEffectsContext.Provider, {
value: glassEffectsContextValue,
children: jsx(PreferencesContext.Provider, {
value: preferencesContextValue,
children: jsx(ResponsiveContext.Provider, {
value: responsiveContextValue,
children: themedChildren
})
})
})
})
})
})
});
};
/**
* ThemeProvider component (exporting the unified provider directly for now).
*
* This component provides theme context.
*/
const ThemeProvider = UnifiedThemeProvider;
// ------ Theme Hooks ------
/**
* Returns the full theme object based on current settings.
*/
const useTheme = () => {
const colorModeContext = useContext(ColorModeContext);
const themeVariantContext = useContext(ThemeVariantContext);
const styleUtilsContext = useContext(StyleUtilsContext);
if (!colorModeContext || !themeVariantContext || !styleUtilsContext) {
throw new Error("useTheme must be used within a ThemeProvider");
}
return {
isDark: colorModeContext.isDarkMode,
currentColorMode: colorModeContext.colorMode,
toggleColorMode: colorModeContext.toggleColorMode,
setColorMode: colorModeContext.setColorMode,
currentTheme: themeVariantContext.themeVariant,
setTheme: themeVariantContext.setThemeVariant,
availableThemes: themeVariantContext.availableThemes,
...styleUtilsContext
};
};
/**
* Hook for accessing only theme variant aspects.
* More efficient than useTheme when only theme variant is needed.
*/
const useThemeVariant = () => {
const context = useContext(ThemeVariantContext);
if (!context) {
throw new Error("useThemeVariant must be used within a ThemeProvider");
}
return context;
};
export { ThemeProvider, useTheme, useThemeProviderPresence, useThemeVariant };
//# sourceMappingURL=ThemeProvider.js.map