-
Notifications
You must be signed in to change notification settings - Fork 155
Description
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 aroundnext-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:
- Create
components/theme-provider.tsxwith next-themes wrapper - Create
components/theme-toggle.tsxwith two-state toggle button - Update
app/layout.tsx: Add ThemeProvider, remove hardcoded "dark" class, add suppressHydrationWarning - Update
components/navbar.tsx: Integrate ThemeToggle component (right-aligned) - Add CSS transitions (150ms fade for smooth theme switching)
- Both components have ABOUTME comments
Testing:
- Unit tests for ThemeToggle component (5 core scenarios):
- Component renders without crashing
- Toggle functionality works (light ↔ dark)
- localStorage persistence
- Initial load respects saved preference
- CSS variables update correctly
- Test coverage >80% for theme components
- Manual testing checklist completed (see below)
- Zero console errors/warnings
- No regressions in conversation feature
Documentation:
- Update CLAUDE.md with Theme Management section
- Update
.claude/sessions/context_session_dark_light_mode.mdwith completion status
Quality:
- Code matches existing style and conventions
- Follows hexagonal architecture principles (infrastructure concern)
- CI/CD passes (lint, build, tests)
- Code review approved
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
themekey 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-10on 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-labelupdates 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