UNPKG

@unicity/design-system

Version:

A comprehensive React component library built on Material-UI with advanced theming capabilities including neumorphism design support

367 lines 11.3 kB
/** * Theme Settings Utilities * Handles theme settings management, font scaling, and local storage */ // Font size scale mappings export const FONT_SIZE_SCALES = { 'small': 0.875, // 14px base 'medium': 1, // 16px base (default) 'large': 1.125, // 18px base 'extra-large': 1.25, // 20px base }; // Content density scale mappings export const CONTENT_DENSITY_SCALES = { 'compact': { spacing: 0.75, padding: 0.75 }, // 25% less space 'comfortable': { spacing: 1, padding: 1 }, // Default 'spacious': { spacing: 1.5, padding: 1.25 }, // 50% more spacing, 25% more padding }; // Touch target size mappings export const TOUCH_TARGET_SCALES = { 'standard': 1, // 44px minimum (WCAG AA) 'large': 1.25, // 55px (better for motor disabilities) 'extra-large': 1.5, // 66px (best accessibility) }; /** * System theme detection utilities */ export const SystemThemeUtils = { /** * Detect if user prefers dark mode from system settings */ getSystemThemePreference: () => { if (typeof window === 'undefined') return 'light'; return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; }, /** * Listen for system theme changes */ addSystemThemeListener: (callback) => { if (typeof window === 'undefined') return () => { }; const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); const listener = (e) => callback(e.matches); mediaQuery.addEventListener('change', listener); return () => mediaQuery.removeEventListener('change', listener); }, /** * Resolve auto mode to actual theme mode */ resolveAutoMode: (mode) => { if (mode === 'auto') { return SystemThemeUtils.getSystemThemePreference(); } return mode; }, }; // Default settings export const DEFAULT_THEME_SETTINGS = { mode: 'auto', accessibilityMode: 'default', fontSize: { level: 'medium', scale: FONT_SIZE_SCALES.medium, }, contentDensity: { density: 'comfortable', spacing: CONTENT_DENSITY_SCALES.comfortable.spacing, padding: CONTENT_DENSITY_SCALES.comfortable.padding, touchTargetSize: 'standard', }, motion: { reduceMotion: false, animationSpeed: 'normal', autoPlay: true, }, focusNavigation: { showFocusRings: true, enhancedKeyboardNav: false, skipLinks: false, focusTimeout: 0, }, reading: { readingMode: false, lineHeight: 1.5, letterSpacing: 0, wordSpacing: 0, readingGuide: false, }, colorVisibility: { highContrast: false, colorBlindSupport: 'none', saturation: 1, brightness: 1, }, audioAlerts: { soundEnabled: true, volume: 0.5, visualAlerts: false, vibration: false, }, }; // Local storage keys const STORAGE_KEYS = { THEME_SETTINGS: 'unicity-theme-settings', LEGACY_THEME: 'unicity-theme', // For backward compatibility }; /** * Font size utility functions */ export const FontSizeUtils = { /** * Get font size settings from level */ getSettingsFromLevel: (level) => ({ level, scale: FONT_SIZE_SCALES[level], }), /** * Get the next font size level */ getNextLevel: (currentLevel) => { const levels = ['small', 'medium', 'large', 'extra-large']; const currentIndex = levels.indexOf(currentLevel); const nextIndex = Math.min(currentIndex + 1, levels.length - 1); return levels[nextIndex]; }, /** * Get the previous font size level */ getPreviousLevel: (currentLevel) => { const levels = ['small', 'medium', 'large', 'extra-large']; const currentIndex = levels.indexOf(currentLevel); const prevIndex = Math.max(currentIndex - 1, 0); return levels[prevIndex]; }, /** * Get level index for slider (0-3) */ getLevelIndex: (level) => { const levels = ['small', 'medium', 'large', 'extra-large']; return levels.indexOf(level); }, /** * Get level from slider index (0-3) */ getLevelFromIndex: (index) => { const levels = ['small', 'medium', 'large', 'extra-large']; return levels[Math.max(0, Math.min(index, levels.length - 1))]; }, /** * Apply font scale to CSS-in-JS styles */ scaleFont: (baseSize, scale) => { return baseSize * scale; }, }; /** * Content density utility functions */ export const ContentDensityUtils = { /** * Get content density settings from density level */ getSettingsFromDensity: (density, touchTargetSize = 'standard') => ({ density, spacing: CONTENT_DENSITY_SCALES[density].spacing, padding: CONTENT_DENSITY_SCALES[density].padding, touchTargetSize, }), /** * Get density index for slider (0-2) */ getDensityIndex: (density) => { const densities = ['compact', 'comfortable', 'spacious']; return densities.indexOf(density); }, /** * Get density from slider index (0-2) */ getDensityFromIndex: (index) => { const densities = ['compact', 'comfortable', 'spacious']; return densities[Math.max(0, Math.min(index, densities.length - 1))]; }, /** * Get touch target size index for slider (0-2) */ getTouchTargetIndex: (size) => { const sizes = ['standard', 'large', 'extra-large']; return sizes.indexOf(size); }, /** * Get touch target size from slider index (0-2) */ getTouchTargetFromIndex: (index) => { const sizes = ['standard', 'large', 'extra-large']; return sizes[Math.max(0, Math.min(index, sizes.length - 1))]; }, /** * Apply spacing scale to CSS values */ scaleSpacing: (baseSpacing, scale) => { return baseSpacing * scale; }, /** * Apply padding scale to CSS values */ scalePadding: (basePadding, scale) => { return basePadding * scale; }, /** * Get minimum touch target size in pixels */ getTouchTargetSize: (size) => { return 44 * TOUCH_TARGET_SCALES[size]; // Base 44px (WCAG AA minimum) }, }; /** * Local storage management */ export const ThemeStorageUtils = { /** * Get stored theme settings */ getStoredSettings: () => { if (typeof window === 'undefined') { return DEFAULT_THEME_SETTINGS; } try { // Try new settings format first const stored = localStorage.getItem(STORAGE_KEYS.THEME_SETTINGS); if (stored) { const parsed = JSON.parse(stored); const result = { ...DEFAULT_THEME_SETTINGS, ...parsed, fontSize: { ...DEFAULT_THEME_SETTINGS.fontSize, ...(parsed.fontSize || {}), }, contentDensity: { ...DEFAULT_THEME_SETTINGS.contentDensity, ...(parsed.contentDensity || {}), }, motion: { ...DEFAULT_THEME_SETTINGS.motion, ...(parsed.motion || {}), }, focusNavigation: { ...DEFAULT_THEME_SETTINGS.focusNavigation, ...(parsed.focusNavigation || {}), }, reading: { ...DEFAULT_THEME_SETTINGS.reading, ...(parsed.reading || {}), }, colorVisibility: { ...DEFAULT_THEME_SETTINGS.colorVisibility, ...(parsed.colorVisibility || {}), }, audioAlerts: { ...DEFAULT_THEME_SETTINGS.audioAlerts, ...(parsed.audioAlerts || {}), }, }; return result; } // Fall back to legacy theme mode only const legacyTheme = localStorage.getItem(STORAGE_KEYS.LEGACY_THEME); if (legacyTheme && ['light', 'dark', 'neumorphism-light', 'neumorphism-dark', 'auto'].includes(legacyTheme)) { return { ...DEFAULT_THEME_SETTINGS, mode: legacyTheme, }; } } catch (error) { console.warn('Failed to parse stored theme settings:', error); } return DEFAULT_THEME_SETTINGS; }, /** * Store theme settings */ setStoredSettings: (settings) => { if (typeof window === 'undefined') return; try { localStorage.setItem(STORAGE_KEYS.THEME_SETTINGS, JSON.stringify(settings)); // Also update legacy key for backward compatibility localStorage.setItem(STORAGE_KEYS.LEGACY_THEME, settings.mode); } catch (error) { console.warn('Failed to store theme settings:', error); } }, /** * Clear stored settings */ clearStoredSettings: () => { if (typeof window === 'undefined') return; try { localStorage.removeItem(STORAGE_KEYS.THEME_SETTINGS); localStorage.removeItem(STORAGE_KEYS.LEGACY_THEME); } catch (error) { console.warn('Failed to clear stored theme settings:', error); } }, }; /** * High contrast utilities */ export const AccessibilityUtils = { /** * Get high contrast color overrides */ getHighContrastOverrides: (baseMode) => { if (baseMode === 'dark') { return { background: { default: '#000000', paper: '#1a1a1a', paper2: '#2d2d2d', }, text: { primary: '#ffffff', secondary: '#cccccc', }, primary: { main: '#ffffff', contrastText: '#000000', }, secondary: { main: '#ffff00', contrastText: '#000000', }, }; } return { background: { default: '#ffffff', paper: '#ffffff', paper2: '#f8f8f8', }, text: { primary: '#000000', secondary: '#333333', }, primary: { main: '#000000', contrastText: '#ffffff', }, secondary: { main: '#0000ff', contrastText: '#ffffff', }, }; }, /** * Check if high contrast mode is enabled */ isHighContrast: (mode) => { return mode === 'high-contrast'; }, }; //# sourceMappingURL=themeSettings.js.map