summaryrefslogtreecommitdiff
path: root/examples/portfolio/src/components/ThemeToggle.astro
blob: 88f7bf67c752220b1be01e9157ab02debe460af6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
---
import Icon from './Icon.astro';
---

<theme-toggle>
	<button>
		<span class="sr-only">Dark theme</span>
		<span class="icon light"><Icon icon="sun" /></span>
		<span class="icon dark"><Icon icon="moon-stars" /></span>
	</button>
</theme-toggle>

<style>
	button {
		display: flex;
		border: 0;
		border-radius: 999rem;
		padding: 0;
		background-color: var(--gray-999);
		box-shadow: inset 0 0 0 1px var(--accent-overlay);
		cursor: pointer;
	}

	.icon {
		z-index: 1;
		position: relative;
		display: flex;
		padding: 0.5rem;
		width: 2rem;
		height: 2rem;
		font-size: 1rem;
		color: var(--accent-overlay);
	}

	.icon.light::before {
		content: '';
		z-index: -1;
		position: absolute;
		inset: 0;
		background-color: var(--accent-regular);
		border-radius: 999rem;
	}

	:global(.theme-dark) .icon.light::before {
		transform: translateX(100%);
	}

	:global(.theme-dark) .icon.dark,
	:global(html:not(.theme-dark)) .icon.light,
	button[aria-pressed='false'] .icon.light {
		color: var(--accent-text-over);
	}

	@media (prefers-reduced-motion: no-preference) {
		.icon,
		.icon.light::before {
			transition:
				transform var(--theme-transition),
				color var(--theme-transition);
		}
	}

	@media (forced-colors: active) {
		.icon.light::before {
			background-color: SelectedItem;
		}
	}
</style>

<script>
	class ThemeToggle extends HTMLElement {
		constructor() {
			super();

			const button = this.querySelector('button')!;

			/** Set the theme to dark/light mode. */
			const setTheme = (dark: boolean) => {
				document.documentElement.classList[dark ? 'add' : 'remove']('theme-dark');
				button.setAttribute('aria-pressed', String(dark));
			};

			// Toggle the theme when a user clicks the button.
			button.addEventListener('click', () => setTheme(!this.isDark()));

			// Initialize button state to reflect current theme.
			setTheme(this.isDark());
		}

		isDark() {
			return document.documentElement.classList.contains('theme-dark');
		}
	}
	customElements.define('theme-toggle', ThemeToggle);
</script>