From 853bbc77ba911e186610fd66c53fe64ce820d4bb Mon Sep 17 00:00:00 2001 From: Debbie Matthews Date: Mon, 8 Dec 2025 06:56:57 -0800 Subject: [PATCH 1/5] Load site with apps to match the current theme --- components/blocks/cloud.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/components/blocks/cloud.js b/components/blocks/cloud.js index 40ffcb57f..a6dffad8c 100644 --- a/components/blocks/cloud.js +++ b/components/blocks/cloud.js @@ -34,6 +34,16 @@ import classNames from "classnames"; // -> https://foo.streamlit.app/bar/?embed=true&embed_options=show_padding&embed_options=show_colored_line // const Cloud = ({ name, path, query, height, domain, stylePlaceholder }) => { + // Get the current theme from localStorage (same as themeToggle) + const getCurrentTheme = () => { + if (typeof window !== "undefined" && window.localStorage.getItem("theme")) { + return window.localStorage.getItem("theme"); + } + return "light"; // Default fallback + }; + + const currentTheme = getCurrentTheme(); + if (!domain) domain = `${name}.streamlit.app`; if (domain.endsWith("/")) domain = domain.slice(0, -1); @@ -47,6 +57,9 @@ const Cloud = ({ name, path, query, height, domain, stylePlaceholder }) => { let normalQueryStr = ""; let embedQueryStr = ""; + // Add theme parameter to embed options + const themeParam = `embed_options=${currentTheme}_theme`; + // Separate "normal" query params from "embed-related" query params. // This way we can include only the "normal" query params in the Fullscreen link. // Note that this only applies to iframes rendered via the component @@ -68,6 +81,9 @@ const Cloud = ({ name, path, query, height, domain, stylePlaceholder }) => { normalQueryStr = "&" + normalQueryParams.join("&"); } + // Add theme parameter to embed query string + embedQueryStr += `&${themeParam}`; + if (!height) height = "10rem"; const style = stylePlaceholder From 994f6e26a3fbef063084807efd819a9558dd4c46 Mon Sep 17 00:00:00 2001 From: Debbie Matthews Date: Mon, 8 Dec 2025 07:13:50 -0800 Subject: [PATCH 2/5] Get current theme from DOM --- components/blocks/cloud.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/components/blocks/cloud.js b/components/blocks/cloud.js index a6dffad8c..1dea19b52 100644 --- a/components/blocks/cloud.js +++ b/components/blocks/cloud.js @@ -34,12 +34,14 @@ import classNames from "classnames"; // -> https://foo.streamlit.app/bar/?embed=true&embed_options=show_padding&embed_options=show_colored_line // const Cloud = ({ name, path, query, height, domain, stylePlaceholder }) => { - // Get the current theme from localStorage (same as themeToggle) + // Get the current theme from DOM class (fastest and most accurate) const getCurrentTheme = () => { - if (typeof window !== "undefined" && window.localStorage.getItem("theme")) { - return window.localStorage.getItem("theme"); + if (typeof document !== "undefined") { + return document.documentElement.classList.contains("dark") + ? "dark" + : "light"; } - return "light"; // Default fallback + return "light"; // Default fallback for SSR }; const currentTheme = getCurrentTheme(); From acca8a5fee112f1f5473cd06ed9798400e065416 Mon Sep 17 00:00:00 2001 From: Debbie Matthews Date: Mon, 8 Dec 2025 07:15:46 -0800 Subject: [PATCH 3/5] Reload Cloud components on theme change --- components/utilities/themeToggle.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/components/utilities/themeToggle.js b/components/utilities/themeToggle.js index 07d1e2240..df6002da6 100644 --- a/components/utilities/themeToggle.js +++ b/components/utilities/themeToggle.js @@ -22,6 +22,33 @@ const ThemeToggle = () => { document.documentElement.classList.remove(inactiveTheme); setActiveTheme(theme); localStorage.setItem("theme", theme); + + // Force reload all Cloud iframes on the current page + const iframes = document.querySelectorAll('iframe[src*="streamlit.app"]'); + iframes.forEach((iframe) => { + const currentSrc = iframe.src; + const url = new URL(currentSrc); + + // Get all existing embed_options + const existingEmbedOptions = url.searchParams.getAll("embed_options"); + + // Remove only theme-related embed_options (light_theme or dark_theme) + const nonThemeOptions = existingEmbedOptions.filter( + (option) => option !== "light_theme" && option !== "dark_theme", + ); + + // Clear all embed_options and re-add the non-theme ones + url.searchParams.delete("embed_options"); + nonThemeOptions.forEach((option) => + url.searchParams.append("embed_options", option), + ); + + // Add new theme parameter + url.searchParams.append("embed_options", `${theme}_theme`); + + // Force reload iframe with new theme + iframe.src = url.toString(); + }); }; const showTooltip = () => { From 2ed49aea1271cdd4e4c0b779da665fbd4b65d615 Mon Sep 17 00:00:00 2001 From: Debbie Matthews Date: Mon, 8 Dec 2025 16:17:34 -0800 Subject: [PATCH 4/5] Get embed string directly --- components/blocks/cloud.js | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/components/blocks/cloud.js b/components/blocks/cloud.js index 1dea19b52..851691fe0 100644 --- a/components/blocks/cloud.js +++ b/components/blocks/cloud.js @@ -34,17 +34,17 @@ import classNames from "classnames"; // -> https://foo.streamlit.app/bar/?embed=true&embed_options=show_padding&embed_options=show_colored_line // const Cloud = ({ name, path, query, height, domain, stylePlaceholder }) => { - // Get the current theme from DOM class (fastest and most accurate) - const getCurrentTheme = () => { + // Get the current theme embed option directly (with SSR safety) + const getThemeEmbedOption = () => { if (typeof document !== "undefined") { return document.documentElement.classList.contains("dark") - ? "dark" - : "light"; + ? "embed_options=dark_theme" + : "embed_options=light_theme"; } - return "light"; // Default fallback for SSR + return "embed_options=light_theme"; // Default fallback for SSR }; - const currentTheme = getCurrentTheme(); + const themeEmbedOption = getThemeEmbedOption(); if (!domain) domain = `${name}.streamlit.app`; if (domain.endsWith("/")) domain = domain.slice(0, -1); @@ -59,9 +59,6 @@ const Cloud = ({ name, path, query, height, domain, stylePlaceholder }) => { let normalQueryStr = ""; let embedQueryStr = ""; - // Add theme parameter to embed options - const themeParam = `embed_options=${currentTheme}_theme`; - // Separate "normal" query params from "embed-related" query params. // This way we can include only the "normal" query params in the Fullscreen link. // Note that this only applies to iframes rendered via the component @@ -84,7 +81,7 @@ const Cloud = ({ name, path, query, height, domain, stylePlaceholder }) => { } // Add theme parameter to embed query string - embedQueryStr += `&${themeParam}`; + embedQueryStr += `&${themeEmbedOption}`; if (!height) height = "10rem"; From 95fdbe45be195ff176729f58f263804d2dac23c6 Mon Sep 17 00:00:00 2001 From: Debbie Matthews Date: Mon, 8 Dec 2025 16:34:49 -0800 Subject: [PATCH 5/5] Update cloud.js --- components/blocks/cloud.js | 39 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/components/blocks/cloud.js b/components/blocks/cloud.js index 851691fe0..308e64192 100644 --- a/components/blocks/cloud.js +++ b/components/blocks/cloud.js @@ -1,4 +1,4 @@ -import React, { useEffect, useRef } from "react"; +import React, { useEffect, useRef, useState } from "react"; import classNames from "classnames"; // Arguments: @@ -34,17 +34,18 @@ import classNames from "classnames"; // -> https://foo.streamlit.app/bar/?embed=true&embed_options=show_padding&embed_options=show_colored_line // const Cloud = ({ name, path, query, height, domain, stylePlaceholder }) => { - // Get the current theme embed option directly (with SSR safety) - const getThemeEmbedOption = () => { - if (typeof document !== "undefined") { - return document.documentElement.classList.contains("dark") - ? "embed_options=dark_theme" - : "embed_options=light_theme"; - } - return "embed_options=light_theme"; // Default fallback for SSR - }; + // State to track theme, starts with light theme for SSR + const [themeEmbedOption, setThemeEmbedOption] = useState( + "embed_options=light_theme", + ); - const themeEmbedOption = getThemeEmbedOption(); + // Update theme after component mounts (client-side only) + useEffect(() => { + const currentTheme = document.documentElement.classList.contains("dark") + ? "embed_options=dark_theme" + : "embed_options=light_theme"; + setThemeEmbedOption(currentTheme); + }, []); if (!domain) domain = `${name}.streamlit.app`; if (domain.endsWith("/")) domain = domain.slice(0, -1); @@ -56,18 +57,15 @@ const Cloud = ({ name, path, query, height, domain, stylePlaceholder }) => { path = ""; } - let normalQueryStr = ""; - let embedQueryStr = ""; - // Separate "normal" query params from "embed-related" query params. // This way we can include only the "normal" query params in the Fullscreen link. // Note that this only applies to iframes rendered via the component // in React. For iframes rendered via the ".. output::" directive we **always** // include any provided query param in the Fullscreen link. - if (query) { - const embedQueryParams = []; - const normalQueryParams = []; + const embedQueryParams = [themeEmbedOption]; + const normalQueryParams = []; + if (query) { query.split("&").forEach((qStr) => { if (qStr.startsWith("embed=") || qStr.startsWith("embed_options=")) { embedQueryParams.push(qStr); @@ -75,13 +73,10 @@ const Cloud = ({ name, path, query, height, domain, stylePlaceholder }) => { normalQueryParams.push(qStr); } }); - - embedQueryStr = "&" + embedQueryParams.join("&"); - normalQueryStr = "&" + normalQueryParams.join("&"); } - // Add theme parameter to embed query string - embedQueryStr += `&${themeEmbedOption}`; + const embedQueryStr = "&" + embedQueryParams.join("&"); + const normalQueryStr = "&" + normalQueryParams.join("&"); if (!height) height = "10rem";