Skip to content

feat(theme): restore dark mode toggle and polish light-mode styling#11

Open
gomessguii wants to merge 1 commit intomainfrom
develop
Open

feat(theme): restore dark mode toggle and polish light-mode styling#11
gomessguii wants to merge 1 commit intomainfrom
develop

Conversation

@gomessguii
Copy link
Copy Markdown
Member

@gomessguii gomessguii commented Apr 17, 2026

  • 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"

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:

  • Add a persistent DarkModeProvider that respects saved preference and system prefers-color-scheme, with a UI ThemeToggle in the header.
  • Introduce a theme-aware AppLogo component with dark and light SVG variants and use it across authentication, setup, callbacks, loading, and layout screens.

Enhancements:

  • Adjust badge and channel icon colors to improve readability and contrast in light mode while preserving appropriate dark-mode styling.
  • Ensure onboarding and other flows use consistent, theme-aware branding via the new AppLogo component.
  • Update English i18n strings to rename user-facing "Operators" references to "Team Members" to avoid confusion with AI agents.

- 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"
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Apr 17, 2026

Reviewer's Guide

Restores 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 preference

sequenceDiagram
  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
Loading

Sequence diagram for user toggling dark mode via ThemeToggle

sequenceDiagram
  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
Loading

Class diagram for restored dark mode and theme-aware AppLogo

classDiagram
  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
Loading

File-Level Changes

Change Details Files
Reimplement dynamic dark mode handling with persistence and system preference integration.
  • Replace hardcoded dark theme with stateful theme initialized from localStorage or prefers-color-scheme media query.
  • Synchronize documentElement .dark class and localStorage with the current theme in a layout effect.
  • Listen for prefers-color-scheme changes and update theme only when no explicit theme is saved.
  • Enhance toggleTheme to animate theme transitions via a temporary theme-switching class and double requestAnimationFrame.
src/contexts/ThemeContext.tsx
Add theme-aware AppLogo component and replace legacy PNG logo usages.
  • Introduce AppLogo component that selects dark or light SVG logo based on current theme or an optional forceTheme prop.
  • Add new SVG logo assets for dark and light modes.
  • Refactor multiple pages (auth, setup, callbacks, loading, onboarding) to use AppLogo instead of the EVO_CRM.png image, removing fallback logic.
src/components/AppLogo.tsx
src/assets/EVO_CRM.svg
src/assets/EVO_CRM_light.svg
src/components/auth/ResetPassword.tsx
src/components/LoadingScreen.tsx
src/pages/ChangePassword/ChangePassword.tsx
src/components/auth/EmailConfirmation.tsx
src/components/auth/MfaVerification.tsx
src/pages/Auth/Auth.tsx
src/pages/GoogleCallback.tsx
src/pages/InstagramCallback.tsx
src/pages/MicrosoftCallback.tsx
src/pages/Setup/Setup.tsx
src/components/integrations/CallbackPage.tsx
src/pages/Setup/OnboardingPage.tsx
Expose dark mode control in the main header layout.
  • Import and render ThemeToggle in both desktop header actions and mobile sidebar drawer.
  • Replace inline logo elements in the header with AppLogo, simplifying error handling and centralizing logo theming.
src/components/layout/components/Header.tsx
Improve light-mode contrast for status/tone badges and channel icons while preserving dark-mode styling.
  • Update BaseStatusBadge color classes to use brighter Tailwind palette colors in light mode with dark: overrides for existing dark-mode hex values.
  • Adjust dashboard metric tone badges and unassigned conversation tone badges to use darker text colors in light mode and previous colors behind dark: variants.
  • Switch ChannelIcon container backgrounds to light gray in light mode with dark gray in dark mode to improve legibility.
src/components/base/BaseStatusBadge.tsx
src/pages/Customer/Dashboard/components/DashboardMetricsSection.tsx
src/pages/Customer/Dashboard/components/DashboardMetricCard.tsx
src/components/channels/ChannelIcon.tsx
Rename "Operators" to "Team Members" across English locales to avoid confusion with AI "Agents".
  • Update relevant English i18n JSON files so any UI labels or descriptions previously referring to operators now say team members instead.
src/i18n/locales/en/cannedResponses.json
src/i18n/locales/en/channels.json
src/i18n/locales/en/chat.json
src/i18n/locales/en/contacts.json
src/i18n/locales/en/customerDashboard.json
src/i18n/locales/en/integrations.json
src/i18n/locales/en/layout.json
src/i18n/locales/en/macros.json
src/i18n/locales/en/teams.json
src/i18n/locales/en/tours.json
src/i18n/locales/en/users.json
src/i18n/locales/en/widget.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • 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.
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>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +28 to +29
useEffect(() => {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

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.

1 participant