apache / apache/apisix-dashboard
Feat: Dark Mode Support for APISIX Dashboard
- Dominant language
- TypeScript
- Stars
- 1.2k
- Forks
- 617
- Avg merge
- 4d 18h
- Merged PRs (30d)
- 1
Description
# Feature request
## Please describe your feature
Add dark mode theme support to the APISIX Dashboard, allowing users to switch between light, dark, and system preferred color schemes.
Users frequently work with the dashboard for extended periods, and the current light only theme causes eye strain in low light environments. Most modern admin dashboards (Grafana, Kibana, Cloudflare) offer dark mode as a standard feature. Additionally, system-level dark mode preferences (`prefers-color-scheme`) are widely adopted across operating systems, and the dashboard currently does not respect them.
## Describe the solution you'd like
A three-way theme switcher (Light / Dark / Auto) in the dashboard header, implemented as a segmented control. The "Auto" option follows the user's OS preference via the `prefers-color-scheme` media query.
The implementation leverages the existing stack's built-in dark mode support:
- **Mantine v8**: Set `defaultColorScheme="auto"` on `MantineProvider` and use `useMantineColorScheme()` for runtime toggling. Mantine handles persistence to `localStorage` internally no additional state management is needed.
- **Ant Design v5**: Use Mantine's `useComputedColorScheme('light')` to resolve the actual scheme (since Ant Design does not handle `'auto'` internally), then conditionally apply `theme.darkAlgorithm` on `ConfigProvider`.
- **Monaco Editor**: Imperatively call `monaco.editor.setTheme()` via a `useEffect` + `onMount` ref, since Monaco does not re-theme on React prop changes after initial mount.
- **FOUC prevention**: Add an inline `` in `index.html` that reads the persisted scheme from `localStorage` and sets `data-mantine-color-scheme` on `<html>` before CSS parses.
### Files to modify
| File | Change |
|------|--------|
| `index.html` | Inline `<script>` for FOUC prevention |
| `src/main.tsx` | `defaultColorScheme="auto"` on `MantineProvider` |
| `src/config/antdConfigProvider.tsx` | `useComputedColorScheme()` → conditional `theme.darkAlgorithm` |
| `src/components/form/Editor.tsx` | Imperative `monaco.editor.setTheme()` via `useEffect` + `onMount` ref |
| `src/components/Header/index.tsx` | Add `ThemeToggle` component |
| `src/styles/global.css` | Audit for any hardcoded colors |
### New files
| File | Purpose |
|------|---------|
| `src/components/Header/ThemeToggle.tsx` | Segmented control theme switcher (Light / Dark / Auto) |
A detailed proposal with the full implementation plan is available at [`docs/proposal-dark-mode.md`](../docs/proposal-dark-mode.md).
## Describe alternatives you've considered
1. **Two-way toggle (Light / Dark only)**: Simpler, but ignores the user's OS preference. Users who switch OS themes throughout the day would need to manually toggle the dashboard each time.
2. **Separate Jotai atom for color scheme state**: Considered using `atomWithStorage` (already used for admin key persistence), but Mantine already persists the color scheme to `localStorage` internally under `mantine-color-scheme-value`. Adding a separate atom would create a dual source of truth.
3. **`forceColorScheme` on `MantineProvider`**: This overrides system preference entirely, making "Auto" mode a silent no-op. Using `defaultColorScheme="auto"` with `useMantineColorScheme()` is the correct approach.
4. **Mantine's `ColorSchemeScript` React component in `index.html`**: `ColorSchemeScript` is a React component and cannot be used as JSX in a plain HTML file. An inline `<script>` that reads `localStorage` and sets the `data-mantine-color-scheme` attribute achieves the same FOUC prevention without requiring React to mount first.
## Additional context
- The existing custom CSS in `global.css` already uses Mantine CSS variables (`--mantine-color-*`), which automatically adapt when the color scheme changes no manual CSS overrides are needed for most components.
- This change is purely additive no structural or architectural changes are required.
- Ant Design is only used for `ProTable` on list pages; a single `ConfigProvider` algorithm swap covers all table instances.
- E2E tests should verify auto mode resolution (using Playwright's `emulateMedia({ colorScheme: 'dark' })`) and theme persistence across page reloads.
Contributor guide
Research direction
Start with docs/proposal-dark-mode.md, then inspect index.html, src/main.tsx, src/config/antdConfigProvider.tsx, src/components/form/Editor.tsx, src/components/Header/index.tsx, src/components/Header/ThemeToggle.tsx, and src/styles/global.css. Verify the three-way switcher, persisted scheme, FOUC prevention, Ant Design and Monaco updates, and the proposed Playwright checks for system preference and reload persistence.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- playwright, react, typescript
- Domain
- frontend, testing-qa
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100