Personal site staging.colinozanne.co.uk
portfolio astro

feat: add contact section

finxol.io 032eb5dc d2c8741d

verified
+176 -226
+55
src/assets/styles/index.css
··· 11 11 grid-template-areas: 12 12 "content-intro content-intro" 13 13 "content-languages content-blog" 14 + "contact contact" 14 15 "title-projects title-projects" 15 16 "content-projects content-projects"; 16 17 gap: var(--spacing); ··· 102 103 svg { 103 104 width: 1.5rem; 104 105 height: 1.5rem; 106 + } 107 + } 108 + } 109 + 110 + &.contact { 111 + grid-area: contact; 112 + --container-color: var(--fuchsia-300); 113 + 114 + & > aside { 115 + display: flex; 116 + flex-direction: row; 117 + align-items: end; 118 + justify-content: start; 119 + margin-block-end: 1rem; 120 + gap: var(--spacing); 121 + 122 + h2 { 123 + margin: 0; 124 + } 125 + } 126 + 127 + & > div { 128 + display: grid; 129 + grid-template-columns: repeat(4, 1fr); 130 + grid-template-rows: auto; 131 + grid-gap: var(--spacing); 132 + 133 + @media screen and (max-width: 768px) { 134 + grid-template-columns: 1fr; 135 + grid-template-rows: repeat(2, 1fr); 136 + } 137 + 138 + & > * { 139 + display: flex; 140 + flex-direction: column; 141 + align-items: center; 142 + justify-content: start; 143 + text-decoration: none; 144 + margin: 0; 145 + background-color: var(--fuchsia-400); 146 + padding: 1rem 0.5rem; 147 + border-radius: var(--radius); 148 + font-size: var(--size-0); 149 + 150 + & > svg { 151 + width: 2rem; 152 + height: 2rem; 153 + margin-block-end: 0.5rem; 154 + } 155 + 156 + a { 157 + color: inherit; 158 + font-size: var(--size--2); 159 + } 105 160 } 106 161 } 107 162 }
-210
src/components/Welcome.astro
··· 1 - --- 2 - import astroLogo from '../assets/astro.svg'; 3 - import background from '../assets/background.svg'; 4 - --- 5 - 6 - <div id="container"> 7 - <img id="background" src={background.src} alt="" fetchpriority="high" /> 8 - <main> 9 - <section id="hero"> 10 - <a href="https://astro.build" 11 - ><img src={astroLogo.src} width="115" height="48" alt="Astro Homepage" /></a 12 - > 13 - <h1> 14 - To get started, open the <code><pre>src/pages</pre></code> directory in your project. 15 - </h1> 16 - <section id="links"> 17 - <a class="button" href="https://docs.astro.build">Read our docs</a> 18 - <a href="https://astro.build/chat" 19 - >Join our Discord <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 127.14 96.36" 20 - ><path 21 - fill="currentColor" 22 - d="M107.7 8.07A105.15 105.15 0 0 0 81.47 0a72.06 72.06 0 0 0-3.36 6.83 97.68 97.68 0 0 0-29.11 0A72.37 72.37 0 0 0 45.64 0a105.89 105.89 0 0 0-26.25 8.09C2.79 32.65-1.71 56.6.54 80.21a105.73 105.73 0 0 0 32.17 16.15 77.7 77.7 0 0 0 6.89-11.11 68.42 68.42 0 0 1-10.85-5.18c.91-.66 1.8-1.34 2.66-2a75.57 75.57 0 0 0 64.32 0c.87.71 1.76 1.39 2.66 2a68.68 68.68 0 0 1-10.87 5.19 77 77 0 0 0 6.89 11.1 105.25 105.25 0 0 0 32.19-16.14c2.64-27.38-4.51-51.11-18.9-72.15ZM42.45 65.69C36.18 65.69 31 60 31 53s5-12.74 11.43-12.74S54 46 53.89 53s-5.05 12.69-11.44 12.69Zm42.24 0C78.41 65.69 73.25 60 73.25 53s5-12.74 11.44-12.74S96.23 46 96.12 53s-5.04 12.69-11.43 12.69Z" 23 - ></path></svg 24 - > 25 - </a> 26 - </section> 27 - </section> 28 - </main> 29 - 30 - <a href="https://astro.build/blog/astro-5/" id="news" class="box"> 31 - <svg width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg" 32 - ><path 33 - d="M24.667 12c1.333 1.414 2 3.192 2 5.334 0 4.62-4.934 5.7-7.334 12C18.444 28.567 18 27.456 18 26c0-4.642 6.667-7.053 6.667-14Zm-5.334-5.333c1.6 1.65 2.4 3.43 2.4 5.333 0 6.602-8.06 7.59-6.4 17.334C13.111 27.787 12 25.564 12 22.666c0-4.434 7.333-8 7.333-16Zm-6-5.333C15.111 3.555 16 5.556 16 7.333c0 8.333-11.333 10.962-5.333 22-3.488-.774-6-4-6-8 0-8.667 8.666-10 8.666-20Z" 34 - fill="#111827"></path></svg 35 - > 36 - <h2>What's New in Astro 5.0?</h2> 37 - <p> 38 - From content layers to server islands, click to learn more about the new features and 39 - improvements in Astro 5.0 40 - </p> 41 - </a> 42 - </div> 43 - 44 - <style> 45 - #background { 46 - position: fixed; 47 - top: 0; 48 - left: 0; 49 - width: 100%; 50 - height: 100%; 51 - z-index: -1; 52 - filter: blur(100px); 53 - } 54 - 55 - #container { 56 - font-family: Inter, Roboto, 'Helvetica Neue', 'Arial Nova', 'Nimbus Sans', Arial, sans-serif; 57 - height: 100%; 58 - } 59 - 60 - main { 61 - height: 100%; 62 - display: flex; 63 - justify-content: center; 64 - } 65 - 66 - #hero { 67 - display: flex; 68 - align-items: start; 69 - flex-direction: column; 70 - justify-content: center; 71 - padding: 16px; 72 - } 73 - 74 - h1 { 75 - font-size: 22px; 76 - margin-top: 0.25em; 77 - } 78 - 79 - #links { 80 - display: flex; 81 - gap: 16px; 82 - } 83 - 84 - #links a { 85 - display: flex; 86 - align-items: center; 87 - padding: 10px 12px; 88 - color: #111827; 89 - text-decoration: none; 90 - transition: color 0.2s; 91 - } 92 - 93 - #links a:hover { 94 - color: rgb(78, 80, 86); 95 - } 96 - 97 - #links a svg { 98 - height: 1em; 99 - margin-left: 8px; 100 - } 101 - 102 - #links a.button { 103 - color: white; 104 - background: linear-gradient(83.21deg, #3245ff 0%, #bc52ee 100%); 105 - box-shadow: 106 - inset 0 0 0 1px rgba(255, 255, 255, 0.12), 107 - inset 0 -2px 0 rgba(0, 0, 0, 0.24); 108 - border-radius: 10px; 109 - } 110 - 111 - #links a.button:hover { 112 - color: rgb(230, 230, 230); 113 - box-shadow: none; 114 - } 115 - 116 - pre { 117 - font-family: 118 - ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', 119 - monospace; 120 - font-weight: normal; 121 - background: linear-gradient(14deg, #d83333 0%, #f041ff 100%); 122 - -webkit-background-clip: text; 123 - -webkit-text-fill-color: transparent; 124 - background-clip: text; 125 - margin: 0; 126 - } 127 - 128 - h2 { 129 - margin: 0 0 1em; 130 - font-weight: normal; 131 - color: #111827; 132 - font-size: 20px; 133 - } 134 - 135 - p { 136 - color: #4b5563; 137 - font-size: 16px; 138 - line-height: 24px; 139 - letter-spacing: -0.006em; 140 - margin: 0; 141 - } 142 - 143 - code { 144 - display: inline-block; 145 - background: 146 - linear-gradient(66.77deg, #f3cddd 0%, #f5cee7 100%) padding-box, 147 - linear-gradient(155deg, #d83333 0%, #f041ff 18%, #f5cee7 45%) border-box; 148 - border-radius: 8px; 149 - border: 1px solid transparent; 150 - padding: 6px 8px; 151 - } 152 - 153 - .box { 154 - padding: 16px; 155 - background: rgba(255, 255, 255, 1); 156 - border-radius: 16px; 157 - border: 1px solid white; 158 - } 159 - 160 - #news { 161 - position: absolute; 162 - bottom: 16px; 163 - right: 16px; 164 - max-width: 300px; 165 - text-decoration: none; 166 - transition: background 0.2s; 167 - backdrop-filter: blur(50px); 168 - } 169 - 170 - #news:hover { 171 - background: rgba(255, 255, 255, 0.55); 172 - } 173 - 174 - @media screen and (max-height: 368px) { 175 - #news { 176 - display: none; 177 - } 178 - } 179 - 180 - @media screen and (max-width: 768px) { 181 - #container { 182 - display: flex; 183 - flex-direction: column; 184 - } 185 - 186 - #hero { 187 - display: block; 188 - padding-top: 10%; 189 - } 190 - 191 - #links { 192 - flex-wrap: wrap; 193 - } 194 - 195 - #links a.button { 196 - padding: 14px 18px; 197 - } 198 - 199 - #news { 200 - right: 16px; 201 - left: 16px; 202 - bottom: 2.5rem; 203 - max-width: 100%; 204 - } 205 - 206 - h1 { 207 - line-height: 1.5; 208 - } 209 - } 210 - </style>
+89 -4
src/components/pages/index.astro
··· 3 3 import "@/assets/styles/index.css"; 4 4 import { Icon } from "astro-icon/components"; 5 5 import { projects } from "@/data/projects"; 6 - import { locale } from "@/hooks/useLocale.astro"; 6 + import { config } from "@/config"; 7 + import type { Locale } from "@/hooks/useLocale.astro"; 7 8 8 9 interface Props { 9 10 content: { ··· 16 17 blogSection: { 17 18 linkText: string; 18 19 }; 20 + contact: { 21 + title: string; 22 + description: string; 23 + }; 19 24 projectsSection: { 20 25 title: string; 21 26 description: string; ··· 23 28 }; 24 29 } 25 30 31 + const locale = (Astro.currentLocale as Locale) ?? config.defaultLocale; 32 + 26 33 const { 27 - content: { intro, languages, blogSection, projectsSection }, 34 + content: { intro, languages, blogSection, contact, projectsSection }, 28 35 } = Astro.props; 29 36 --- 30 37 ··· 55 62 </a> 56 63 </section> 57 64 65 + <section class="container contact"> 66 + <aside> 67 + <h2>{contact.title}</h2> 68 + <p>{contact.description}</p> 69 + </aside> 70 + <div> 71 + <div> 72 + <Icon name="pixel:bluesky" /> 73 + Bluesky 74 + <a 75 + href="https://bsky.app/profile/finxol.io" 76 + target="_blank" 77 + rel="noopener noreferrer" 78 + class="external-link cover" 79 + > 80 + <span> @finxol.io </span> 81 + <Icon 82 + name="pixel:external-link" 83 + class="external-link" 84 + /> 85 + </a> 86 + </div> 87 + <div> 88 + <Icon name="pixel:github" /> 89 + Github 90 + <a 91 + href="https://github.com/finxol" 92 + target="_blank" 93 + rel="noopener noreferrer" 94 + class="external-link cover" 95 + > 96 + <span> @finxol </span> 97 + <Icon 98 + name="pixel:external-link" 99 + class="external-link" 100 + /> 101 + </a> 102 + </div> 103 + <div> 104 + <Icon name="pixel:envelope" /> 105 + Email 106 + <a 107 + href="mailto:finxol.io@gmail.com" 108 + target="_blank" 109 + rel="noopener noreferrer" 110 + class="external-link cover" 111 + > 112 + <span> 113 + contact@{ 114 + config.domains[locale].replace( 115 + /https?:\/\//, 116 + "", 117 + ) 118 + } 119 + </span> 120 + </a> 121 + </div> 122 + <div> 123 + <Icon name="pixel:linkedin" /> 124 + LinkedIn 125 + <a 126 + href="https://www.linkedin.com/in/finxol/" 127 + target="_blank" 128 + rel="noopener noreferrer" 129 + class="external-link cover" 130 + > 131 + <span> Colin Ozanne </span> 132 + <Icon 133 + name="pixel:external-link" 134 + class="external-link" 135 + /> 136 + </a> 137 + </div> 138 + </div> 139 + </section> 140 + 58 141 <section class="title-projects"> 59 - <aside>💻</aside> 142 + <aside> 143 + <Icon name="pixel:code-block-solid" /> 144 + </aside> 60 145 <h3>{projectsSection.title}</h3> 61 146 <p> 62 147 {projectsSection.description} ··· 85 170 <a href={`/projects/${project.slug}`} class="cover"> 86 171 {project.title[locale]} 87 172 </a> 88 - <p>{project.description[locale]}</p> 173 + <p>{project.subtitle[locale]}</p> 89 174 <ul> 90 175 {project.tags.map((tag: string) => ( 91 176 <li class="tag">{tag}</li>
+23 -12
src/components/pages/project.astro
··· 3 3 import "@/assets/styles/projects/projects.css"; 4 4 import { Icon } from "astro-icon/components"; 5 5 import { projects } from "@/data/projects"; 6 + import type { ProjectMetadata } from "@/data/projects"; 7 + import type { Locale } from "@/hooks/useLocale.astro"; 6 8 9 + import { config } from "@/config"; 7 10 interface Props { 8 11 content: { 9 12 projectId: string; 10 - title: string; 11 - subtitle: string; 12 13 datePrefix: string; 13 14 repoLinkText: string; 14 15 }; 15 16 } 16 17 17 - const locale = Astro.currentLocale; 18 - 19 - const { content } = Astro.props; 18 + const locale = (Astro.currentLocale as Locale) ?? config.defaultLocale; 20 19 21 - const project = projects.find((p) => p.slug === content.projectId); 20 + const project = projects.find((p) => p.slug === Astro.props.content.projectId); 22 21 if (!project) { 23 - throw new Error(`Project not found: ${content.projectId}`); 22 + throw new Error(`Project not found: ${Astro.props.content.projectId}`); 24 23 } 24 + 25 + const content: ProjectMetadata & Props["content"] = { 26 + ...Astro.props.content, 27 + ...project, 28 + }; 25 29 26 30 const homepage = Astro.url; 27 31 homepage.pathname = "/"; ··· 33 37 <Icon name="pixel:arrow-alt-circle-left" /> 34 38 </a> 35 39 36 - <h2>{content.title}</h2> 40 + <h2>{content.title[locale]}</h2> 37 41 38 - <p class="subtitle">{content.subtitle}</p> 42 + <p class="subtitle">{content.subtitle[locale]}</p> 39 43 40 44 <div class="boxes"> 41 45 <picture style="view-transition-name: project-img"> 42 46 <source srcset={project.imgs.webp.src} type="image/webp" /> 43 - <img src={project.imgs.jpg.src} alt={content.title} /> 47 + <img src={project.imgs.jpg.src} alt={content.title[locale]} /> 44 48 </picture> 45 49 46 50 <div class="container"> 47 51 <Icon name="tabler:calendar" /> 48 52 <span> 49 - {content.datePrefix} 50 - {project.startDate.toLocaleDateString(locale)} 53 + { 54 + project.endDate 55 + ? project.startDate.toLocaleDateString(locale) + 56 + " – " + 57 + project.endDate.toLocaleDateString(locale) 58 + : content.datePrefix + 59 + " " + 60 + project.startDate.toLocaleDateString(locale) 61 + } 51 62 </span> 52 63 </div> 53 64
+4
src/pages/en/index.astro
··· 11 11 blogSection: { 12 12 linkText: "Check out my blog for some technical write-ups!", 13 13 }, 14 + contact: { 15 + title: "Contact me", 16 + description: "You can reach me via social media or email.", 17 + }, 14 18 projectsSection: { 15 19 title: "Projects", 16 20 description:
+5
src/pages/fr/index.astro
··· 11 11 blogSection: { 12 12 linkText: "Voir mon blog pour des articles techniques !", 13 13 }, 14 + contact: { 15 + title: "Contactez-moi", 16 + description: 17 + "Vous pouvez me contacter via les réseaux sociaux ou par email.", 18 + }, 14 19 projectsSection: { 15 20 title: "Projets", 16 21 description: