[Archived] Archived WIP of vielle.dev

Move light-dark toggle to reusable component

- Colours are set by component user
- last child margins selector is adjusted to ignore scripts/styles (as they can be added from the astro component (WHY NOT JUST HOIST IT OUTSIDE TEMPLATES PLEASE)

vielle.dev 098959cc 83096a7c

verified
+95 -78
+90
src/components/generic/LightDarkToggle.astro
··· 1 + --- 2 + import Sun from "@/assets/sun.svg"; 3 + import Moon from "@/assets/moon.svg"; 4 + 5 + interface Props { 6 + colours: { 7 + bg: string; 8 + fg: string; 9 + }; 10 + } 11 + 12 + const { colours } = Astro.props; 13 + --- 14 + 15 + <button 16 + aria-label="toggle colour scheme" 17 + id="colour-toggle" 18 + style="visibility: hidden" 19 + > 20 + <Sun data-mode-light /> 21 + <Moon data-mode-dark /> 22 + </button> 23 + 24 + <style define:vars={colours}> 25 + #colour-toggle { 26 + background-color: var(--bg); 27 + 28 + border: none; 29 + border-radius: 50%; 30 + padding: 0.5rem; 31 + 32 + width: 3.4rem; 33 + height: 3.4rem; 34 + 35 + & svg { 36 + fill: var(--fg); 37 + stroke: var(--fg); 38 + } 39 + } 40 + </style> 41 + 42 + <script> 43 + const root = document.querySelector(":root"); 44 + if (!(root instanceof HTMLElement)) throw new Error(":root is not html"); 45 + 46 + const button = document.getElementById("colour-toggle"); 47 + if (!button) throw new Error("No #colour-toggle element"); 48 + 49 + const modeToggled = { 50 + light: document.querySelectorAll("[data-mode-light]"), 51 + dark: document.querySelectorAll("[data-mode-dark]"), 52 + }; 53 + 54 + const cookies = document.cookie 55 + .split("; ") 56 + .reduce< 57 + Record<string, string> 58 + >((prev, cur) => ({ ...prev, [cur.split("=")[0]]: cur.split("=")[1] }), {}); 59 + 60 + const mediaMode = matchMedia("(prefers-color-scheme: light)").matches; 61 + const cookieMode = 62 + cookies["colour-mode"] === "light" 63 + ? true 64 + : cookies["colour-mode"] === "dark" 65 + ? false 66 + : undefined; 67 + let lightMode = cookieMode ?? mediaMode; 68 + 69 + const updateColourScheme = () => { 70 + root.style.colorScheme = lightMode ? "light" : "dark"; 71 + document.cookie = `colour-mode=${root.style.colorScheme};path=/`; 72 + 73 + modeToggled[lightMode ? "light" : "dark"].forEach((el) => { 74 + if (!(el instanceof SVGElement || el instanceof HTMLElement)) return; 75 + el.style.display = "block"; 76 + }); 77 + 78 + modeToggled[!lightMode ? "light" : "dark"].forEach((el) => { 79 + if (!(el instanceof SVGElement || el instanceof HTMLElement)) return; 80 + el.style.display = "none"; 81 + }); 82 + }; 83 + 84 + updateColourScheme(); 85 + button.addEventListener("click", () => { 86 + lightMode = !lightMode; 87 + updateColourScheme(); 88 + }); 89 + button.style.visibility = "visible"; 90 + </script>
+5 -78
src/pages/blog/[id].astro
··· 1 1 --- 2 2 import Base from "@/Base.astro"; 3 3 import Nav from "@/components/generic/Nav.astro"; 4 - 5 - import Sun from "@/assets/sun.svg"; 6 - import Moon from "@/assets/moon.svg"; 4 + import LightDarkToggle from "@/components/generic/LightDarkToggle.astro"; 7 5 8 6 import Copy from "@/assets/copy.svg"; 9 7 ··· 44 42 45 43 <h1>{title}</h1> 46 44 47 - <button 48 - aria-label="toggle colour scheme" 49 - id="colour-toggle" 50 - style="visibility: hidden" 51 - > 52 - <Sun data-mode-light /> 53 - <Moon data-mode-dark /> 54 - </button> 45 + <LightDarkToggle 46 + colours={{ fg: "var(--bg-main)", bg: "var(--typo-body)" }} 47 + /> 55 48 </header> 56 49 57 50 <main style={`--accent: ${colour};`}> ··· 60 53 </div> 61 54 </main> 62 55 </Base> 63 - 64 - <script> 65 - const root = document.querySelector(":root"); 66 - if (!(root instanceof HTMLElement)) throw new Error(":root is not html"); 67 - 68 - const button = document.getElementById("colour-toggle"); 69 - if (!button) throw new Error("No #colour-toggle element"); 70 - 71 - const modeToggled = { 72 - light: document.querySelectorAll("[data-mode-light]"), 73 - dark: document.querySelectorAll("[data-mode-dark]"), 74 - }; 75 - 76 - const cookies = document.cookie 77 - .split("; ") 78 - .reduce< 79 - Record<string, string> 80 - >((prev, cur) => ({ ...prev, [cur.split("=")[0]]: cur.split("=")[1] }), {}); 81 - 82 - const mediaMode = matchMedia("(prefers-color-scheme: light)").matches; 83 - const cookieMode = 84 - cookies["colour-mode"] === "light" 85 - ? true 86 - : cookies["colour-mode"] === "dark" 87 - ? false 88 - : undefined; 89 - let lightMode = cookieMode ?? mediaMode; 90 - 91 - const updateColourScheme = () => { 92 - root.style.colorScheme = lightMode ? "light" : "dark"; 93 - document.cookie = `colour-mode=${root.style.colorScheme};path=/`; 94 - 95 - modeToggled[lightMode ? "light" : "dark"].forEach((el) => { 96 - if (!(el instanceof SVGElement || el instanceof HTMLElement)) return; 97 - el.style.display = "block"; 98 - }); 99 - 100 - modeToggled[!lightMode ? "light" : "dark"].forEach((el) => { 101 - if (!(el instanceof SVGElement || el instanceof HTMLElement)) return; 102 - el.style.display = "none"; 103 - }); 104 - }; 105 - 106 - updateColourScheme(); 107 - button.addEventListener("click", () => { 108 - lightMode = !lightMode; 109 - updateColourScheme(); 110 - }); 111 - button.style.visibility = "visible"; 112 - </script> 113 56 114 57 <template id="code-heading"> 115 58 <!-- slots need to be set using set:html ··· 276 219 color: var(--typo-heading); 277 220 padding-bottom: 0.5rem; 278 221 279 - & > :last-child { 222 + & > :global(:not(style, script):last-of-type) { 280 223 margin-right: 1rem; 281 224 margin-top: 0.5rem; 282 - } 283 - } 284 - 285 - #colour-toggle { 286 - background-color: var(--typo-body); 287 - 288 - border: none; 289 - border-radius: 50%; 290 - padding: 0.5rem; 291 - 292 - width: 3.4rem; 293 - height: 3.4rem; 294 - 295 - & svg { 296 - fill: var(--bg-main); 297 - stroke: var(--bg-main); 298 225 } 299 226 } 300 227