UNPKG

@asafarim/react-themes

Version:

A comprehensive theme management system for React applications with automatic dark/light mode detection, custom theme creation, and smooth transitions.

1 lines 23.4 kB
{"version":3,"sources":["../src/components/ThemeProvider.tsx","../src/hooks/useTheme.ts","../src/components/ThemeToggle.tsx","../src/components/ThemeSelector.tsx","../src/components/DensitySelector.tsx"],"sourcesContent":["import * as React from 'react';\nimport { createContext, useContext, useEffect, useState } from 'react';\nimport type { ThemeContextValue, ThemeProviderProps, ThemeMode, DensityMode, DirectionMode } from '../types';\nimport '@asafarim/design-tokens/css/index.css';\n\nconst ThemeContext = createContext<ThemeContextValue | undefined>(undefined);\n\nexport const ThemeProvider: React.FC<ThemeProviderProps> = ({\n children,\n defaultMode = 'auto',\n defaultDensity = 'default',\n defaultDirection = 'ltr',\n storageKey = 'asafarim-theme',\n enableTransitions = true,\n persistMode = true,\n persistDensity = false,\n persistDirection = false,\n}) => {\n const getStoredValue = <T extends string>(key: string, defaultValue: T): T => {\n if (typeof window === 'undefined') return defaultValue;\n try {\n const stored = localStorage.getItem(`${storageKey}-${key}`);\n return (stored as T) || defaultValue;\n } catch {\n return defaultValue;\n }\n };\n\n const setStoredValue = (key: string, value: string, persist: boolean) => {\n if (!persist || typeof window === 'undefined') return;\n try {\n localStorage.setItem(`${storageKey}-${key}`, value);\n } catch (error) {\n console.warn(`Failed to save ${key} to localStorage:`, error);\n }\n };\n\n const [mode, setModeState] = useState<ThemeMode>(() => \n persistMode ? getStoredValue('mode', defaultMode) : defaultMode\n );\n const [density, setDensityState] = useState<DensityMode>(() => \n persistDensity ? getStoredValue('density', defaultDensity) : defaultDensity\n );\n const [direction, setDirectionState] = useState<DirectionMode>(() => \n persistDirection ? getStoredValue('direction', defaultDirection) : defaultDirection\n );\n const [systemPrefersDark, setSystemPrefersDark] = useState<boolean>(false);\n\n const resolvedMode: 'light' | 'dark' = \n mode === 'auto' \n ? (systemPrefersDark ? 'dark' : 'light')\n : mode;\n\n const setMode = (newMode: ThemeMode) => {\n setModeState(newMode);\n setStoredValue('mode', newMode, persistMode);\n };\n\n const setDensity = (newDensity: DensityMode) => {\n setDensityState(newDensity);\n setStoredValue('density', newDensity, persistDensity);\n };\n\n const setDirection = (newDirection: DirectionMode) => {\n setDirectionState(newDirection);\n setStoredValue('direction', newDirection, persistDirection);\n };\n\n const toggleMode = () => {\n if (mode === 'auto') {\n setMode(systemPrefersDark ? 'light' : 'dark');\n } else {\n setMode(mode === 'light' ? 'dark' : 'light');\n }\n };\n\n useEffect(() => {\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n setSystemPrefersDark(mediaQuery.matches);\n\n const handleChange = (e: MediaQueryListEvent) => {\n setSystemPrefersDark(e.matches);\n };\n\n mediaQuery.addEventListener('change', handleChange);\n return () => mediaQuery.removeEventListener('change', handleChange);\n }, []);\n\n useEffect(() => {\n const root = document.documentElement;\n\n if (enableTransitions) {\n root.style.setProperty('transition', 'background-color var(--asm-motion-duration-normal) var(--asm-motion-easing-standard), color var(--asm-motion-duration-normal) var(--asm-motion-easing-standard)');\n }\n\n root.setAttribute('data-theme', resolvedMode);\n\n if (density !== 'default') {\n root.setAttribute('data-density', density);\n } else {\n root.removeAttribute('data-density');\n }\n\n root.setAttribute('dir', direction);\n\n return () => {\n if (enableTransitions) {\n root.style.removeProperty('transition');\n }\n };\n }, [resolvedMode, density, direction, enableTransitions]);\n\n const contextValue: ThemeContextValue = {\n mode,\n setMode,\n toggleMode,\n density,\n setDensity,\n direction,\n setDirection,\n isDark: resolvedMode === 'dark',\n isLight: resolvedMode === 'light',\n isAuto: mode === 'auto',\n systemPrefersDark,\n resolvedMode,\n };\n\n return (\n <ThemeContext.Provider value={contextValue}>\n {children}\n </ThemeContext.Provider>\n );\n};\n\nexport const useThemeContext = () => {\n const context = useContext(ThemeContext);\n if (context === undefined) {\n throw new Error('useThemeContext must be used within a ThemeProvider');\n }\n return context;\n};\n","import { useThemeContext } from '../components/ThemeProvider';\nimport type { ThemeContextValue } from '../types';\n\nexport function useTheme(): ThemeContextValue {\n return useThemeContext();\n}\n","import * as React from \"react\";\nimport { useTheme } from \"../hooks/useTheme\";\nimport type { CSSProperties, ReactNode } from 'react';\nimport { Moon, Sun, type LucideIcon } from \"lucide-react\";\n\ntype ThemeToggleVariant =\n | \"default\"\n | \"outline\"\n | \"ghost\"\n | \"link\"\n | \"circle\"\n | \"icon\";\n\nexport interface ThemeToggleProps {\n className?: string;\n style?: React.CSSProperties;\n showLabels?: boolean;\n size?: \"sm\" | \"md\" | \"lg\";\n lightIcon?: ReactNode | LucideIcon | string;\n darkIcon?: ReactNode | LucideIcon | string;\n ariaLabel?: string;\n variant?: ThemeToggleVariant;\n}\n\nexport const ThemeToggle: React.FC<ThemeToggleProps> = ({\n className = \"\",\n style = {},\n showLabels = false,\n size = \"md\",\n lightIcon = <Sun /> as ReactNode | LucideIcon | string,\n darkIcon = <Moon /> as ReactNode | LucideIcon | string,\n ariaLabel = \"Toggle theme\",\n variant = \"default\",\n}) => {\n const { resolvedMode, toggleMode } = useTheme();\n\n const iconSizeMap: Record<NonNullable<ThemeToggleProps['size']>, string> = {\n sm: 'var(--asm-icon-size-sm)',\n md: 'var(--asm-icon-size-md)',\n lg: 'var(--asm-icon-size-lg)',\n };\n\n const circleSizeMap: Record<NonNullable<ThemeToggleProps['size']>, string> = {\n sm: '32px',\n md: '40px',\n lg: '48px',\n };\n\n const renderIcon = (icon: ReactNode | LucideIcon | string) => {\n if (React.isValidElement(icon)) {\n return icon;\n }\n if (typeof icon === 'function') {\n const IconComp = icon as LucideIcon;\n return <IconComp size={iconSizeMap[size]} strokeWidth={1.5} aria-hidden=\"true\" />;\n }\n return icon as React.ReactNode;\n };\n\n const getIcon = () => {\n return resolvedMode === \"dark\" ? renderIcon(darkIcon) : renderIcon(lightIcon);\n };\n\n const getLabel = () => {\n return resolvedMode === \"dark\" ? \"Dark\" : \"Light\";\n };\n\n const baseStyles: CSSProperties = {\n borderRadius: 'var(--asm-radius-md)',\n padding: 'var(--asm-space-2)',\n cursor: 'pointer',\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 'var(--asm-space-2)',\n fontSize: 'var(--asm-font-size-md)',\n transition: 'var(--asm-transition-fade), var(--asm-transition-scale)',\n border: 'none',\n };\n\n const variantStyles: Record<ThemeToggleVariant, CSSProperties> = {\n default: {\n background: 'var(--asm-color-button-secondary-bg)',\n border: `var(--asm-border-hairline) solid var(--asm-color-border)`,\n color: 'var(--asm-color-button-secondary-text)',\n },\n outline: {\n background: 'transparent',\n border: `var(--asm-border-hairline) solid var(--asm-color-border)`,\n color: 'var(--asm-color-text)',\n },\n ghost: {\n background: 'transparent',\n color: 'var(--asm-color-text)',\n },\n link: {\n background: 'transparent',\n padding: 0,\n color: 'var(--asm-color-button-primary-bg)',\n },\n circle: {\n background: 'var(--asm-color-button-secondary-bg)',\n border: `var(--asm-border-hairline) solid var(--asm-color-border)`,\n borderRadius: 'var(--asm-radius-full)',\n },\n icon: {\n background: 'transparent',\n padding: 0,\n },\n };\n\n const mergedStyles: CSSProperties = {\n ...baseStyles,\n ...(variantStyles[variant] ?? variantStyles.default),\n ...(variant === \"circle\" ? { width: circleSizeMap[size], height: circleSizeMap[size] } : {}),\n ...style,\n };\n \n return (\n <button\n aria-label={ariaLabel}\n type=\"button\"\n style={mergedStyles}\n onClick={toggleMode}\n className={className}\n title={ariaLabel}\n onMouseEnter={(e) => {\n if (variant === 'ghost' || variant === 'icon') {\n e.currentTarget.style.background = 'var(--asm-color-button-ghost-bg-hover)';\n }\n }}\n onMouseLeave={(e) => {\n if (variant === 'ghost' || variant === 'icon') {\n e.currentTarget.style.background = 'transparent';\n }\n }}\n >\n {getIcon()}\n {showLabels && (\n <span style={{ fontSize: 'var(--asm-font-size-sm)', fontWeight: 'var(--asm-font-weight-500)' }}>\n {getLabel()}\n </span>\n )}\n </button>\n );\n};\n","import * as React from \"react\";\nimport { useTheme } from \"../hooks/useTheme\";\nimport type { ThemeMode } from \"../types\";\nimport { Sun, Moon, Monitor } from \"lucide-react\";\n\nexport interface ThemeSelectorProps {\n className?: string;\n style?: React.CSSProperties;\n variant?: \"buttons\" | \"dropdown\";\n}\n\nexport const ThemeSelector: React.FC<ThemeSelectorProps> = ({\n className = \"\",\n style = {},\n variant = \"buttons\",\n}) => {\n const { mode, setMode } = useTheme();\n\n const options = [\n { mode: \"light\" as ThemeMode, label: \"Light\", Icon: Sun },\n { mode: \"dark\" as ThemeMode, label: \"Dark\", Icon: Moon },\n { mode: \"auto\" as ThemeMode, label: \"Auto\", Icon: Monitor },\n ];\n\n if (variant === \"dropdown\") {\n const selectStyles: React.CSSProperties = {\n padding: 'var(--asm-space-control-padding-y) var(--asm-space-control-padding-x)',\n borderRadius: 'var(--asm-radius-md)',\n border: `var(--asm-border-hairline) solid var(--asm-color-input-border)`,\n backgroundColor: 'var(--asm-color-input-bg)',\n color: 'var(--asm-color-input-text)',\n fontSize: 'var(--asm-font-size-sm)',\n fontFamily: 'var(--asm-font-family-primary)',\n cursor: 'pointer',\n transition: 'var(--asm-transition-fade)',\n outline: 'none',\n ...style,\n };\n\n return (\n <select\n value={mode}\n onChange={(e) => setMode(e.target.value as ThemeMode)}\n style={selectStyles}\n className={className}\n aria-label=\"Select theme mode\"\n onFocus={(e) => {\n e.currentTarget.style.borderColor = 'var(--asm-color-input-border-focus)';\n e.currentTarget.style.boxShadow = `0 0 0 2px var(--asm-color-focus-ring)`;\n }}\n onBlur={(e) => {\n e.currentTarget.style.borderColor = 'var(--asm-color-input-border)';\n e.currentTarget.style.boxShadow = 'none';\n }}\n >\n {options.map((option) => (\n <option key={option.mode} value={option.mode}>\n {option.label}\n </option>\n ))}\n </select>\n );\n }\n\n const containerStyles: React.CSSProperties = {\n display: 'inline-flex',\n gap: 'var(--asm-space-1)',\n padding: 'var(--asm-space-1)',\n borderRadius: 'var(--asm-radius-lg)',\n backgroundColor: 'var(--asm-color-surface-muted)',\n ...style,\n };\n\n const buttonStyles = (isActive: boolean): React.CSSProperties => ({\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 'var(--asm-space-2)',\n padding: 'var(--asm-space-2) var(--asm-space-3)',\n borderRadius: 'var(--asm-radius-md)',\n border: 'none',\n backgroundColor: isActive ? 'var(--asm-color-button-primary-bg)' : 'transparent',\n color: isActive ? 'var(--asm-color-button-primary-text)' : 'var(--asm-color-text)',\n fontSize: 'var(--asm-font-size-sm)',\n fontWeight: 'var(--asm-font-weight-500)',\n cursor: 'pointer',\n transition: 'var(--asm-transition-fade), var(--asm-transition-scale)',\n });\n\n return (\n <div style={containerStyles} className={className} role=\"radiogroup\" aria-label=\"Theme mode selector\">\n {options.map((option) => {\n const isActive = mode === option.mode;\n const Icon = option.Icon;\n return (\n <button\n key={option.mode}\n onClick={() => setMode(option.mode)}\n style={buttonStyles(isActive)}\n role=\"radio\"\n aria-checked={isActive}\n aria-label={`${option.label} mode`}\n onMouseEnter={(e) => {\n if (!isActive) {\n e.currentTarget.style.backgroundColor = 'var(--asm-color-button-ghost-bg-hover)';\n }\n }}\n onMouseLeave={(e) => {\n if (!isActive) {\n e.currentTarget.style.backgroundColor = 'transparent';\n }\n }}\n >\n <Icon size={16} strokeWidth={1.5} />\n <span>{option.label}</span>\n </button>\n );\n })}\n </div>\n );\n};\n","import * as React from \"react\";\nimport { useTheme } from \"../hooks/useTheme\";\nimport type { DensityMode } from \"../types\";\n\nexport interface DensitySelectorProps {\n className?: string;\n style?: React.CSSProperties;\n}\n\nexport const DensitySelector: React.FC<DensitySelectorProps> = ({\n className = \"\",\n style = {},\n}) => {\n const { density, setDensity } = useTheme();\n\n const options: Array<{ mode: DensityMode; label: string }> = [\n { mode: \"compact\", label: \"Compact\" },\n { mode: \"default\", label: \"Default\" },\n { mode: \"comfortable\", label: \"Comfortable\" },\n ];\n\n const containerStyles: React.CSSProperties = {\n display: 'inline-flex',\n gap: 'var(--asm-space-1)',\n padding: 'var(--asm-space-1)',\n borderRadius: 'var(--asm-radius-lg)',\n backgroundColor: 'var(--asm-color-surface-muted)',\n ...style,\n };\n\n const buttonStyles = (isActive: boolean): React.CSSProperties => ({\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n padding: 'var(--asm-space-2) var(--asm-space-3)',\n borderRadius: 'var(--asm-radius-md)',\n border: 'none',\n backgroundColor: isActive ? 'var(--asm-color-button-primary-bg)' : 'transparent',\n color: isActive ? 'var(--asm-color-button-primary-text)' : 'var(--asm-color-text)',\n fontSize: 'var(--asm-font-size-sm)',\n fontWeight: 'var(--asm-font-weight-500)',\n cursor: 'pointer',\n transition: 'var(--asm-transition-fade)',\n });\n\n return (\n <div style={containerStyles} className={className} role=\"radiogroup\" aria-label=\"Density selector\">\n {options.map((option) => {\n const isActive = density === option.mode;\n return (\n <button\n key={option.mode}\n onClick={() => setDensity(option.mode)}\n style={buttonStyles(isActive)}\n role=\"radio\"\n aria-checked={isActive}\n aria-label={`${option.label} density`}\n onMouseEnter={(e) => {\n if (!isActive) {\n e.currentTarget.style.backgroundColor = 'var(--asm-color-button-ghost-bg-hover)';\n }\n }}\n onMouseLeave={(e) => {\n if (!isActive) {\n e.currentTarget.style.backgroundColor = 'transparent';\n }\n }}\n >\n {option.label}\n </button>\n );\n })}\n </div>\n );\n};\n"],"mappings":"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","names":["createContext","useContext","useEffect","useState","jsx","ThemeContext","ThemeProvider","children","defaultMode","defaultDensity","defaultDirection","storageKey","enableTransitions","persistMode","persistDensity","persistDirection","getStoredValue","key","defaultValue","setStoredValue","value","persist","error","mode","setModeState","density","setDensityState","direction","setDirectionState","systemPrefersDark","setSystemPrefersDark","resolvedMode","setMode","newMode","setDensity","newDensity","setDirection","newDirection","toggleMode","mediaQuery","handleChange","e","root","contextValue","useThemeContext","context","useTheme","useThemeContext","React","Moon","Sun","jsx","jsxs","ThemeToggle","className","style","showLabels","size","lightIcon","darkIcon","ariaLabel","variant","resolvedMode","toggleMode","useTheme","iconSizeMap","circleSizeMap","renderIcon","icon","getIcon","getLabel","baseStyles","variantStyles","mergedStyles","e","Sun","Moon","Monitor","jsx","jsxs","ThemeSelector","className","style","variant","mode","setMode","useTheme","options","selectStyles","option","containerStyles","buttonStyles","isActive","Icon","e","jsx","DensitySelector","className","style","density","setDensity","useTheme","options","containerStyles","buttonStyles","isActive","option"]}