diff --git a/docs/iframe-theme.js b/docs/iframe-theme.js index 35287274c..07e44f82b 100644 --- a/docs/iframe-theme.js +++ b/docs/iframe-theme.js @@ -1,67 +1,65 @@ (function () { + const PROCESSED_ATTR = "data-theme-listener-added"; + function updateIframesForDarkMode() { const isDark = document.documentElement.classList.contains("dark"); - document.querySelectorAll('iframe[src*="chromatic.com/iframe.html"]').forEach((iframe) => { - iframe.style.background = "transparent"; - iframe.allowTransparency = true; + document + .querySelectorAll('iframe[src*="chromatic.com/iframe.html"]') + .forEach((iframe) => { + iframe.style.background = "transparent"; + iframe.allowTransparency = true; - // Use postMessage to communicate with the iframe since we can't access contentDocument due to CORS - const sendThemeMessage = () => { - try { - const message = { - type: 'THEME_CHANGE', - isDark: isDark, - styles: { - background: isDark ? '#0b0d0f' : 'transparent' // transparent for light mode - } - }; - iframe.contentWindow.postMessage(message, '*'); - } catch (e) { - console.warn("Could not send message to iframe:", e); - } - }; + const sendThemeMessage = () => { + try { + const message = { + type: "THEME_CHANGE", + isDark: isDark, + styles: { + background: isDark ? "#0b0d0f" : "transparent", + }, + }; + + const targetOrigin = new URL(iframe.src).origin; + iframe.contentWindow.postMessage(message, targetOrigin); + } catch (e) { + console.warn("Could not send message to iframe:", e); + } + }; - // Send message immediately if iframe might be loaded - sendThemeMessage(); + // Prevent adding multiple event listeners + if (!iframe.hasAttribute(PROCESSED_ATTR)) { + iframe.addEventListener("load", () => { + setTimeout(sendThemeMessage, 100); + }); + iframe.setAttribute(PROCESSED_ATTR, "true"); + } - // Also send message when iframe loads - iframe.addEventListener("load", () => { - // Add a small delay to ensure iframe is ready - setTimeout(sendThemeMessage, 100); + // Initial message send + sendThemeMessage(); }); - }); } - // Listen for theme changes on the parent document const observer = new MutationObserver((mutations) => { - mutations.forEach((mutation) => { - if (mutation.type === 'attributes' && - mutation.attributeName === 'class' && - mutation.target === document.documentElement) { + for (const mutation of mutations) { + if ( + mutation.type === "attributes" && + mutation.attributeName === "class" && + mutation.target === document.documentElement + ) { updateIframesForDarkMode(); } - }); + } }); observer.observe(document.documentElement, { attributes: true, - attributeFilter: ['class'] + attributeFilter: ["class"], }); if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", updateIframesForDarkMode); } else { - setTimeout(updateIframesForDarkMode, 100); - // TODO: add Storybook with Darkmode enabled - let themeChangeCount = 0; - const themeChangeInterval = setInterval(() => { - if (themeChangeCount < 2) { - updateIframesForDarkMode(); - themeChangeCount++; - } else { - clearInterval(themeChangeInterval); - } - }, 1000); + updateIframesForDarkMode(); } })();