Skip to content

[Studio] feat: add centralized ThemeContext and useTheme hook for dark mode management#435

Merged
lizhimins merged 3 commits into
apache:rocketmq-studiofrom
zhaohai666:feature/studio-theme-context
Jul 17, 2026
Merged

[Studio] feat: add centralized ThemeContext and useTheme hook for dark mode management#435
lizhimins merged 3 commits into
apache:rocketmq-studiofrom
zhaohai666:feature/studio-theme-context

Conversation

@zhaohai666

Copy link
Copy Markdown

Summary

Introduce centralized theme management built on React Context, refactor scattered local dark mode useState inside MainLayout. The exported useTheme hook exposes darkMode, setDarkMode and toggleTheme, following the same design pattern as existing useLang i18n hook.

Changes

File Type Description
web/src/theme/ThemeContext.tsx Added Implement ThemeProvider and useTheme hook with TypeScript. Load initial theme preference from localStorage, fall back to system prefers-color-scheme. Automatically persist updated selection to localStorage.
web/src/layouts/MainLayout.tsx Modified Remove local useState(false) dark mode state, consume shared state via useTheme(). Delete redundant inline ConfigProvider theme wrapper (moved globally). Replace manual () => setDarkMode(!darkMode) calls with built-in toggleTheme.
web/src/main.tsx Modified Wrap root application with <ThemeProvider>. Lift Ant Design ConfigProvider theme logic to entry level; darkAlgorithm / defaultAlgorithm switching and dark-mode token overrides apply globally for the whole app.

Design Decisions

  1. React Context instead of Redux
    The codebase already adopts React Context for i18n (LangContext) and Zustand for authentication & cluster state. Theme state is presentation-layer UI state and mainly consumed inside layout components. React Context keeps consistent architecture with internationalization module, avoiding heavy state library dependency for a single boolean flag.

  2. localStorage persistence
    User selected theme will survive page refresh. Storage key: rocketmq-studio-theme.

  3. System color scheme fallback
    For first-time visitors without saved preference, automatically detect OS appearance via window.matchMedia('(prefers-color-scheme: dark)').

  4. Global root ConfigProvider
    Moving theme algorithm switching out of MainLayout guarantees all Ant Design components (including standalone modals created by ReactDOM.createRoot outside layout tree) correctly render dark style.

Hook API Usage

import { useTheme } from '../theme/ThemeContext';
const { darkMode, setDarkMode, toggleTheme } = useTheme();

@zhaohai666
zhaohai666 force-pushed the feature/studio-theme-context branch 2 times, most recently from 52bc1d0 to 8023ab1 Compare July 16, 2026 06:36
@lizhimins lizhimins changed the title [studio][feat] Add centralized ThemeContext and useTheme hook for dark mode management [Studio] feat: add centralized ThemeContext and useTheme hook for dark mode management Jul 17, 2026
…e to localStorage

- Create ThemeContext.tsx with React Context pattern (consistent with LangContext)
- Provide useTheme hook: darkMode, setDarkMode, toggleTheme
- Persist theme preference to localStorage with system preference fallback
- Refactor MainLayout.tsx: replace local useState with useTheme hook
- Elevate ConfigProvider theme logic to main.tsx for global dark mode support
- Remove redundant ConfigProvider wrapper from MainLayout.tsx
- Add vitest + @testing-library/react + @testing-library/user-event dev dependencies
- Configure vitest in vite.config.ts with jsdom environment
- Add test setup file with jest-dom matchers and localStorage cleanup
- Add ThemeContext unit tests (8 cases):
  - default light mode with no stored preference
  - read dark/light mode from localStorage
  - toggle between dark and light
  - persist theme to localStorage after toggle
  - setDarkMode(true/false) direct control
@zhaohai666
zhaohai666 force-pushed the feature/studio-theme-context branch from 94bdcdd to 374c8a7 Compare July 17, 2026 08:43
@lizhimins
lizhimins merged commit decb079 into apache:rocketmq-studio Jul 17, 2026
2 checks passed
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