Personal site staging.colinozanne.co.uk
portfolio astro

feat: add language switcher

finxol.io f19fc912 3f1cde7d

verified
+78 -28
+4 -5
astro.config.mjs
··· 1 1 // @ts-check 2 2 import { defineConfig } from "astro/config"; 3 3 import deno from "@deno/astro-adapter"; 4 + import preact from "@astrojs/preact"; 4 5 import icon from "astro-icon"; 6 + import { config } from "./src/config.ts"; 5 7 6 8 // https://astro.build/config 7 9 export default defineConfig({ 8 10 site: "https://staging.colinozanne.fr", 9 11 output: "server", 10 12 adapter: deno(), 11 - integrations: [icon()], 13 + integrations: [preact(), icon()], 12 14 i18n: { 13 15 locales: ["en", "fr"], 14 16 defaultLocale: "fr", 15 17 routing: { 16 18 prefixDefaultLocale: false, 17 19 }, 18 - domains: { 19 - fr: "https://staging.colinozanne.fr", 20 - en: "https://staging.colinozanne.co.uk", 21 - }, 20 + domains: config.domains, 22 21 }, 23 22 });
+6
src/config.ts
··· 1 + export const config = { 2 + domains: { 3 + fr: "https://staging.colinozanne.fr", 4 + en: "https://staging.colinozanne.co.uk", 5 + }, 6 + } as const;
+68 -23
src/layouts/Layout.astro
··· 2 2 import "@/assets/styles/main.css"; 3 3 import "@/assets/styles/reset.css"; 4 4 import { Icon } from "astro-icon/components"; 5 + import { config } from "@/config.ts"; 5 6 6 7 import colinPng from "@/assets/img/colin.png"; 7 8 import colinWebp from "@/assets/img/colin.webp"; 9 + 10 + const path = Astro.originPathname.replace(/\/(fr|en)\//, "/"); 11 + const locale = Astro.currentLocale; 8 12 --- 9 13 10 14 <!doctype html> ··· 18 22 </head> 19 23 <body> 20 24 <header> 21 - <picture> 25 + <picture class="container"> 22 26 <source srcset={colinWebp.src} type="image/webp" /> 23 27 <img src={colinPng.src} alt="Colin Ozanne" /> 24 28 </picture> 25 - <h1>Colin <br class="desktop-only" /> Ozanne</h1> 26 - <p>Étudiant M1 Informatique</p> 29 + <h1 class="container">Colin <br class="desktop-only" /> Ozanne</h1> 30 + <p class="container">Étudiant M1 Informatique</p> 27 31 </header> 28 - <main class="content-grid"> 32 + <main class=""> 29 33 <slot /> 30 34 </main> 31 35 <footer> 36 + <div class="container lang-switcher"> 37 + <a 38 + class={locale === "en" ? "active" : ""} 39 + href={`${config.domains.en}${path}`} 40 + target="_self" 41 + > 42 + EN 43 + </a> 44 + <Icon name="pixel:globe" /> 45 + <a 46 + class={locale === "fr" ? "active" : ""} 47 + href={`${config.domains.fr}${path}`} 48 + target="_self" 49 + > 50 + FR 51 + </a> 52 + </div> 53 + 32 54 <a 33 55 href="https://bsky.app/profile/did:plc:hpmpe3pzpdtxbmvhlwrevhju" 34 56 target="_blank" 35 57 rel="noopener noreferrer" 58 + class="container" 36 59 > 37 60 <Icon name="pixel:bluesky" /> 38 61 <span> Bluesky </span> ··· 42 65 href="mailto:contact@colinozanne.fr" 43 66 target="_blank" 44 67 rel="noopener noreferrer" 68 + class="container" 45 69 > 46 70 <Icon name="pixel:envelope" /> 47 71 <span> Email </span> 48 72 <Icon name="pixel:external-link" class="external-link" /> 49 73 </a> 50 - <p>&copy; {new Date().getFullYear()} Colin Ozanne</p> 74 + <p class="container"> 75 + &copy; {new Date().getFullYear()} Colin Ozanne 76 + </p> 51 77 </footer> 52 78 </body> 53 79 </html> ··· 64 90 "header main" 65 91 "footer main"; 66 92 gap: 0; 67 - font-family: sans-serif; 93 + font-family: "Scorekard", sans-serif; 68 94 69 95 header { 70 96 grid-area: header; ··· 86 112 "main" 87 113 "footer"; 88 114 gap: 0; 115 + height: auto; 116 + min-height: 100svh; 89 117 } 90 118 } 91 119 ··· 115 143 font-size: 15cqi; 116 144 color: var(--clr-dark-a0); 117 145 margin: 0; 118 - --aside-elem-color: var(--fuchsia-500); 119 - --aside-elem-text-color: var(--fuchsia-950); 146 + --container-color: var(--fuchsia-500); 147 + --container-text-color: var(--fuchsia-950); 120 148 121 149 @media screen and (max-width: 768px) { 122 150 font-size: 2rem; ··· 125 153 126 154 p { 127 155 margin: 0; 128 - --aside-elem-color: var(--rose-500); 129 - --aside-elem-text-color: var(--rose-950); 156 + --container-color: var(--sky-500); 157 + --container-text-color: var(--sky-950); 158 + } 159 + 160 + .lang-switcher { 161 + display: flex; 162 + align-items: center; 163 + justify-content: center; 164 + gap: 1rem; 165 + 166 + --container-color: var(--fuchsia-100); 167 + --container-text-color: var(--fuchsia-800); 168 + 169 + a { 170 + padding: 0.5rem 1rem; 171 + border-radius: var(--radius); 172 + background-color: var(--fuchsia-200); 173 + color: inherit; 174 + transition: background-color 0.2s ease-in-out; 175 + 176 + &:hover { 177 + background-color: var(--fuchsia-300); 178 + } 179 + 180 + &.active { 181 + background-color: var(--fuchsia-400); 182 + --container-text-color: var(--fuchsia-950); 183 + } 184 + } 130 185 } 131 186 132 187 a { ··· 134 189 align-items: center; 135 190 justify-content: center; 136 191 gap: 0.5rem; 137 - color: var(--black-500); 138 192 text-decoration: none; 139 193 transition: color 0.3s ease-in-out; 140 - --aside-elem-color: var(--amber-500); 141 - --aside-elem-text-color: var(--amber-950); 142 - 143 - &:hover { 144 - color: var(--clr-dark-a1); 145 - } 194 + --container-color: var(--amber-500); 195 + --container-text-color: var(--amber-950); 146 196 147 197 svg { 148 198 --size: 1.5rem; ··· 154 204 } 155 205 156 206 & > * { 157 - padding: var(--spacing); 158 - width: calc(100% - 2rem * var(--spacing)); 159 - border-radius: var(--radius); 160 207 text-align: center; 161 - color: var(--aside-elem-text-color, var(--emerald-950, black)); 162 - background-color: var(--aside-elem-color, var(--emerald-500, red)); 163 208 font-family: "Spagetty", serif; 164 209 letter-spacing: 0.01em; 165 210 font-size: 1.2rem; ··· 200 245 } 201 246 202 247 main { 203 - border-left: 10px solid black; 248 + padding: var(--spacing); 204 249 overflow-y: scroll; 205 250 } 206 251 </style>