theme switcher
This commit is contained in:
23
src/components/Icon.astro
Normal file
23
src/components/Icon.astro
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
export interface Props {
|
||||||
|
name: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
const { name } = Astro.props;
|
||||||
|
|
||||||
|
const icons = import.meta.glob<{string: string}>('@components/icons/*.svg', { query: '?raw', import: 'default' });
|
||||||
|
const path = `/src/components/icons/${name}.svg`;
|
||||||
|
if (icons[path] === undefined) {
|
||||||
|
throw new Error(`Icon ${name} does not exist.`);
|
||||||
|
}
|
||||||
|
const icon = await icons[path]();
|
||||||
|
---
|
||||||
|
|
||||||
|
<Fragment set:html={icon} />
|
||||||
|
|
||||||
|
<style>
|
||||||
|
svg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
73
src/components/ThemeSwitcher.astro
Normal file
73
src/components/ThemeSwitcher.astro
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
---
|
||||||
|
import Icon from '@components/Icon.astro';
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
<div class="theme-switcher">
|
||||||
|
<button id="switch-to-dark">
|
||||||
|
<Icon name="sun" />
|
||||||
|
</button>
|
||||||
|
<button id="switch-to-light">
|
||||||
|
<Icon name="moon" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.theme-switcher {
|
||||||
|
position: relative;
|
||||||
|
isolation: isolate;
|
||||||
|
width: 1.5rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
|
||||||
|
transform: translateY(0.1rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 0;
|
||||||
|
color: var(--nav-link-color);
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hide by default, i.e. if JS isn't enabled and the data-theme attribute didn't get set, */
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
transition:
|
||||||
|
color 0.2s ease,
|
||||||
|
opacity 0.5s ease,
|
||||||
|
transform 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(html[data-theme="light"]) button#switch-to-dark {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
transform: rotate(360deg);
|
||||||
|
/* whichever one is currently active should be on top */
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
:global(html[data-theme="dark"]) button#switch-to-light {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
transform: rotate(-360deg);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.getElementById('switch-to-dark')?.addEventListener('click', () => {
|
||||||
|
localStorage.setItem('theme-preference', 'dark');
|
||||||
|
document.documentElement.dataset.theme = 'dark';
|
||||||
|
});
|
||||||
|
document.getElementById('switch-to-light')?.addEventListener('click', () => {
|
||||||
|
localStorage.setItem('theme-preference', 'light');
|
||||||
|
document.documentElement.dataset.theme = 'light';
|
||||||
|
})
|
||||||
|
</script>
|
||||||
10
src/components/icons/moon.svg
Normal file
10
src/components/icons/moon.svg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M21.752 15.002A9.72 9.72 0 0 1 18 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 0 0 3 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 0 0 9.002-5.998Z" />
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
svg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
After Width: | Height: | Size: 425 B |
3
src/components/icons/sun.svg
Normal file
3
src/components/icons/sun.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v2.25m6.364.386-1.591 1.591M21 12h-2.25m-.386 6.364-1.591-1.591M12 18.75V21m-4.773-4.227-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0Z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 377 B |
@@ -2,6 +2,7 @@
|
|||||||
import '@styles/main.css';
|
import '@styles/main.css';
|
||||||
import '@fontsource-variable/baskervville';
|
import '@fontsource-variable/baskervville';
|
||||||
import '@fontsource-variable/baskervville-sc';
|
import '@fontsource-variable/baskervville-sc';
|
||||||
|
import ThemeSwitcher from '@components/ThemeSwitcher.astro';
|
||||||
---
|
---
|
||||||
|
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
@@ -11,9 +12,12 @@ import '@fontsource-variable/baskervville-sc';
|
|||||||
|
|
||||||
<!-- avoid FOUC by setting the color schme here in the header -->
|
<!-- avoid FOUC by setting the color schme here in the header -->
|
||||||
<script>
|
<script>
|
||||||
const pref = localStorage.getItem('theme-preference');
|
const explicitPref = localStorage.getItem('theme-preference');
|
||||||
if (pref) {
|
if (explicitPref) {
|
||||||
document.documentElement.dataset.theme = pref;
|
document.documentElement.dataset.theme = explicitPref;
|
||||||
|
} else {
|
||||||
|
const isLight = window.matchMedia('(prefers-color-scheme: light)').matches;
|
||||||
|
document.documentElement.dataset.theme = isLight ? 'light' : 'dark';
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -55,12 +59,19 @@ import '@fontsource-variable/baskervville-sc';
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.switcher-container {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<nav>
|
<nav>
|
||||||
<a href="/" class="home" data-astro-prefetch>Joe's Blog</a>
|
<a href="/" class="home" data-astro-prefetch>Joe's Blog</a>
|
||||||
|
<div class="switcher-container">
|
||||||
|
<ThemeSwitcher />
|
||||||
|
</div>
|
||||||
<a href="/posts" data-astro-prefetch>Posts</a>
|
<a href="/posts" data-astro-prefetch>Posts</a>
|
||||||
<a href="/about" data-astro-prefetch>About</a>
|
<a href="/about" data-astro-prefetch>About</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -51,6 +51,7 @@
|
|||||||
|
|
||||||
&:not([data-theme="light"]) {
|
&:not([data-theme="light"]) {
|
||||||
@media(prefers-color-scheme: dark) {
|
@media(prefers-color-scheme: dark) {
|
||||||
|
color-scheme: dark;
|
||||||
--bg-color: var(--dark-bg-color);
|
--bg-color: var(--dark-bg-color);
|
||||||
--content-color: var(--dark-content-color);
|
--content-color: var(--dark-content-color);
|
||||||
--content-color-faded: var(--dark-content-color-faded);
|
--content-color-faded: var(--dark-content-color-faded);
|
||||||
|
|||||||
Reference in New Issue
Block a user