Skip to content

fix(app): wire settings theme switcher with resilient persistence#2167

Open
AchuAshwath wants to merge 2 commits intokriasoft:mainfrom
AchuAshwath:fix/theme-switcher
Open

fix(app): wire settings theme switcher with resilient persistence#2167
AchuAshwath wants to merge 2 commits intokriasoft:mainfrom
AchuAshwath:fix/theme-switcher

Conversation

@AchuAshwath
Copy link
Contributor

Summary

  • Add app-level ThemeProvider and wire the settings dark-mode switch to shared theme state.
  • Persist user theme preference with fallback to system preference.
  • Handle storage edge cases safely and support cross-tab synchronization.
  • Add focused tests for theme behavior and failure modes.

Why

The dark-mode switch in Settings was previously not connected to application theme behavior. This change makes theme switching functional, persistent, and robust across browser/storage conditions.

Changes

  • Wrap routed app with ThemeProvider in apps/app/index.tsx.
  • Implement theme state management in apps/app/lib/theme.tsx:
    • storage -> system preference -> default resolution
    • class sync on <html> with useLayoutEffect
    • guarded localStorage read/write
    • storage event handling for multi-tab sync
  • Connect Switch in apps/app/routes/(app)/settings.tsx to theme state.
  • Add apps/app/lib/theme.test.tsx with behavior-first coverage for:
    • persisted theme
    • system fallback
    • DOM class + storage updates
    • storage event sync
    • storage read/write failure handling

In ThemeProvider, the context value is memoized with:

useMemo(() => ({ theme, setTheme, toggleTheme }), [theme])

Since toggleTheme is currently stabilized with useCallback([]), is [theme] sufficient here, or do we prefer [theme, toggleTheme] for explicitness/future-proofing if toggleTheme dependencies change later?

Signed-off-by: AchuAshwath <achuashwath88@gmail.com>
Made-with: Cursor
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Wires the settings dark-mode toggle to actual theme state via a new ThemeProvider context, with localStorage persistence, system-preference fallback, and cross-tab sync via storage events.

Changes:

  • New ThemeProvider context (apps/app/lib/theme.tsx) managing theme state, DOM class sync, guarded localStorage persistence, and cross-tab synchronization.
  • Settings page switch connected to theme context; app entry point wrapped with ThemeProvider.
  • Comprehensive test suite covering persisted theme, system fallback, DOM/storage sync, and failure modes.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
apps/app/lib/theme.tsx New theme context provider with localStorage persistence, system preference fallback, and cross-tab sync
apps/app/index.tsx Wraps RouterProvider with ThemeProvider at the app root
apps/app/routes/(app)/settings.tsx Connects the dark-mode Switch to the theme context
apps/app/lib/theme.test.tsx Tests for theme initialization, toggling, storage events, and failure handling

You can also share your feedback on Copilot code review. Take the survey.

setTheme,
toggleTheme,
}),
[theme],
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The useMemo dependency array [theme] omits toggleTheme. While toggleTheme is currently stable (due to useCallback([], [])), this will break silently if toggleTheme ever gains dependencies—and it will trigger an react-hooks/exhaustive-deps lint warning. Include toggleTheme in the dependency array for correctness and future safety. (setTheme from useState is guaranteed stable by React, so it doesn't need to be listed.)

Suggested change
[theme],
[theme, toggleTheme],

Copilot uses AI. Check for mistakes.
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.

2 participants