Repository navigation
feat(ui): add explicit system option to the theme toggle - #5639
Open
fabianfrankwerner wants to merge 1 commit into
Open
fabianfrankwerner wants to merge 1 commit into
fabianfrankwerner wants to merge 1 commit into
Conversation
The toggle only ever called setTheme("dark")/setTheme("light"), which
next-themes persists to localStorage. Although _app.tsx already sets
defaultTheme="system" with enableSystem, that is only a first-visit
default: after a single click the choice is sticky and there is no UI
anywhere that can restore "system", so the panel stops following OS
appearance changes.
Replace the two-state button with a dropdown exposing Light, Dark and
System as radio items, so the current mode is visible and selectable.
The trigger keeps the existing Button and the existing Sun/Moon
rotate+scale classes verbatim. It still renders from the `dark` class on
<html>, which next-themes applies in its pre-hydration script, so in
system mode the icon reflects the resolved appearance and the animation
still plays when the OS theme changes.
The mounted guard is scoped to the radio group `value` only, so the
checkmark cannot cause a hydration mismatch and the trigger renders
identically to before. No change to _app.tsx and no new dependency.
Closes Dokploy#5638
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What is this PR about?
Adds an explicit System option to the dark/light mode setting, closing #5638.
apps/dokploy/pages/_app.tsxalready configuresnext-themeswithdefaultTheme="system"andenableSystem, butapps/dokploy/components/ui/modeToggle.tsxonly ever calledsetTheme("dark")/setTheme("light"), andnext-themespersists that choice tolocalStorage. So "system" was only a first-visit default: after a single click the choice became sticky and there was no UI anywhere that could restore"system"— the only way back was manually clearinglocalStorage. As a result the panel stopped following OS-level appearance changes (macOS/iOS Dark Mode, forced-colors on Windows/Linux).This PR turns the existing button into a dropdown with three radio items — Light, Dark, System — so the active mode is both visible and selectable.
Design notes, aimed at staying minimal:
Buttonand the existingSun/Moonrotate+scaleclasses verbatim. It still renders off thedarkclass on<html>, whichnext-themessets in its pre-hydration script, so in system mode the icon shows the resolved appearance and the animation still plays when the OS theme changes. No third icon on the trigger, and no lost animation.mountedguard is scoped to the radio groupvalueonly, so the checkmark cannot cause a hydration mismatch and the trigger renders exactly as before.DropdownMenuRadioGroup/DropdownMenuRadioItemalready exist incomponents/ui/dropdown-menu.tsx;sonner.tsxalready forwards"system"to the toaster. No new dependency, no schema change, and_app.tsxis untouched.sr-onlylabel is a plain string (the previous attempt in feat: add support for a system theme #3163 rendered a literalSwitch to ${nextTheme} theme), and the trigger carries a matchingaria-label.Scope is one file,
apps/dokploy/components/ui/modeToggle.tsx. Its only consumer isapps/dokploy/components/layouts/user-nav.tsx, which renders it inside the account dropdown.Checklist
Before submitting this PR, please make sure that:
canarybranch.What I actually verified
Tested against a real local instance following the CONTRIBUTING setup —
pnpm run dokploy:setup,pnpm run server:script,pnpm run dokploy:dev, then driven through a real Chromium at 1440x900:menuitemradioitems.htmlgetsclass="… dark"under an OS dark scheme andclass="… light"under light, with zero React hydration warnings in both cases.localStorage.theme === "system"and thedarkclass on<html>tracked an OS scheme change live in both directions (light → dark → light) without a reload.rotate/scaleanimation is intact — the trigger icon flips from sun to moon when the OS scheme changes while System is selected, which is the point of keeping the trigger driven by thedarkclass rather than by a per-mode icon.Also checked statically:
biome checkwith the repo-pinned@biomejs/biome@2.5.7passes on the changed file, andtsc --noEmitinapps/dokploypasses.One note on scope: I did not add an automated component test. The repo's vitest setup is server-side only (
include: ["__test__/**/*.test.ts"], node environment, nojsdom/@testing-libraryinstalled), and standing up a component-test stack is a separate concern from a one-file UI change. Happy to open a follow-up PR for that if you want component tests — say the word.Issues related (if applicable)
closes #5638
Screenshots (if applicable)
System selected, OS in light mode — sun icon, light UI,
Systemchecked:Same selection, OS switched to dark — moon icon, whole UI follows,
Systemstill checked. The point of these two together is that the mode stayed System while the resolved appearance changed, and the trigger animation still ran:Light pinned while the OS is dark — the override still wins:
Dark pinned while the OS is light — the other override direction:
Note on the previous attempt
#3163 tried to reach the same goal by cycling light → dark → system on the existing button. That approach was closed with feedback about over-engineering, the lost CSS animations, and the
sr-onlybug. This PR deliberately takes the dropdown route instead, which keeps the animations and the existing trigger markup intact, and I kept the diff deliberately small.