Skip to content

feat: dark mode with system preference detection (#104)#375

Open
i010542 wants to merge 1 commit intorohitdash08:mainfrom
i010542:feat/dark-mode-104
Open

feat: dark mode with system preference detection (#104)#375
i010542 wants to merge 1 commit intorohitdash08:mainfrom
i010542:feat/dark-mode-104

Conversation

@i010542
Copy link

@i010542 i010542 commented Mar 11, 2026

Problem Summary

The app lacked a dark mode, making it uncomfortable for users in low-light environments and failing accessibility contrast requirements.

Solution Approach

  • ThemeProvider context (src/hooks/use-theme.tsx): manages light | dark | system modes, persists to localStorage with validation, listens to prefers-color-scheme changes
  • FOUC prevention: inline script in index.html applies theme class before React hydration — no flash on page load
  • CSS variables: full .dark { ... } override in index.css using a WCAG AA-compliant navy palette
  • ThemeToggle in Navbar: cycles light → dark → system with accessible aria-label and focus-visible ring
  • Fixed all hardcoded bg-white / hsl(0 0% 100%) references to use semantic tokens

Key Design Decisions

Decision Reason
createContext<ThemeContextType | undefined>(undefined) Enables proper guard: useTheme throws if used outside Provider
parseTheme() validation Guards against corrupted / manually-edited localStorage values
Inline sync script before <body> Applies dark class synchronously — eliminates FOUC
useMemo on context value Prevents unnecessary re-renders of all consumers

Files Changed

File Change
app/src/hooks/use-theme.tsx New: ThemeProvider + useTheme hook
app/src/App.tsx Wrap app with ThemeProvider
app/src/index.css Add .dark { ... } CSS variable overrides
app/src/components/layout/Navbar.tsx Add ThemeToggle button
app/index.html Add FOUC-prevention inline script
app/src/__tests__/useTheme.test.tsx New: 11 unit tests
app/src/__tests__/Navbar.test.tsx Add ThemeProvider wrapper to existing tests

Test Evidence

Test Suites: 5 passed, 5 total
Tests:       38 passed, 38 total
TypeScript:  0 errors

New tests include:

  • Invalid localStorage value → falls back to system
  • System preference dark → applies dark class
  • System preference light → removes dark class
  • Toggle cycles correctly: light → dark → system → light
  • useTheme outside Provider → throws descriptive error

Resolves: #104

- Add ThemeProvider context with light/dark/system modes
- Persist preference to localStorage with validation
- Add FOUC-prevention inline script to index.html
- Add ThemeToggle button to Navbar (cycles light->dark->system)
- Override all CSS variables for dark theme in index.css
- Fix hardcoded bg-white references to use semantic tokens
- Add 11 unit tests for useTheme hook
@i010542 i010542 requested a review from rohitdash08 as a code owner March 11, 2026 06:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dark mode & accessibility contrast improvements

1 participant