feat(theme): restore dark mode toggle and polish light-mode styling#11
feat(theme): restore dark mode toggle and polish light-mode styling#11gomessguii wants to merge 1 commit intomainfrom
Conversation
- restore DarkModeProvider with localStorage + prefers-color-scheme and re-render ThemeToggle in the header - replace EVO_CRM.png with theme-aware AppLogo (dark/light SVG variants) across all 13 usages - fix contrast on dashboard tone badges, BaseStatusBadge and channel icon wrapper in light mode - rename "Operators" to "Team Members" in English locales to avoid collision with AI "Agents"
Reviewer's GuideRestores a fully functional dark mode system with theme persistence and header toggle, introduces a theme-aware SVG AppLogo used across the app, improves light-mode color contrast for status/tone badges and channel icons, and updates English i18n strings to rename "Operators" to "Team Members". Sequence diagram for theme initialization with persistence and system preferencesequenceDiagram
participant Browser
participant DarkModeProvider
participant LocalStorage
participant MatchMedia
participant DocumentElement
Browser->>DarkModeProvider: mount
DarkModeProvider->>LocalStorage: getItem(theme)
alt saved theme is light or dark
LocalStorage-->>DarkModeProvider: savedTheme
DarkModeProvider->>DarkModeProvider: setTheme(savedTheme)
else no valid saved theme
DarkModeProvider->>MatchMedia: matchMedia(prefers-color-scheme: dark)
MatchMedia-->>DarkModeProvider: matches true or false
DarkModeProvider->>DarkModeProvider: setTheme(matches ? dark : light)
end
DarkModeProvider->>DocumentElement: toggle dark class based on theme
DarkModeProvider->>LocalStorage: setItem(theme, theme)
DarkModeProvider->>MatchMedia: addEventListener(change, handleChange)
MatchMedia-->>DarkModeProvider: change event
DarkModeProvider->>LocalStorage: getItem(theme)
alt no explicit saved theme
DarkModeProvider->>DarkModeProvider: setTheme(matches ? dark : light)
DarkModeProvider->>DocumentElement: toggle dark class
else explicit saved theme exists
DarkModeProvider->>DarkModeProvider: keep existing theme
end
Sequence diagram for user toggling dark mode via ThemeTogglesequenceDiagram
actor User
participant ThemeToggle
participant useDarkMode
participant DarkModeProvider
participant DocumentElement
participant LocalStorage
User->>ThemeToggle: click toggle
ThemeToggle->>useDarkMode: useDarkMode()
useDarkMode-->>ThemeToggle: { theme, toggleTheme }
ThemeToggle->>DarkModeProvider: toggleTheme()
DarkModeProvider->>DocumentElement: add class theme-switching
DarkModeProvider->>DarkModeProvider: compute newTheme
DarkModeProvider->>DocumentElement: toggle dark class based on newTheme
DarkModeProvider->>LocalStorage: setItem(theme, newTheme)
DarkModeProvider->>DarkModeProvider: setTheme(newTheme)
DarkModeProvider->>DocumentElement: requestAnimationFrame remove theme-switching
DocumentElement-->>DocumentElement: remove class theme-switching
Class diagram for restored dark mode and theme-aware AppLogoclassDiagram
class DarkModeContextType {
<<interface>>
Theme theme
toggleTheme()
}
class ThemeContextModule {
+DarkModeContext
+DarkModeProvider(children)
}
class DarkModeContext {
<<context>>
}
class DarkModeProvider {
-Theme theme
-setTheme(theme)
+toggleTheme()
}
class useDarkModeHook {
+useDarkMode()
}
class AppLogo {
+className
+alt
+style
+forceTheme
}
class ThemeToggle {
+onClick()
}
class Header {
+ThemeToggle
+AppLogo
}
class DocumentElement {
+classList
}
class LocalStorage {
+getItem(key)
+setItem(key, value)
}
class MatchMedia {
+matches
+addEventListener(type, listener)
+removeEventListener(type, listener)
}
ThemeContextModule --> DarkModeContext : defines
ThemeContextModule --> DarkModeProvider : exports
DarkModeProvider --> DarkModeContext : provides DarkModeContextType
useDarkModeHook --> DarkModeContext : uses
AppLogo --> useDarkModeHook : calls useDarkMode
ThemeToggle --> useDarkModeHook : calls useDarkMode
Header --> ThemeToggle : renders
Header --> AppLogo : renders
DarkModeProvider --> DocumentElement : toggles dark class
DarkModeProvider --> LocalStorage : persists theme
DarkModeProvider --> MatchMedia : reads prefers_color_scheme
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- In
DarkModeProvider,toggleThemedirectly updatesdocument.documentElementandlocalStoragewhileuseLayoutEffectdoes the same based ontheme; consider centralizing DOM/localStorage updates in the effect and havingtoggleThemeonly callsetThemeto avoid duplicated and potentially inconsistent side effects. - The
matchMediachange listener inDarkModeProviderusesaddEventListener('change', ...)/removeEventListener, which is not supported in some older browsers; if broader browser support is required, consider falling back toaddListener/removeListenerwhenaddEventListeneris unavailable.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `DarkModeProvider`, `toggleTheme` directly updates `document.documentElement` and `localStorage` while `useLayoutEffect` does the same based on `theme`; consider centralizing DOM/localStorage updates in the effect and having `toggleTheme` only call `setTheme` to avoid duplicated and potentially inconsistent side effects.
- The `matchMedia` change listener in `DarkModeProvider` uses `addEventListener('change', ...)`/`removeEventListener`, which is not supported in some older browsers; if broader browser support is required, consider falling back to `addListener`/`removeListener` when `addEventListener` is unavailable.
## Individual Comments
### Comment 1
<location path="src/contexts/ThemeContext.tsx" line_range="28-29" />
<code_context>
+ localStorage.setItem('theme', theme);
+ }, [theme]);
+
+ useEffect(() => {
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
+
+ const handleChange = () => {
</code_context>
<issue_to_address>
**suggestion:** Consider a fallback for `matchMedia` listener to avoid issues in browsers that don't support `addEventListener` on MediaQueryList.
`mediaQuery.addEventListener('change', handleChange)` isn’t available in some older browsers (e.g. Safari), which still rely on `mediaQuery.addListener`. For wider compatibility, feature-detect `addEventListener` and fall back to `addListener`/`removeListener` when it’s missing so theme syncing still works there.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| useEffect(() => { | ||
| const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); |
There was a problem hiding this comment.
suggestion: Consider a fallback for matchMedia listener to avoid issues in browsers that don't support addEventListener on MediaQueryList.
mediaQuery.addEventListener('change', handleChange) isn’t available in some older browsers (e.g. Safari), which still rely on mediaQuery.addListener. For wider compatibility, feature-detect addEventListener and fall back to addListener/removeListener when it’s missing so theme syncing still works there.
Summary by Sourcery
Reintroduce a stateful dark mode system with a theme toggle and theme-aware branding while improving light-mode contrast and updating English terminology.
New Features:
Enhancements: