Skip to content

Instantly share code, notes, and snippets.

@trepidacious
Created September 7, 2023 16:43
Show Gist options
  • Save trepidacious/4456a5cc7004449fe82b5170942306ea to your computer and use it in GitHub Desktop.
Save trepidacious/4456a5cc7004449fe82b5170942306ea to your computer and use it in GitHub Desktop.
Very rough theme select in Astro with view transitions, using TailwindCSS and daisyUI
---
interface Props {
lightColor: string;
darkColor: string;
}
const { lightColor, darkColor } = Astro.props;
---
<!-- Store the theme colors for scripts to use -->
<meta
name="set-theme-color-data"
data-light-color={lightColor}
data-dark-color={darkColor}
/>
<!--
Start with theme color set by preferred color scheme
This will work when user is on "Default" theme
-->
<meta
name="theme-color"
media="(prefers-color-scheme: light)"
content={lightColor}
/>
<meta
name="theme-color"
media="(prefers-color-scheme: dark)"
content={darkColor}
/>
<script is:inline>
// This script is inline to apply styles as early as possible
// to avoid a flash of unthemed content
// Loads theme from local storage and applies to document
const setTheme = () => {
const theme = localStorage.getItem("theme");
if (theme) {
document.documentElement.setAttribute("data-theme", theme);
} else {
document.documentElement.removeAttribute("data-theme");
}
const getThemeColorData = () => {
const setThemeColorData = document.querySelector(
'meta[name="set-theme-color-data"]'
);
if (setThemeColorData instanceof HTMLElement) {
const lightColor = setThemeColorData.dataset["lightColor"];
const darkColor = setThemeColorData.dataset["darkColor"];
if (lightColor && darkColor) {
return { lightColor, darkColor };
}
}
return { lightColor: "#EEEEEE", darkColor: "#333333" };
};
const { lightColor, darkColor } = getThemeColorData();
const setThemeColors = (l, d) => {
document
.querySelector(
'meta[name="theme-color"][media="(prefers-color-scheme: light)"]'
)
?.setAttribute("content", l);
document
.querySelector(
'meta[name="theme-color"][media="(prefers-color-scheme: dark)"]'
)
?.setAttribute("content", d);
};
const setThemeColor = (theme) => {
if (theme === "dark") {
setThemeColors(darkColor, darkColor);
} else if (theme === "light") {
setThemeColors(lightColor, lightColor);
} else {
setThemeColors(lightColor, darkColor);
}
};
setThemeColor(theme);
};
// Runs on initial navigation
setTheme();
// Runs on view transitions navigation
document.addEventListener("astro:after-swap", setTheme);
</script>
<script>
// This script isn't inline, so it runs after the select
// elements for choosing a theme are present
const getThemeColorData = () => {
const setThemeColorData = document.querySelector(
'meta[name="set-theme-color-data"]'
);
if (setThemeColorData instanceof HTMLElement) {
const lightColor = setThemeColorData.dataset["lightColor"];
const darkColor = setThemeColorData.dataset["darkColor"];
if (lightColor && darkColor) {
return { lightColor, darkColor };
}
}
return { lightColor: "#EEEEEE", darkColor: "#333333" };
};
const { lightColor, darkColor } = getThemeColorData();
const setThemeColors = (l: string, d: string) => {
document
.querySelector(
'meta[name="theme-color"][media="(prefers-color-scheme: light)"]'
)
?.setAttribute("content", l);
document
.querySelector(
'meta[name="theme-color"][media="(prefers-color-scheme: dark)"]'
)
?.setAttribute("content", d);
};
const setThemeColor = (theme: string | null) => {
if (theme === "dark") {
setThemeColors(darkColor, darkColor);
} else if (theme === "light") {
setThemeColors(lightColor, lightColor);
} else {
setThemeColors(lightColor, darkColor);
}
};
// Sets up theme color meta, and adds event listeners to any ThemeSelect controls
const setThemeSelects = () => {
const theme = localStorage.getItem("theme");
// setThemeColor(theme);
[...document.querySelectorAll("select[data-choose-theme]")].forEach(
(el) => {
if (el instanceof HTMLSelectElement) {
if (theme) {
el.value = theme;
}
el.addEventListener("change", function () {
document.documentElement.setAttribute("data-theme", el.value);
setThemeColor(el.value);
localStorage.setItem("theme", el.value);
});
}
}
);
};
// Runs on initial navigation
setThemeSelects();
// Runs on view transitions navigation
document.addEventListener("astro:after-swap", setThemeSelects);
</script>
<!-- Note that for this to work, you need to use SetTheme component in the head of the page
Currently uses font-bold to avoid safari rendering issue,
see https://github.com/saadeghi/daisyui/issues/2311 -->
<select
class="select select-ghost select-sm w-full max-w-xs font-bold"
data-choose-theme
>
<option value="">Default theme</option>
<option value="dark">Dark theme</option>
<option value="light">Light theme</option>
</select>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment