Problem Statement
The application currently has a hardcoded dark theme (see app/layout.tsx:36), preventing users from choosing their preferred color scheme. This creates accessibility issues for users who:
- Prefer light mode for better readability in bright environments
- Have visual sensitivities to dark backgrounds
- Want consistency with their OS-level theme preference
Current limitation: The className="dark" is hardcoded in the root layout, ignoring user preferences and system settings.
User Value
Users will gain:
- Theme Control: Toggle between light and dark modes with a single click via a navbar button
- Smart Defaults: Initial theme automatically respects OS-level preference (Windows/macOS dark mode setting)
- Persistence: Theme choice persists across browser sessions via localStorage
- Smooth Experience: Subtle 150ms fade transitions during theme switches
- Accessibility: WCAG 2.1 AA compliant toggle with keyboard navigation and proper ARIA labels
Concrete Example: A user working in a bright office can switch to light mode for reduced eye strain, and when they return later, the app remembers their preference. If a new user visits with dark mode enabled in their OS, the app starts in dark mode automatically.
Technical Implementation
Architecture Decision: Theme management is an infrastructure-level concern (not a feature), implemented as:
- ThemeProvider (
components/theme-provider.tsx) - Thin wrapper around next-themes
- ThemeToggle (
components/theme-toggle.tsx) - Two-state toggle button (Light ↔ Dark)
- Root Layout (
app/layout.tsx) - Wrap with ThemeProvider, remove hardcoded dark class
- Navbar (
components/navbar.tsx) - Integrate ThemeToggle component (right-aligned)
Dependencies: All required packages are already installed:
next-themes@^0.2.1 ✅
lucide-react@^0.460.0 ✅
button (shadcn/ui) ✅
Key Design Choices:
- Two-state toggle (Light ↔ Dark only) for maximum simplicity
- System preference as default (
defaultTheme="system")
- Responsive button sizing:
h-8 w-8 md:h-10 md:w-10
- Sun/Moon icons with smooth rotate/scale animations
- No dropdown menu needed (simpler UX)
Definition of Done
Implementation:
Testing:
Documentation:
Quality:
Manual Testing Checklist
Basic Flow:
- Start dev server (
yarn dev)
- Open app in browser - verify initial theme matches OS preference
- Click theme toggle button - verify smooth transition to opposite theme
- Click again - verify return to original theme
- Check DevTools → Application → Local Storage - verify
theme key exists with correct value
- Refresh page - verify theme persists
Edge Case Testing:
- Rapid Toggling: Click toggle 10 times quickly - verify no UI glitches or console errors
- New User (No localStorage): Clear localStorage → refresh → verify defaults to system preference
- System Preference Override: Set OS to dark mode → app starts dark → toggle to light → app stays light (ignores OS)
- Private Browsing: Open in incognito mode → verify theme still works (falls back to in-memory storage)
Error Handling:
- Hydration Check: Open console → verify NO hydration mismatch errors
- White Flash Test: Toggle theme multiple times → verify NO white flash during transitions
- Build Test: Run
yarn build → verify NO build errors related to theme
Integration Testing:
- Conversation Feature: Send chat messages in both themes → verify streaming works, no regressions
- Navbar Layout: Verify theme toggle is right-aligned, proper spacing
- Responsive Design: Test on mobile (DevTools responsive mode) → verify button sizing (
h-8 w-8 → h-10 w-10 on md+)
Accessibility Testing:
- Keyboard Navigation: Tab to theme toggle → press Enter/Space → verify toggle works
- Screen Reader: Use VoiceOver/NVDA → verify button announces current theme state
- ARIA Labels: Inspect button element → verify
aria-label updates dynamically ("Switch to dark mode" / "Switch to light mode")
- Focus Visible: Tab to button → verify focus ring is visible
Visual Testing:
- Icon Animation: Toggle theme → verify Sun/Moon icons rotate and scale smoothly
- Color Consistency: Check all UI elements (navbar, chat bubbles, buttons) → verify colors update correctly
- CSS Variables: Inspect elements → verify
--background, --foreground, etc. update on toggle
Related Documentation
Comprehensive planning documentation available:
.claude/sessions/context_session_dark_light_mode.md - Complete implementation plan with all decisions
.claude/doc/dark_light_mode/theme-architecture-integration.md - Architecture analysis
.claude/doc/dark_light_mode/theme-testing-strategy.md - Testing strategy
.claude/doc/dark_light_mode/theme-toggle-component-design.md - UI/UX design details
Estimated Effort
Total: 2-2.5 hours
Breakdown:
- Verify dependencies: 5 min
- Create ThemeProvider: 10 min
- Create ThemeToggle: 20 min
- Update Layout: 15 min
- Update Navbar: 10 min
- Manual Testing: 15 min
- Write Core Tests: 30-45 min
- Documentation: 10 min
Problem Statement
The application currently has a hardcoded dark theme (see
app/layout.tsx:36), preventing users from choosing their preferred color scheme. This creates accessibility issues for users who:Current limitation: The
className="dark"is hardcoded in the root layout, ignoring user preferences and system settings.User Value
Users will gain:
Concrete Example: A user working in a bright office can switch to light mode for reduced eye strain, and when they return later, the app remembers their preference. If a new user visits with dark mode enabled in their OS, the app starts in dark mode automatically.
Technical Implementation
Architecture Decision: Theme management is an infrastructure-level concern (not a feature), implemented as:
components/theme-provider.tsx) - Thin wrapper aroundnext-themescomponents/theme-toggle.tsx) - Two-state toggle button (Light ↔ Dark)app/layout.tsx) - Wrap with ThemeProvider, remove hardcoded dark classcomponents/navbar.tsx) - Integrate ThemeToggle component (right-aligned)Dependencies: All required packages are already installed:
next-themes@^0.2.1✅lucide-react@^0.460.0✅button(shadcn/ui) ✅Key Design Choices:
defaultTheme="system")h-8 w-8 md:h-10 md:w-10Definition of Done
Implementation:
components/theme-provider.tsxwith next-themes wrappercomponents/theme-toggle.tsxwith two-state toggle buttonapp/layout.tsx: Add ThemeProvider, remove hardcoded "dark" class, add suppressHydrationWarningcomponents/navbar.tsx: Integrate ThemeToggle component (right-aligned)Testing:
Documentation:
.claude/sessions/context_session_dark_light_mode.mdwith completion statusQuality:
Manual Testing Checklist
Basic Flow:
yarn dev)themekey exists with correct valueEdge Case Testing:
Error Handling:
yarn build→ verify NO build errors related to themeIntegration Testing:
h-8 w-8→h-10 w-10on md+)Accessibility Testing:
aria-labelupdates dynamically ("Switch to dark mode" / "Switch to light mode")Visual Testing:
--background,--foreground, etc. update on toggleRelated Documentation
Comprehensive planning documentation available:
.claude/sessions/context_session_dark_light_mode.md- Complete implementation plan with all decisions.claude/doc/dark_light_mode/theme-architecture-integration.md- Architecture analysis.claude/doc/dark_light_mode/theme-testing-strategy.md- Testing strategy.claude/doc/dark_light_mode/theme-toggle-component-design.md- UI/UX design detailsEstimated Effort
Total: 2-2.5 hours
Breakdown: