Skip to content

fix(devtools): persist the selected theme across reloads - #1135

Open
ahmetomerv wants to merge 1 commit into
nuxt:v3from
ahmetomerv:fix/theme-persistence
Open

ahmetomerv wants to merge 1 commit into
nuxt:v3from
ahmetomerv:fix/theme-persistence

Conversation

@ahmetomerv

@ahmetomerv ahmetomerv commented Oct 23, 2026 •

Copy link
Copy Markdown

Fixes #970

The Nuxt DevTools V3 watched the host app's color mode and applied it with immediate: true. On reload, the host app reported its own light/dark mode and overwrote the selection stored in nuxt-devtools-color-mode, so a dark theme reset to light.

This PR adds an explicit override marker:

  • Selecting Light/Dark in DevTools marks the choice as explicit.
  • Host sync only applies while no explicit choice exists.
  • Fresh installs still follow the host app.
  • localStorage access is guarded, so restricted storage contexts do not break the client.

Demo

Screen.Recording.2026-10-23.at.16.26.34.mov

Upgrade behavior

The old storage value cannot distinguish a mode chosen by the user from a mode written by host sync. To avoid resetting an existing preference, any stored legacy light/dark value is preserved as an explicit choice. Matching modes are therefore pinned too; users can reset local settings to resume following the host.

After an explicit choice, DevTools no longer follows host theme changes until local settings are reset.

Shared storage keys

The color-mode storage keys now live in packages/devtools-ui-kit/src/constants.ts. Both NDarkToggle and the client import the same constants, so the keys cannot drift between packages.

Scope

This targets v3 only. On main/v4 the color mode is derived from the embedder or OS and does not read these storage keys.

Verification

  • pnpm build — passed
  • pnpm lint — passed
  • pnpm typecheck — passed
  • pnpm -C packages/devtools test:unit — passed
  • nuxi generate client — passed
  • Manual browser validation against the v3 playground

New unit tests cover fresh installs, explicit overrides, host-following mode, legacy light and dark preferences, and unavailable localStorage.

The v3 client applied the host app's color mode with `immediate: true`,
so a reload overwrote the theme stored in `nuxt-devtools-color-mode`
and reset a selected dark theme to light.

Track explicit choices with a separate override key. Host sync only
applies while no explicit choice exists. Legacy stored light/dark values
are preserved as explicit choices because the old value cannot tell
whether the user or the host sync wrote it. Storage helpers degrade
safely when `localStorage` is unavailable.

Share the color-mode storage keys through the UI kit package instead of
duplicating them between packages.

Fixes nuxt#970
@coderabbitai

coderabbitai Bot commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: abf235cf-2cdd-4f03-b9f0-8839f36bf973
📥 Commits

Reviewing files that changed from the base of the PR and between 4a51fc2 and 85ff319.

📒 Files selected for processing: 6
  • packages/devtools-ui-kit/src/components/NDarkToggle.vue
  • packages/devtools-ui-kit/src/constants.ts
  • packages/devtools/client/app.vue
  • packages/devtools/client/composables/client.ts
  • packages/devtools/client/composables/color-mode.ts
  • packages/devtools/test/color-mode.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.

📝 Walkthrough

The change adds shared storage keys for color-mode preferences and an override. A new function determines whether the DevTools client should apply the host color mode, based on stored preferences and override state. The app watcher uses that function, and the dark toggle records an override when the user changes the mode. Tests cover preference and override cases, including unavailable storage.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Severity of issue fixed: Low

Merge Risk: ⚪ Minimal · up to 85ff3

Fresh installs continue to follow the host theme, while explicit selections remain protected. No merge-blocking issue was established.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 4 files. (2 skipped: 2… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: persisting the selected DevTools theme across reloads.
Description check Passed The description directly explains the theme persistence fix, host synchronization behavior, storage safeguards, tests, and v3 scope.
Linked Issues check Passed Issue #970 requires the selected dark theme to persist after refresh. NDarkToggle.vue records an explicit override, shouldApplyHostColorMode() preserves legacy light/dark values, and app.vue…
Out of Scope Changes check Passed The changes stay within the color-mode persistence fix for v3. The shared constants, guarded storage helpers, client integration, toggle integration, and focused unit tests directly support issue #970…
Full details: Linked Issues check

Explanation

Issue #970 requires the selected dark theme to persist after refresh. NDarkToggle.vue records an explicit override, shouldApplyHostColorMode() preserves legacy light/dark values, and app.vue skips host synchronization when the override is explicit. Fresh installs still follow the host. packages/devtools/test/color-mode.test.ts covers explicit choices, legacy values, fresh installs, host sync, and unavailable storage.

Full details: Out of Scope Changes check

Explanation

The changes stay within the color-mode persistence fix for v3. The shared constants, guarded storage helpers, client integration, toggle integration, and focused unit tests directly support issue #970. No unrelated behavior is demonstrated.

Full details: Docstring Coverage

Explanation

Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 4 files. (2 skipped: 2 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Some tools did not complete. Review the errors below.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

This branch has not been deployed

No deployments
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