Repository navigation
feat: support theme override via query param - #10196
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
There was a problem hiding this comment.
Pull request overview
Adds support for overriding the UI theme via a theme URL query parameter (light/dark/system), allowing per-view theme control without changing the notebook’s saved display.theme.
Changes:
- Add
themetoKnownQueryParamsand document its behavior. - Resolve theme from the URL query param (when present) before falling back to saved config.
- Add Vitest coverage for query-param parsing and precedence over saved config.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| frontend/src/theme/useTheme.ts | Adds query-param parsing and uses it to override the saved theme when not running as an island. |
| frontend/src/theme/tests/useTheme.test.ts | New tests for query-param parsing and override precedence. |
| frontend/src/core/constants.ts | Registers and documents the new theme query parameter. |
Add a `theme` URL query param (light/dark/system) that overrides the notebook's saved display.theme in the app, read, and export paths. Embedded islands are excluded: they infer their theme from the host page.
kirangadhave
force-pushed
the
kg/embed-dark-mode-query-param
branch
from
July 16, 2026 00:04
2967e79 to
4d546f1
Compare
mscolnick
approved these changes
Jul 17, 2026
kirangadhave
added a commit
to marimo-team/jetbrains-marimo
that referenced
this pull request
Jul 28, 2026
## 📝 Summary Closes #54 A user on a dark IDE theme sets marimo's **User settings → Display → Theme** to `light`; after restarting PyCharm it is `dark` again, and they can watch the plugin rewrite a `marimo.toml` on every launch. **Fix.** Drive the theme from marimo's `theme` URL query param (added in marimo 0.23.15, marimo-team/marimo#10196) and stop touching config files entirely. One rule: > The plugin appends `?theme=light|dark` **only when marimo's resolved `display.theme` is `system`**. > An explicit `light`/`dark` is passed through untouched. Inside the IDE, `system` means the IDE. marimo's own three-value dropdown is then sufficient — `light`/`dark` pins the notebook, `system` follows the IDE and now tracks it *live* (previously an IDE theme switch needed a restart to take effect). No new plugin setting: "always override" would just duplicate a setting marimo owns, and "never override" would leave `system` resolving to the OS appearance, which is the bug. **Why read the theme back from the server.** marimo resolves `display.theme` from four layers — user config, `pyproject.toml [tool.marimo]`, the notebook's PEP 723 metadata, env overrides — and already inlines the resolved result into the page it serves as `<marimo-user-config data-config="…">`. Reading that is one GET and stays correct if the layering changes; reimplementing the resolution in Kotlin, or reading `~/.config/marimo/marimo.toml` alone, would miss per-project and per-notebook themes. **Why `system` can't just be left to the browser.** Verified under `runIde` with a throwaway probe: with the IDE in Dark (`JBColor.isBright()=false`), the embedded page reported `prefers-color-scheme: light` (host OS was Light). JCEF follows the **OS** appearance, not the IDE's look and feel — so `system` must be resolved on the Kotlin side, and an IDE theme switch can only reach the page via a reload. ### Behavior changes - A user with an explicit `theme = "light"` and a dark IDE now gets light notebooks; the IDE used to always win. This is the requested fix. - Switching marimo's theme *to* `system` in an open notebook starts IDE-following after that notebook is reopened — the param is decided at load. - marimo's other settings and state are read from and written to the user's normal config again. `<IDE config>/marimo-theme/` is left orphaned rather than migrated: the real config is authoritative again, and copying the stale copy over it could clobber live settings. Called out in the changelog. - Following the IDE theme needs marimo ≥ 0.23.15. On older versions the param is ignored, so the theme comes from the user's own config — no following, but no clobbering either. ### Changes - **Removed** `MarimoThemeConfig` (and its test) and the `withEnvironment` wiring in `SdkLauncher` / `UvLauncher`. The plugin no longer writes any marimo config file. - **Added** `server/MarimoPageConfig` — fetches the served page and extracts `display.theme` from the `data-config` attribute; null on any read/parse failure. - **Added** `editor/MarimoThemedUrl` — the rule above as pure functions, building the URL with `Urls.newFromEncoded(...).addParameters(...)`. - **`MarimoNotebookView`** resolves the theme off the EDT before `loadURL`, and reloads on `LafManagerListener` only when the notebook follows the host *and* the light/dark answer actually changed (the LaF topic also fires for font and colour-scheme edits). Both launch paths (SDK and uv sandbox) get this for free, since it sits above the launcher layer.
mscolnick
added a commit
to marimo-team/marimohub
that referenced
this pull request
Jul 30, 2026
Force the embedded marimo app onto the marimohub light/dark theme via marimo's `?theme=` query param ([marimo-team/marimo#10196](marimo-team/marimo#10196)), so the notebook matches the chrome around it. ## Changes - `NotebookPage.tsx`: append `?theme=light`/`?theme=dark` (from `useTheme()`, already resolved) to the kernel iframe URL via the `URL` API. - The URL is memoized on `sandboxUrl` only — the theme is frozen at load, since marimo reads `?theme=` only on load and re-deriving it live would reload (and reset) the running app on a toggle. **Restart** re-reads the current theme.
williambdean
pushed a commit
to williambdean/marimo
that referenced
this pull request
Sep 6, 2026
## 📝 Summary Add a `theme` URL query param that overrides the notebook's saved `display.theme`. Values are `light`, `dark`, or `system`; anything else is ignored and falls back to the saved config. Until now theme was resolved solely from the notebook's saved display config (serialized into the page at serve/export time), with no way to change it per-view. This lets a deployment or embed force a theme from the URL, e.g. `https://marimo.app/...?embed=true&theme=dark`. It works across the run, read, and static-export paths since they all resolve through the same theme atom. `system` continues to follow `prefers-color-scheme`.
This branch was successfully 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.
📝 Summary
Add a
themeURL query param that overrides the notebook's saveddisplay.theme. Values arelight,dark, orsystem; anything else is ignored and falls back to the saved config.Until now theme was resolved solely from the notebook's saved display config (serialized into the page at serve/export time), with no way to change it per-view. This lets a deployment or embed force a theme from the URL, e.g.
https://marimo.app/...?embed=true&theme=dark. It works across the run, read, and static-export paths since they all resolve through the same theme atom.systemcontinues to followprefers-color-scheme.