@@ -3,69 +3,128 @@ import Icon from './Icon.astro';
33---
44
55<theme-toggle>
6- <button >
6+ <button type = " button " aria-pressed = " false " >
77 <span class =" sr-only" >Toggle color theme</span >
8- <span class =" icon light" ><Icon icon =" sun" /></span >
9- <span class =" icon dark" ><Icon icon =" moon-stars" /></span >
8+ <span class =" halo" aria-hidden =" true" ></span >
9+ <span class =" glyph sun" aria-hidden =" true" ><Icon icon =" sun" /></span >
10+ <span class =" glyph moon" aria-hidden =" true" ><Icon icon =" moon-stars" /></span >
1011 </button >
1112</theme-toggle>
1213
1314<style >
1415 button {
15- display: flex;
16- border: 0;
16+ --size: 2.4rem;
17+ position: relative;
18+ display: inline-flex;
19+ align-items: center;
20+ justify-content: center;
21+ width: var(--size);
22+ height: var(--size);
23+ border: 1px solid color-mix(in srgb, var(--gray-800) 45%, transparent);
1724 border-radius: 999rem;
1825 padding: 0;
19- background-color: var(--gray-999);
20- box-shadow: inset 0 0 0 1px var(--accent-overlay);
26+ background:
27+ linear-gradient(145deg, color-mix(in srgb, #fff 10%, transparent), transparent 55%),
28+ linear-gradient(165deg, color-mix(in srgb, var(--glass-regular) 60%, transparent), color-mix(in srgb, var(--glass-thin) 40%, transparent));
29+ box-shadow:
30+ inset 0 1px 0 color-mix(in srgb, #fff 12%, transparent),
31+ var(--glass-shadow-light);
32+ backdrop-filter: blur(18px) saturate(1.2);
33+ -webkit-backdrop-filter: blur(18px) saturate(1.2);
2134 cursor: pointer;
2235 overflow: hidden;
23- transition: box-shadow var(--theme-transition), transform var(--theme-transition);
36+ transition:
37+ box-shadow var(--theme-transition),
38+ transform var(--theme-transition),
39+ border-color var(--theme-transition),
40+ background-color var(--theme-transition);
41+ will-change: transform, box-shadow;
2442 }
2543
26- .icon {
27- z-index: 1;
28- position: relative;
29- display: flex;
30- padding: 0.5rem;
31- width: 2rem;
32- height: 2rem;
33- font-size: 1rem;
34- color: var(--accent-overlay);
44+ .halo {
45+ position: absolute;
46+ inset: -6%;
47+ border-radius: inherit;
48+ background: radial-gradient(circle at 35% 35%, color-mix(in srgb, #fff 14%, transparent), transparent 60%);
49+ mix-blend-mode: soft-light;
50+ opacity: 0.55;
51+ pointer-events: none;
3552 }
3653
37- .icon.light::before {
38- content: '';
39- z-index: -1;
54+ .glyph {
55+ z-index: 1;
56+ display: flex;
57+ align-items: center;
58+ justify-content: center;
59+ width: 1.25rem;
60+ height: 1.25rem;
61+ font-size: 1.05rem;
62+ color: var(--gray-300);
63+ pointer-events: none;
4064 position: absolute;
4165 inset: 0;
42- background-color: var(--accent-regular) ;
43- border-radius: 999rem ;
66+ margin: auto ;
67+ transition: opacity var(--theme-transition), transform var(--theme-transition) ;
4468 }
4569
46- :global(.theme-dark) .icon.light::before {
47- transform: translateX(100%);
70+ .sun {
71+ opacity: 1;
72+ transform: translateY(0);
73+ color: color-mix(in srgb, #f6d27a 80%, var(--gray-200));
4874 }
4975
50- :global(.theme-dark) .icon.dark,
51- :global(html:not(.theme-dark)) .icon.light,
52- button[aria-pressed='false'] .icon.light {
53- color: var(--accent-text-over );
76+ .moon {
77+ opacity: 0;
78+ transform: translateY(6%);
79+ color: color-mix(in srgb, #c6ddff 78%, var(--gray-300) );
5480 }
5581
56- @media (prefers-reduced-motion: no-preference) {
57- .icon,
58- .icon.light::before {
59- transition:
60- transform var(--theme-transition),
61- color var(--theme-transition),
62- box-shadow var(--theme-transition);
82+ :global(.theme-dark) button {
83+ border-color: color-mix(in srgb, var(--glass-stroke) 80%, transparent);
84+ background: linear-gradient(150deg, color-mix(in srgb, var(--glass-regular) 46%, transparent), color-mix(in srgb, var(--glass-thin) 34%, transparent));
85+ }
86+
87+ :global(.theme-dark) .sun {
88+ opacity: 0;
89+ transform: translateY(-6%);
90+ color: color-mix(in srgb, var(--gray-400) 88%, var(--accent-regular));
6391 }
92+
93+ :global(.theme-dark) .moon {
94+ opacity: 1;
95+ transform: translateY(0);
96+ color: color-mix(in srgb, #e6f1ff 90%, var(--gray-100));
97+ }
98+
99+ button:focus-visible {
100+ outline: 2px solid var(--accent-regular);
101+ outline-offset: 3px;
102+ }
103+
104+ button:hover {
105+ box-shadow: var(--glass-shadow-medium);
106+ transform: translateY(-1px);
107+ }
108+
109+ @media (prefers-reduced-motion: no-preference) {
110+ button,
111+ .glyph {
112+ transition:
113+ transform var(--theme-transition),
114+ color var(--theme-transition),
115+ box-shadow var(--theme-transition),
116+ border-color var(--theme-transition),
117+ opacity var(--theme-transition);
118+ }
64119 }
65120
66121 @media (forced-colors: active) {
67- .icon.light::before {
68- background-color: SelectedItem;
122+ button {
123+ border: 1px solid ButtonText;
124+ background: ButtonFace;
125+ }
126+ .glyph {
127+ color: ButtonText;
69128 }
70129 }
71130</style >
@@ -79,7 +138,6 @@ import Icon from './Icon.astro';
79138 if (this.button) return;
80139 this.button = this.querySelector('button')!;
81140
82- // Sync button state with current theme.
83141 const sync = () => {
84142 const isDark = document.documentElement.classList.contains('theme-dark');
85143 this.button?.setAttribute('aria-pressed', String(isDark));
@@ -88,7 +146,6 @@ import Icon from './Icon.astro';
88146
89147 const setTheme = (dark: boolean) => {
90148 document.documentElement.classList[dark ? 'add' : 'remove']('theme-dark');
91- // Persist immediately for next visit.
92149 try {
93150 localStorage.setItem('theme', dark ? 'dark' : 'light');
94151 } catch (_) {}
0 commit comments