Personal site staging.colinozanne.co.uk
portfolio astro

feat: first attempt at theme switcher

(dark mode looks horrendous)

finxol.io f7db328d fd74d934

verified
+275 -32
+25 -10
src/assets/styles/index.css
··· 26 26 gap: 1rem; 27 27 margin-inline: calc(var(--spacing) * 2); 28 28 margin-block: calc(var(--spacing) * 3) calc(var(--spacing) * 2); 29 + color: var(--primary-text); 29 30 30 31 @media screen and (max-width: 768px) { 31 32 flex-direction: column; ··· 62 63 } 63 64 } 64 65 65 - section.container { 66 - &.content-intro { 67 - --container-color: var(--rose-400); 68 - grid-area: content-intro; 66 + section.content-intro { 67 + grid-area: content-intro; 68 + 69 + display: grid; 70 + grid-template-columns: 1fr auto; 71 + grid-template-rows: auto; 72 + column-gap: var(--spacing); 73 + 74 + @media screen and (max-width: 768px) { 75 + grid-template-columns: 1fr; 76 + grid-template-rows: auto auto; 77 + row-gap: var(--spacing); 78 + } 79 + 80 + & > div.container { 81 + --container-color: var(--intro-container); 69 82 } 83 + } 70 84 85 + section.container { 71 86 &.content-languages { 72 - --container-color: var(--sky-400); 87 + --container-color: var(--secondary); 73 88 74 89 grid-area: content-languages; 75 90 display: flex; ··· 83 98 } 84 99 85 100 &.content-blog { 86 - --container-color: var(--yellow-400); 101 + --container-color: var(--blog-container); 87 102 88 103 grid-area: content-blog; 89 104 ··· 109 124 110 125 &.contact { 111 126 grid-area: contact; 112 - --container-color: var(--fuchsia-300); 127 + --container-color: var(--primary-muted); 113 128 114 129 & > aside { 115 130 display: flex; ··· 142 157 justify-content: start; 143 158 text-decoration: none; 144 159 margin: 0; 145 - background-color: var(--fuchsia-400); 160 + background-color: var(--primary); 146 161 padding: 1rem 0.5rem; 147 162 border-radius: var(--radius); 148 163 font-size: var(--size-0); ··· 192 207 193 208 overflow: hidden; 194 209 border-radius: var(--radius); 195 - --container-color: var(--teal-300); 210 + --container-color: var(--accent); 196 211 background-color: var(--container-color); 197 212 198 213 & > a { ··· 244 259 } 245 260 246 261 @layer components { 247 - section.container.layout { 262 + .layout { 248 263 display: grid; 249 264 grid-template-columns: auto auto 1fr; 250 265 grid-template-areas:
+58
src/assets/styles/main.css
··· 46 46 --size-5: clamp(2.7994rem, 2.384rem + 1.8461cqi, 3.8147rem); 47 47 } 48 48 49 + html { 50 + --primary: var(--fuchsia-400); 51 + --primary-text: var(--fuchsia-950); 52 + --primary-muted: var(--fuchsia-300); 53 + 54 + --secondary: var(--sky-400); 55 + --secondary-text: var(--sky-950); 56 + 57 + --accent: var(--teal-400); 58 + --accent-text: var(--teal-950); 59 + 60 + --link: var(--amber-500); 61 + --link-text: var(--amber-950); 62 + 63 + --background: var(--sand-50); 64 + 65 + --name-color: var(--fuchsia-500); 66 + --name-color-text: var(--fuchsia-950); 67 + --lang-bg: var(--fuchsia-100); 68 + --lang: var(--fuchsia-200); 69 + --lang-hover: var(--fuchsia-300); 70 + --lang-active: var(--fuchsia-400); 71 + --lang-active-text: var(--fuchsia-950); 72 + --lang-text: var(--fuchsia-800); 73 + 74 + --intro-container: var(--rose-400); 75 + --blog-container: var(--yellow-400); 76 + 77 + &[data-theme="dark"] { 78 + --primary: var(--fuchsia-600); 79 + --primary-text: var(--fuchsia-100); 80 + --primary-muted: var(--fuchsia-700); 81 + 82 + --secondary: var(--sky-600); 83 + --secondary-text: var(--sky-100); 84 + 85 + --accent: var(--teal-600); 86 + --accent-text: var(--teal-100); 87 + 88 + --link: var(--amber-400); 89 + --link-text: var(--amber-100); 90 + 91 + --background: var(--sand-950); 92 + 93 + --name-color: var(--fuchsia-400); 94 + --name-color-text: var(--fuchsia-100); 95 + --lang-bg: var(--fuchsia-900); 96 + --lang: var(--fuchsia-800); 97 + --lang-hover: var(--fuchsia-700); 98 + --lang-active: var(--fuchsia-600); 99 + --lang-active-text: var(--fuchsia-100); 100 + --lang-text: var(--fuchsia-200); 101 + 102 + --intro-container: var(--rose-600); 103 + --blog-container: var(--yellow-600); 104 + } 105 + } 106 + 49 107 @layer components { 50 108 a svg { 51 109 --size: 1.5rem;
+1 -1
src/assets/styles/projects/projects.css
··· 59 59 } 60 60 61 61 & > div { 62 - --container-color: var(--teal-300); 62 + --container-color: var(--accent); 63 63 display: grid; 64 64 grid-template-areas: 65 65 "img box1"
+168
src/components/customise.astro
··· 1 + --- 2 + import { Icon } from "astro-icon/components"; 3 + --- 4 + 5 + <button 6 + type="button" 7 + class="customise-trigger container" 8 + popovertarget="customisation-popover" 9 + > 10 + <Icon name="pixel:themes" /> 11 + Customise <wbr />the page 12 + </button> 13 + <aside id="customisation-popover" popover="auto"> 14 + <div> 15 + <h2>Customise</h2> 16 + <p> 17 + Change the theme, layout, and other settings to personalise your 18 + experience. 19 + </p> 20 + 21 + <section> 22 + <button id="light-button">Light</button> 23 + <button id="dark-button">Dark</button> 24 + </section> 25 + </div> 26 + </aside> 27 + 28 + <script> 29 + const lightButton = document.getElementById("light-button")!; 30 + const darkButton = document.getElementById("dark-button")!; 31 + 32 + lightButton.addEventListener("click", () => { 33 + document.documentElement.dataset.theme = "light"; 34 + localStorage.setItem("theme", "light"); 35 + }); 36 + 37 + darkButton.addEventListener("click", () => { 38 + document.documentElement.dataset.theme = "dark"; 39 + localStorage.setItem("theme", "dark"); 40 + }); 41 + 42 + window.onload = () => { 43 + const theme = localStorage.getItem("theme"); 44 + if (theme) { 45 + document.documentElement.dataset.theme = theme; 46 + } else { 47 + document.documentElement.dataset.theme = "light"; 48 + localStorage.setItem("theme", "light"); 49 + } 50 + }; 51 + </script> 52 + 53 + <style> 54 + button.customise-trigger { 55 + --container-color: var(--fuchsia-500); 56 + 57 + display: flex; 58 + flex-direction: column; 59 + align-items: center; 60 + justify-content: center; 61 + transition: background-color 0.3s ease; 62 + border: 0; 63 + cursor: pointer; 64 + 65 + &:hover { 66 + animation: rainbow 2s infinite; 67 + } 68 + 69 + @keyframes rainbow { 70 + 0% { 71 + background-color: var(--red-600); 72 + } 73 + 100% { 74 + background-color: var(--fuchsia-600); 75 + } 76 + } 77 + 78 + & > svg { 79 + margin-block-end: 0.5rem; 80 + } 81 + } 82 + 83 + aside#customisation-popover { 84 + position: fixed; 85 + top: var(--spacing); 86 + right: var(--spacing); 87 + bottom: var(--spacing); 88 + left: auto; 89 + max-width: 30rem; 90 + margin: 0; 91 + 92 + opacity: 1; 93 + background-color: var(--background); 94 + color: var(--primary-text); 95 + border: 1px solid var(--primary-muted); 96 + border-radius: 0.5rem; 97 + box-shadow: 0 0 10px oklch(from var(--fuchsia-900) l c h / 0.1); 98 + 99 + transform: translateX(0); 100 + transform-origin: right center; 101 + transition: 102 + opacity 0.3s ease, 103 + transform 0.3s ease; 104 + transition-behavior: allow-discrete; 105 + 106 + @starting-style { 107 + opacity: 0; 108 + transform: translateX(100%); 109 + } 110 + 111 + &::backdrop { 112 + background: transparent; 113 + } 114 + 115 + div { 116 + display: flex; 117 + flex-direction: column; 118 + align-items: start; 119 + justify-content: start; 120 + gap: calc(var(--spacing) * 0.5); 121 + padding: var(--spacing); 122 + 123 + h2 { 124 + margin-block: 0; 125 + font-size: var(--size-2); 126 + font-weight: bold; 127 + } 128 + 129 + p { 130 + margin: 0; 131 + font-size: var(--size--1); 132 + font-weight: normal; 133 + text-wrap: balance; 134 + } 135 + 136 + section { 137 + display: grid; 138 + grid-template-columns: 1fr 1fr; 139 + gap: var(--spacing); 140 + 141 + button { 142 + background-color: var(--background); 143 + border: 1px solid var(--primary-muted); 144 + border-radius: 0.5rem; 145 + padding: calc(var(--spacing) * 0.3); 146 + font-size: var(--size--1); 147 + font-weight: bold; 148 + cursor: pointer; 149 + color: inherit; 150 + transition: background-color 0.3s ease; 151 + 152 + &:hover { 153 + background-color: var(--primary-muted); 154 + } 155 + 156 + :where(html:not([data-theme]), html[data-theme="light"]) 157 + &#light-button { 158 + background-color: var(--primary); 159 + } 160 + 161 + :where(html[data-theme="dark"]) &#dark-button { 162 + background-color: var(--primary); 163 + } 164 + } 165 + } 166 + } 167 + } 168 + </style>
+10 -6
src/components/pages/index.astro
··· 5 5 import { projects } from "@/data/projects"; 6 6 import { config } from "@/config"; 7 7 import type { Locale } from "@/hooks/useLocale.astro"; 8 + import Customise from "../customise.astro"; 8 9 9 10 interface Props { 10 11 content: { ··· 37 38 38 39 <Layout> 39 40 <article> 40 - <section class="container layout content-intro"> 41 - <aside>👋</aside> 42 - <h3>{intro.title}</h3> 43 - <p>{intro.description}</p> 44 - <div> 45 - {intro.ps.map((p) => <p>{p}</p>)} 41 + <section class="content-intro"> 42 + <div class="container layout"> 43 + <aside>👋</aside> 44 + <h3>{intro.title}</h3> 45 + <p>{intro.description}</p> 46 + <div> 47 + {intro.ps.map((p) => <p>{p}</p>)} 48 + </div> 46 49 </div> 50 + <Customise /> 47 51 </section> 48 52 49 53 <section class="container content-languages">
+13 -15
src/layouts/Layout.astro
··· 165 165 body { 166 166 font-family: "Scorekard", sans-serif; 167 167 container: body / inline-size; 168 - background-color: var(--sand-50); 168 + background-color: var(--background); 169 169 } 170 170 171 171 .app { ··· 243 243 font-family: "EasyCoast", serif; 244 244 letter-spacing: initial; 245 245 font-size: 15cqi; 246 - color: var(--clr-dark-a0); 247 246 margin: 0; 248 - --container-color: var(--fuchsia-500); 249 - --container-text-color: var(--fuchsia-950); 247 + --container-color: var(--name-color); 248 + --container-text-color: var(--name-color-text); 250 249 251 250 @media screen and (max-width: 768px) { 252 251 font-size: 2rem; ··· 255 254 256 255 p { 257 256 margin: 0; 258 - --container-color: var(--sky-400); 259 - --container-text-color: var(--sky-950); 257 + --container-color: var(--secondary); 258 + --container-text-color: var(--secondary-text); 260 259 } 261 260 262 261 .lang-switcher { ··· 265 264 justify-content: center; 266 265 gap: 1rem; 267 266 268 - --container-color: var(--fuchsia-100); 269 - --container-text-color: var(--fuchsia-800); 267 + --container-color: var(--lang-bg); 268 + --container-text-color: var(--lang-text); 270 269 271 270 a { 272 271 padding: 0.5rem 1rem; 273 272 border-radius: var(--radius); 274 - background-color: var(--fuchsia-200); 273 + background-color: var(--lang); 275 274 color: inherit; 276 275 transition: background-color 0.2s ease-in-out; 277 276 278 277 &:hover { 279 - background-color: var(--fuchsia-300); 278 + background-color: var(--lang-hover); 280 279 } 281 280 282 281 &.active { 283 - background-color: var(--fuchsia-400); 284 - --container-text-color: var(--fuchsia-950); 282 + background-color: var(--lang-active); 283 + --container-text-color: var(--lang-active-text); 285 284 } 286 285 } 287 286 } ··· 292 291 justify-content: center; 293 292 gap: 0.5rem; 294 293 text-decoration: none; 295 - transition: color 0.3s ease-in-out; 296 - --container-color: var(--amber-500); 297 - --container-text-color: var(--amber-950); 294 + --container-color: var(--link); 295 + --container-text-color: var(--link-text); 298 296 } 299 297 300 298 & > * {