// Note that this script can not import anything from outside as it will be converted to a string and executed in the browser. import { SsrScript } from "../components/elements/ssr-layout-effect"; const script = () => { const attributes = ['data-joy-color-scheme', 'data-mui-color-scheme', 'data-theme', 'data-color-scheme', 'class']; const getColorMode = (value: string) => { if (value.includes('dark')) { return 'dark'; } if (value.includes('light')) { return 'light'; } return null; }; const setTheme = (mode: 'dark' | 'light') => { let el = document.getElementById(`--stack-theme-mode`); if (!el) { el = document.createElement("style"); el.id = `--stack-theme-mode`; el.innerHTML = `/* This tag is used by Hexclave to set the theme in the browser without causing a hydration error (since React ignores additional tags in the
). We later use the \`html:has(head > [data-stack-theme=XYZ])\` selector to apply styles based on the theme. */`; document.head.appendChild(el); } el.setAttribute("data-stack-theme", mode); }; const colorToRGB = (color: string): [number, number, number] | null => { // Create a temporary element to use for color conversion const temp = document.createElement('div'); temp.style.color = color; document.body.appendChild(temp); // Get the computed style const computedColor = getComputedStyle(temp).color; document.body.removeChild(temp); // Parse the RGB values const match = computedColor.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); if (match) { return [parseInt(match[1]), parseInt(match[2]), parseInt(match[3])]; } return null; }; const rgbToLuma = (rgb: [number, number, number]) => { return (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000; }; const copyFromColorScheme = () => { const colorScheme = getComputedStyle(document.documentElement).getPropertyValue('color-scheme'); if (colorScheme) { const mode = getColorMode(colorScheme); if (mode) { setTheme(mode); return true; } } return false; }; const copyFromVariables = () => { let backgroundColor = getComputedStyle(document.documentElement).getPropertyValue('--background'); if (backgroundColor) { // shadcn by default uses the hsl values with the format "123 45% 6.5%" if (/^\d+\s\d+%\s\d+(\.\d+)?%$/.test(backgroundColor)) { backgroundColor = `hsl(${backgroundColor})`; } // convert backgroundColor to luma and check if it's dark const rgb = colorToRGB(backgroundColor); if (rgb) { const luma = rgbToLuma(rgb); if (luma < 128) { setTheme('dark'); } else { setTheme('light'); } return true; } } return false; }; const copyFromAttributes = () => { for (const attributeName of attributes) { const colorTheme = document.documentElement.getAttribute(attributeName); if (colorTheme) { const mode = getColorMode(colorTheme); if (mode) { setTheme(mode); return true; } } } return false; }; const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (copyFromColorScheme()) { return; } if (mutation.attributeName && attributes.includes(mutation.attributeName) && copyFromAttributes()) { return; } if (copyFromVariables()) { return; } }); }); observer.observe(document.documentElement, { attributes: true, attributeFilter: attributes, }); // Initial check on page load if (!copyFromColorScheme()) { if (!copyFromAttributes()) { copyFromVariables(); } } }; export function BrowserScript(props : { nonce?: string }) { return