# Settings & Dark Mode Implementation **Status**: ✅ Phase 1 Complete, Phase 2 Complete **Last Updated**: January 2026 --- ## Overview A comprehensive user settings system with persistent preferences, dark mode support, and customizable list display options. Settings are stored per-user in localStorage and automatically applied across the application. --- ## Architecture ### Context Hierarchy ``` ← Server config (image limits, etc.) ← Authentication state ← User preferences (NEW) ``` ### Key Components #### SettingsContext ([frontend/src/context/SettingsContext.jsx](frontend/src/context/SettingsContext.jsx)) - Manages user preferences with localStorage persistence - Storage key pattern: `user_preferences_${username}` - Automatically applies theme to `document.documentElement` - Listens for system theme changes in auto mode - Provides `updateSettings()` and `resetSettings()` methods #### Settings Page ([frontend/src/pages/Settings.jsx](frontend/src/pages/Settings.jsx)) - Tabbed interface: Appearance, List Display, Behavior - Real-time preview of setting changes - Reset to defaults functionality --- ## Settings Schema ```javascript { // === Appearance === theme: "light" | "dark" | "auto", // Theme mode compactView: false, // Reduced spacing for denser lists // === List Display === defaultSortMode: "zone", // Default: "zone" | "az" | "za" | "qty-high" | "qty-low" showRecentlyBought: true, // Toggle recently bought section recentlyBoughtCount: 10, // Initial items shown (5-50) recentlyBoughtCollapsed: false, // Start section collapsed // === Behavior === confirmBeforeBuy: true, // Show confirmation modal autoReloadInterval: 0, // Auto-refresh in minutes (0 = disabled) hapticFeedback: true, // Vibration on mobile interactions // === Advanced === debugMode: false // Developer tools (future) } ``` --- ## Dark Mode Implementation ### Theme System **Three modes**: 1. **Light**: Force light theme 2. **Dark**: Force dark theme 3. **Auto**: Follow system preferences with live updates ### CSS Variable Architecture All colors use CSS custom properties defined in [frontend/src/styles/theme.css](frontend/src/styles/theme.css): **Light Mode** (`:root`): ```css :root { --color-text-primary: #212529; --color-bg-body: #f8f9fa; --color-bg-surface: #ffffff; /* ... */ } ``` **Dark Mode** (`[data-theme="dark"]`): ```css [data-theme="dark"] { --color-text-primary: #f1f5f9; --color-bg-body: #0f172a; --color-bg-surface: #1e293b; /* ... */ } ``` ### Theme Application Logic ```javascript // In SettingsContext.jsx useEffect(() => { const applyTheme = () => { let theme = settings.theme; // Auto mode: check system preference if (theme === "auto") { const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches; theme = prefersDark ? "dark" : "light"; } document.documentElement.setAttribute("data-theme", theme); }; applyTheme(); // Listen for system theme changes if (settings.theme === "auto") { const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); mediaQuery.addEventListener("change", applyTheme); return () => mediaQuery.removeEventListener("change", applyTheme); } }, [settings.theme]); ``` --- ## Integration with Existing Features ### GroceryList Integration **Changed**: ```javascript // Before const [sortMode, setSortMode] = useState("zone"); const [recentlyBoughtDisplayCount, setRecentlyBoughtDisplayCount] = useState(10); // After const { settings } = useContext(SettingsContext); const [sortMode, setSortMode] = useState(settings.defaultSortMode); const [recentlyBoughtDisplayCount, setRecentlyBoughtDisplayCount] = useState(settings.recentlyBoughtCount); const [recentlyBoughtCollapsed, setRecentlyBoughtCollapsed] = useState(settings.recentlyBoughtCollapsed); ``` **Features**: - Sort mode persists across sessions - Recently bought section respects visibility setting - Collapse state controlled by user preference - Initial display count uses user's preference --- ## File Structure ``` frontend/src/ ├── context/ │ └── SettingsContext.jsx ← Settings state management ├── pages/ │ └── Settings.jsx ← Settings UI ├── styles/ │ ├── theme.css ← Dark mode CSS variables │ └── pages/ │ └── Settings.css ← Settings page styles └── App.jsx ← Settings route & provider ``` --- ## Usage Examples ### Access Settings in Component ```javascript import { useContext } from "react"; import { SettingsContext } from "../context/SettingsContext"; function MyComponent() { const { settings, updateSettings } = useContext(SettingsContext); // Read setting const isDark = settings.theme === "dark"; // Update setting const toggleTheme = () => { updateSettings({ theme: settings.theme === "dark" ? "light" : "dark" }); }; return ; } ``` ### Conditional Rendering Based on Settings ```javascript {settings.showRecentlyBought && ( )} ``` ### Using Theme Colors ```css .my-component { background: var(--color-bg-surface); color: var(--color-text-primary); border: 1px solid var(--color-border-light); } ``` --- ## localStorage Structure **Key**: `user_preferences_${username}` **Example stored value**: ```json { "theme": "dark", "compactView": false, "defaultSortMode": "zone", "showRecentlyBought": true, "recentlyBoughtCount": 20, "recentlyBoughtCollapsed": false, "confirmBeforeBuy": true, "autoReloadInterval": 0, "hapticFeedback": true, "debugMode": false } ``` --- ## Testing Checklist ### Settings Page - [ ] All three tabs accessible - [ ] Theme toggle works (light/dark/auto) - [ ] Auto mode follows system preference - [ ] Settings persist after logout/login - [ ] Reset button restores defaults ### Dark Mode - [ ] All pages render correctly in dark mode - [ ] Modals readable in dark mode - [ ] Forms and inputs visible in dark mode - [ ] Navigation and buttons styled correctly - [ ] Images and borders contrast properly ### GroceryList Integration - [ ] Default sort mode applied on load - [ ] Recently bought visibility respected - [ ] Collapse state persists during session - [ ] Display count uses user preference --- ## Future Enhancements (Not Implemented) ### Phase 3: Advanced Preferences - **Compact View**: Reduced padding/font sizes for power users - **Confirm Before Buy**: Toggle for confirmation modal - **Auto-reload**: Refresh list every X minutes for shared lists ### Phase 4: Account Management - **Change Password**: Security feature (needs backend endpoint) - **Display Name**: Friendly name separate from username ### Phase 5: Data Management - **Export List**: Download as CSV/JSON - **Clear History**: Remove recently bought items - **Import Items**: Bulk add from file ### Phase 6: Accessibility - **Font Size**: Adjustable text sizing - **High Contrast Mode**: Increased contrast for visibility - **Reduce Motion**: Disable animations --- ## API Endpoints **None required** - all settings are client-side only. Future backend endpoints may include: - `PATCH /api/users/me` - Update user profile (password, display name) - `GET /api/list/export` - Export grocery list data --- ## Browser Compatibility ### Theme Detection - Chrome/Edge: ✅ Full support - Firefox: ✅ Full support - Safari: ✅ Full support (iOS 12.2+) - Mobile browsers: ✅ Full support ### localStorage - All modern browsers: ✅ Supported - Fallback: Settings work but don't persist (rare) --- ## Troubleshooting ### Settings Don't Persist **Issue**: Settings reset after logout **Cause**: Settings tied to username **Solution**: Working as designed - each user has separate preferences ### Dark Mode Not Applied **Issue**: Page stays light after selecting dark **Cause**: Missing `data-theme` attribute **Solution**: Check SettingsContext is wrapped around App ### System Theme Not Detected **Issue**: Auto mode doesn't work **Cause**: Browser doesn't support `prefers-color-scheme` **Solution**: Fallback to light mode (handled automatically) --- ## Development Notes ### Adding New Settings 1. **Update DEFAULT_SETTINGS** in SettingsContext.jsx: ```javascript const DEFAULT_SETTINGS = { // ...existing settings myNewSetting: defaultValue, }; ``` 2. **Add UI in Settings.jsx**: ```javascript

Description here

``` 3. **Use in components**: ```javascript const { settings } = useContext(SettingsContext); if (settings.myNewSetting) { // Do something } ``` ### Adding Theme Colors 1. Define in both light (`:root`) and dark (`[data-theme="dark"]`) modes 2. Use descriptive semantic names: `--color-purpose-variant` 3. Always provide fallbacks for older code --- ## Performance Considerations - Settings load once per user session - Theme changes apply instantly (no page reload) - localStorage writes are debounced by React state updates - No network requests for settings (all client-side) --- ## Accessibility - ✅ Keyboard navigation works in Settings page - ✅ Theme buttons have clear active states - ✅ Range sliders show current values - ✅ Color contrast meets WCAG AA in both themes - ⚠️ Screen reader announcements for theme changes (future enhancement) --- ## Migration Notes **Upgrading from older versions**: - Old settings are preserved (merged with defaults) - Missing settings use default values - Invalid values are reset to defaults - No migration script needed - handled automatically --- ## Related Documentation - [Code Cleanup Guide](code-cleanup-guide.md) - Code organization patterns - [Component Structure](component-structure.md) - Component architecture - [Theme Usage Examples](../frontend/src/styles/THEME_USAGE_EXAMPLES.css) - CSS variable usage --- **Implementation Status**: ✅ Complete **Phase 1 (Foundation)**: ✅ Complete **Phase 2 (Dark Mode)**: ✅ Complete **Phase 3 (List Preferences)**: ✅ Complete **Phase 4+ (Future)**: ⏳ Planned