Personal site staging.colinozanne.co.uk
portfolio astro

feat: add random theme button

finxol.io 8bc2dd57 c5772ebf

verified
+161 -9
+161 -9
src/components/customise.astro
··· 80 80 ) as HTMLDialogElement; 81 81 const lightButton = document.getElementById("light-button")!; 82 82 const darkButton = document.getElementById("dark-button")!; 83 + const randomButton = document.getElementById("random-button")!; 83 84 84 85 // Open dialog 85 86 trigger.addEventListener("click", () => { ··· 97 98 98 99 lightButton.addEventListener("click", () => { 99 100 store.theme = "light"; 101 + clearRandomColors(); 100 102 }); 101 103 102 104 darkButton.addEventListener("click", () => { 103 105 store.theme = "dark"; 106 + clearRandomColors(); 104 107 }); 105 108 106 - window.onload = () => { 107 - // to trigger the theme change 108 - store.theme; 109 - }; 110 - 111 109 const colours = [ 112 110 "amber", 113 111 "yellow", ··· 118 116 "indigo", 119 117 "fuchsia", 120 118 "rose", 121 - "gray", 122 - "sand", 123 119 ]; 120 + 121 + function pickRandomColors(): [string, string, string] { 122 + const shuffled = [...colours].sort(() => Math.random() - 0.5); 123 + return [shuffled[0], shuffled[1], shuffled[2]]; 124 + } 125 + 126 + function applyRandomColors( 127 + primary: string, 128 + secondary: string, 129 + accent: string, 130 + ) { 131 + const root = document.documentElement; 132 + const isDark = store.theme === "dark"; 133 + const level = isDark ? "500" : "400"; 134 + const textLevel = "950"; 135 + const mutedLevel = isDark ? "600" : "300"; 136 + const bgLevel = isDark ? "900" : "100"; 137 + 138 + // Primary colors 139 + root.style.setProperty("--primary", `var(--${primary}-${level})`); 140 + root.style.setProperty( 141 + "--primary-text", 142 + `var(--${primary}-${textLevel})`, 143 + ); 144 + root.style.setProperty( 145 + "--primary-muted", 146 + `var(--${primary}-${mutedLevel})`, 147 + ); 148 + 149 + // Secondary colors 150 + root.style.setProperty("--secondary", `var(--${secondary}-${level})`); 151 + root.style.setProperty( 152 + "--secondary-text", 153 + `var(--${secondary}-${textLevel})`, 154 + ); 155 + 156 + // Accent colors 157 + root.style.setProperty("--accent", `var(--${accent}-${level})`); 158 + root.style.setProperty( 159 + "--accent-text", 160 + `var(--${accent}-${textLevel})`, 161 + ); 162 + 163 + // Link colors (use accent) 164 + root.style.setProperty("--link", `var(--${accent}-${level})`); 165 + root.style.setProperty("--link-text", `var(--${accent}-${textLevel})`); 166 + 167 + // Background and foreground 168 + root.style.setProperty("--background", `var(--${primary}-${bgLevel})`); 169 + root.style.setProperty( 170 + "--foreground", 171 + `var(--${primary}-${isDark ? "100" : "950"})`, 172 + ); 173 + 174 + // Name color 175 + root.style.setProperty( 176 + "--name-color", 177 + `var(--${primary}-${isDark ? "800" : "500"})`, 178 + ); 179 + root.style.setProperty( 180 + "--name-color-text", 181 + `var(--${primary}-${isDark ? "100" : "950"})`, 182 + ); 183 + 184 + // Lang switcher 185 + root.style.setProperty( 186 + "--lang-bg", 187 + `var(--${primary}-${isDark ? "900" : "100"})`, 188 + ); 189 + root.style.setProperty( 190 + "--lang", 191 + `var(--${primary}-${isDark ? "800" : "200"})`, 192 + ); 193 + root.style.setProperty( 194 + "--lang-hover", 195 + `var(--${primary}-${isDark ? "700" : "300"})`, 196 + ); 197 + root.style.setProperty( 198 + "--lang-active", 199 + `var(--${primary}-${isDark ? "600" : "400"})`, 200 + ); 201 + root.style.setProperty( 202 + "--lang-active-text", 203 + `var(--${primary}-${textLevel})`, 204 + ); 205 + root.style.setProperty( 206 + "--lang-text", 207 + `var(--${primary}-${isDark ? "200" : "800"})`, 208 + ); 209 + 210 + // Containers 211 + root.style.setProperty( 212 + "--intro-container", 213 + `var(--${secondary}-${level})`, 214 + ); 215 + root.style.setProperty("--blog-container", `var(--${accent}-${level})`); 216 + 217 + // Project colors 218 + root.style.setProperty("--project-1", `var(--${secondary}-${level})`); 219 + root.style.setProperty( 220 + "--project-1-text", 221 + `var(--${secondary}-${textLevel})`, 222 + ); 223 + root.style.setProperty("--project-2", `var(--${primary}-${level})`); 224 + root.style.setProperty( 225 + "--project-2-text", 226 + `var(--${primary}-${textLevel})`, 227 + ); 228 + root.style.setProperty("--project-3", `var(--${accent}-${level})`); 229 + root.style.setProperty( 230 + "--project-3-text", 231 + `var(--${accent}-${textLevel})`, 232 + ); 233 + 234 + // Store the colors 235 + localStorage.setItem( 236 + "randomColors", 237 + JSON.stringify([primary, secondary, accent]), 238 + ); 239 + } 240 + 241 + function clearRandomColors() { 242 + const styles = document.documentElement.style; 243 + // remove all custom properties from html element 244 + for (let i = styles.length - 1; i >= 0; i--) { 245 + const prop = styles[i]; 246 + if (prop.startsWith("--")) { 247 + document.documentElement.style.removeProperty(prop); 248 + } 249 + } 250 + localStorage.removeItem("randomColors"); 251 + } 252 + 253 + randomButton.addEventListener("click", () => { 254 + applyRandomColors(...pickRandomColors()); 255 + }); 256 + 257 + // Restore random colors on load if they exist 258 + window.onload = () => { 259 + // Trigger theme initialization 260 + store.theme; 261 + 262 + // Restore random colors if saved 263 + const saved = localStorage.getItem("randomColors"); 264 + if (saved) { 265 + try { 266 + const [primary, secondary, accent] = JSON.parse(saved); 267 + applyRandomColors(primary, secondary, accent); 268 + } catch { 269 + localStorage.removeItem("randomColors"); 270 + } 271 + } 272 + }; 124 273 </script> 125 274 126 275 <style> ··· 293 442 294 443 &:hover svg { 295 444 animation: wiggle 0.4s ease; 445 + transform: rotate(-5deg); 296 446 } 297 447 298 448 &#random-button { ··· 305 455 } 306 456 307 457 @keyframes wiggle { 308 - 0%, 309 - 100% { 458 + 0% { 310 459 transform: rotate(0deg); 311 460 } 312 461 25% { ··· 314 463 } 315 464 75% { 316 465 transform: rotate(15deg); 466 + } 467 + 100% { 468 + transform: rotate(-5deg); 317 469 } 318 470 } 319 471 </style>