Refactor CV into separate role-specific components and misc frontend tweaks
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 4m6s

Split CV.vue into CVGeneral, CVBackend, CVFrontend, and CVHospitality variants.
Also adds halftone body class, reformats index.html, and minor style/layout fixes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-27 14:03:00 +00:00
parent 7f01b1a296
commit 31d4b4c268
15 changed files with 1546 additions and 390 deletions

View File

@@ -1,17 +1,33 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Adam French's personal website" /> <meta name="description" content="Adam French's personal website">
<title>AF</title> <title>AF</title>
<link rel="preconnect" href="https://i.scdn.co" crossorigin /> <link rel="preconnect" href="https://i.scdn.co" crossorigin>
<link rel="preconnect" href="https://cdn.akamai.steamstatic.com" crossorigin /> <link
<link rel="icon" type="/img/x-icon" href="/img/favicon.ico" /> rel="preconnect"
<link rel="preload" href="/fonts/big_noodle_titling.woff2" as="font" type="font/woff2" crossorigin /> href="https://cdn.akamai.steamstatic.com"
<link rel="preload" href="/fonts/CreatoDisplay-Bold.woff2" as="font" type="font/woff2" crossorigin /> crossorigin
>
<link rel="icon" type="/img/x-icon" href="/img/favicon.ico">
<link
rel="preload"
href="/fonts/big_noodle_titling.woff2"
as="font"
type="font/woff2"
crossorigin
>
<link
rel="preload"
href="/fonts/CreatoDisplay-Bold.woff2"
as="font"
type="font/woff2"
crossorigin
>
</head> </head>
<body id="app"> <body id="app" class="halftone">
<script type="module" src="/src/main.js"></script> <script type="module" src="/src/main.js"></script>
</body> </body>
</html> </html>

View File

@@ -14,7 +14,7 @@ import Footer from "@/components/Footer.vue";
</Transition> </Transition>
</RouterView> </RouterView>
</main> </main>
<Footer class="sticky bottom-0 z-50" /> <Footer class="no-print sticky bottom-0 z-50" />
</div> </div>
</template> </template>

View File

@@ -66,7 +66,8 @@
/* BACKGROUND COLORS */ /* BACKGROUND COLORS */
--bg_primary: #1b110e; --bg_primary: #1b110e;
--bg_secondary: #000; --bg_secondary: #04080f;
--bg_tertiary: #0c1c10;
--link: #222; --link: #222;
--bdr: 2px; --bdr: 2px;
@@ -102,7 +103,6 @@ body {
margin: 0 auto; margin: 0 auto;
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
background-color: var(--bg_secondary);
} }
input { input {
@@ -299,17 +299,23 @@ td {
} }
.halftone { .halftone {
--dot_size: 1px; --dot_size: 4px;
--bg_size: 3px; --bg_size: 12px;
--bg_pos: calc(var(--bg_size) / 2); --bg_pos: calc(var(--bg_size) / 2);
--blur: 0%; --blur: 0%;
background-color: var(--bg_secondary); background-color: var(--bg_secondary);
background-image: radial-gradient( background-image:
linear-gradient(to top, transparent 0%, var(--bg_primary) 100%),
radial-gradient(
circle at center, circle at center,
var(--bg_primary) var(--dot_size), var(--bg_tertiary) var(--dot_size),
transparent var(--blur) transparent var(--blur)
); );
background-size: var(--bg_size) var(--bg_size); background-size:
background-position: 0 0; 100% 100%,
var(--bg_size) var(--bg_size);
background-position:
0 0,
0 0;
} }

View File

@@ -15,10 +15,6 @@ const parentPath = computed(() => {
} }
}); });
const inHome = computed(() => {
return route.path == "/" || route.path == "/stp";
});
const faces = [ const faces = [
"^_^", "^_^",
"¯\\_(ツ)_/¯", "¯\\_(ツ)_/¯",
@@ -46,10 +42,7 @@ const faces_string = faces.join(" ");
<template> <template>
<nav class="flex flex-row w-full h-fit border border-primary bg-bg_primary"> <nav class="flex flex-row w-full h-fit border border-primary bg-bg_primary">
<RouterLink class="bdr-2 bg-bg_primary" to="/" v-if="!inHome"> <RouterLink class="bdr-2 bg-bg_primary" v-if="parentPath" :to="parentPath">
<span>HOME</span>
</RouterLink>
<RouterLink class="bdr-2 bg-bg_primary" v-if="parentPath" :to="parentPath">
<span>UP</span> <span>UP</span>
</RouterLink> </RouterLink>
<Headline class="border flex-1 max-w-full"> <Headline class="border flex-1 max-w-full">

View File

@@ -238,7 +238,7 @@ const links = [
</script> </script>
<template> <template>
<main class="items-center flex flex-col halftone"> <main class="items-center flex flex-col">
<div <div
class="a4page-portrait bdr-1 flex flex-row flex-wrap overflow-x-auto gap-1" class="a4page-portrait bdr-1 flex flex-row flex-wrap overflow-x-auto gap-1"
> >

View File

@@ -1,349 +1,76 @@
<script setup> <script setup>
import Project from "./Project.vue"; import { ref, shallowRef, defineAsyncComponent } from "vue";
import CVGeneral from "./CVGeneral.vue";
import CVBackend from "./CVBackend.vue";
import CVFrontend from "./CVFrontend.vue";
const CVHospitality = defineAsyncComponent(() =>
import("./CVHospitality.vue"),
);
const templates = [
{ label: "General", component: CVGeneral },
{ label: "Backend", component: CVBackend },
{ label: "Frontend", component: CVFrontend },
{ label: "Hospitality", component: CVHospitality },
];
const selected = ref(0);
const currentComponent = shallowRef(templates[0].component);
function select(index) {
selected.value = index;
currentComponent.value = templates[index].component;
}
</script> </script>
<template> <template>
<main>
<div class="no-print w-full h-20"></div>
<div class="a4page">
<div class="flex flex-row justify-between">
<h1 class="name">Adam French</h1>
<div class="contact-details text-right">
<p>+447563266931</p>
<p>adam.a.french@outlook.com</p>
<h4>
<a href="https://www.adam-french.co.uk">
www.adam-french.co.uk
</a>
</h4>
</div>
</div>
<h2>Profile</h2>
<p>
First Class Honours graduate in Computer Science with
Mathematics from the University of Leeds (81.1%), with a year
abroad at the University of Waterloo. Proficient in full-stack
development, systems programming, and CI/CD automation. Eager to
contribute to a collaborative engineering team, apply strong
academic foundations to real-world problems, and grow through
hands-on experience.
</p>
<h2>Skills</h2>
<div class="skills-grid">
<div> <div>
<strong>Languages</strong><br /><small <div class="no-print cv-selector">
>Go, Rust, Python, JavaScript / TypeScript, SQL</small <button
v-for="(t, i) in templates"
:key="t.label"
:class="['cv-btn', { active: selected === i }]"
@click="select(i)"
> >
{{ t.label }}
</button>
</div> </div>
<div> <component :is="currentComponent" />
<strong>Frontend</strong><br /><small
>Vue, React / Redux, Svelte, Tailwind CSS,
WebAssembly</small
>
</div> </div>
<div>
<strong>Backend / Infra</strong><br /><small
>Nginx, Docker, PostgreSQL, SQLite, JWT Auth, Git
Actions</small
>
</div>
</div>
<h2>Projects</h2>
<Project class="border-b border-dotted">
<template v-slot:left>
<h4>
<a
href="https://www.adam-french.co.uk/gitea/adamf/web_server.git"
>
web_server.git
</a>
</h4>
</template>
<template v-slot:top>
<small>
Nginx, Vue, Postgres, Docker, Go, Python, Rust Wasm,
Git Actions, JWT Auth
</small>
<small>2025</small>
</template>
<p>
Self-hosted personal website with a fully automated CI/CD
pipeline. Iterated across diverse tech stacks including
Svelte, React/Redux, SQLite, Rust Actix, and Deno.
</p>
</Project>
<Project class="border-b border-dotted">
<template v-slot:left>
<h4>
<a
href="https://www.adam-french.co.uk/gitea/adamf/tour.git"
>
tour.git
</a>
</h4>
</template>
<template v-slot:top>
<small>Rust</small>
<small>2026</small>
</template>
<p>
CLI tool for building and navigating interactive code
tutorials, with version-traversal semantics inspired by Git.
</p>
</Project>
<Project class="border-b border-dotted">
<template v-slot:left>
<h4>
<a
href="https://www.adam-french.co.uk/gitea/adamf/rust-raytracer.git"
>
rust-raytracer.git
</a>
</h4>
</template>
<template v-slot:top>
<small>Rust, Linear Algebra, Multithreading</small>
<small>2023</small>
</template>
<p>
Parallelised recursive ray tracer for realistic 3D
rendering. Emphasised algorithmic efficiency and low-level
memory management in Rust.
</p>
</Project>
<Project>
<template #left>
<h4>
<a
class="text-center w-full"
href="https://community.wolfram.com/groups/-/m/t/3210947"
>
Wolfram Summer School
</a>
</h4>
</template>
<template #top>
<small>Wolfram Mathematica</small>
<small>2024</small>
</template>
<p>
Research project on Mobile Automata with data visualisation
and academic presentation. Delivered within a tight deadline
in collaboration with academic mentors.
</p>
</Project>
<h2>Education</h2>
<div class="w-full h-fit flex-row flex gap-5">
<div class="flex-1 border-r border-dotted pr-3">
<h3>
<a
href="https://www.adam-french.co.uk/pdf/transcript.pdf"
>
University of Leeds
</a>
</h3>
<div
class="flex-row flex place-content-between m-auto place-items-center"
>
<small>81.1% First Class Honours</small>
<small>20212025</small>
</div>
<small
>BSc Computer Science with Mathematics
(International)</small
>
<ul>
<li>Algorithms & Data Structures I & II</li>
<li>Compiler Design and Construction</li>
<li>Formal Languages & Finite Automata</li>
<li>Graph Algorithms & Complexity Theory</li>
<li>
Machine Learning · Databases · Computer Processors
</li>
</ul>
</div>
<div class="flex-1 pl-3">
<h3>University of Waterloo</h3>
<div
class="flex-row flex place-content-between m-auto place-items-center"
>
<small>Year abroad</small>
<small>20232024</small>
</div>
<ul>
<li>Applied Cryptography</li>
<li>Introduction to Computer Graphics</li>
<li>
Introduction to Rings and Fields with Applications
</li>
</ul>
</div>
</div>
</div>
<div class="no-print w-full h-20"></div>
<div class="a4page">
<div class="flex-1 pl-3">
<h2>Experience</h2>
<Project>
<template #left>
<p>Hospitality</p>
</template>
<template #top>
<small>Cashier, Bartender, Waiter</small>
<small>20182023</small>
</template>
<p>
Worked at <em>Belgrave Music Hall</em>,
<em>The Crown and Anchor</em>, and
<em>BFI Riverfront Kitchen</em>. Developed
communication, composure under pressure, and reliability
in customer-facing roles.
</p>
</Project>
<h2>Interests</h2>
<ul>
<li>Leetcode daily competitive problem solving</li>
<li>Learning Mandarin</li>
<li>Rhythm Games</li>
<li>Climbing · Gym</li>
<li>Board games · Meetup.com</li>
</ul>
</div>
</div>
<div class="no-print w-full h-20"></div>
</main>
</template> </template>
<style scoped> <style scoped>
/* Fonts */ .cv-selector {
@font-face { position: sticky;
font-family: "big_noodle_titling"; top: 0;
src: url("/fonts/big_noodle_titling.woff2") format("woff2"); z-index: 40;
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "CreatoDisplay";
src: url("/fonts/CreatoDisplay-Bold.woff2") format("woff2");
font-weight: normal;
font-style: normal;
font-display: swap;
}
/* Variables */
* {
--primary: black;
--secondary: #0000ff;
--tertiary: #ff0000;
--quaternary: #cccccc;
--background: white;
--font-heading: big_noodle_titling;
--font-text: CreatoDisplay;
--font-size-name: 2.5em;
--font-size-text: 100%;
--font-size-small: 0.9em;
--font-size-heading: 2.1em;
--font-size-subheading: 1.7em;
--font-size-subsubheading: 1.4em;
}
/* A4 Page */
.a4page {
line-height: 1.6;
font-family: var(--font-text);
width: 210mm;
height: 297mm;
padding: 5mm;
box-sizing: border-box;
background-color: var(--background);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
border: 1px solid var(--primary);
overflow: hidden;
margin: auto auto;
}
/* Component Styling */
main {
padding: 0px;
display: flex; display: flex;
flex-direction: column; justify-content: center;
height: fit-content; gap: 0.5rem;
padding: 0.5rem;
background: white;
border-bottom: 1px solid #ddd;
} }
span { .cv-btn {
height: 2em; padding: 0.4rem 1rem;
border: 1px solid #333;
border-radius: 4px;
background: white;
color: #333;
cursor: pointer;
font-size: 0.9rem;
transition: background 0.15s, color 0.15s;
} }
h1, .cv-btn:hover {
h2, background: #eee;
h3,
h4 {
border: none;
color: var(--primary);
font-family: var(--font-heading);
text-transform: capitalize;
} }
h1 { .cv-btn.active {
font-size: var(--font-size-heading); background: #333;
} color: white;
h2 {
border-bottom: 1px solid var(--primary);
font-size: var(--font-size-subheading);
}
h3 {
font-size: var(--font-size-subsubheading);
}
a:hover {
color: var(--tertiary);
}
a {
background-color: transparent;
color: var(--secondary);
}
p {
margin-bottom: 0.2em;
color: var(--primary);
font-size: var(--font-size-text);
}
table {
color: var(--secondary);
border-collapse: collapse;
border: 1px solid black;
}
td {
color: var(--secondary);
border-top: 1px solid var(--tertiary);
padding: 1px 10px 1px 10px;
font-size: var(--font-size-text);
text-align: left;
}
th {
color: var(--secondary);
border: 2px solid var(--tertiary);
padding: 1px 0px 1px 7px;
font-family: var(--font-heading);
font-size: var(--font-size-subsubheading);
background-color: var(--quaternary);
text-align: left;
} }
@media print { @media print {
@@ -351,27 +78,4 @@ th {
display: none !important; display: none !important;
} }
} }
small {
font-size: var(--font-size-small);
color: var(--primary);
}
ul {
font-size: var(--font-size-small);
margin: 0;
padding-left: 1.2em;
}
li {
font-size: var(--font-size-small);
color: var(--primary);
}
.skills-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 0.3em 1em;
margin-bottom: 0.2em;
}
</style> </style>

View File

@@ -0,0 +1,379 @@
<script setup>
import Project from "./Project.vue";
</script>
<template>
<main>
<div class="no-print w-full h-20"></div>
<div class="a4page">
<div class="flex flex-row justify-between">
<h1 class="name">Adam French</h1>
<div class="contact-details text-right">
<p>+447563266931</p>
<p>adam.a.french@outlook.com</p>
<h4>
<a href="https://www.adam-french.co.uk">
www.adam-french.co.uk
</a>
</h4>
</div>
</div>
<h2>Profile</h2>
<p>
First Class Honours graduate in Computer Science with
Mathematics from the University of Leeds (81.1%), with a year
abroad at the University of Waterloo. Strong background in
systems programming, API design, database management, and
infrastructure automation. Keen to build reliable, performant
backend services in a collaborative engineering team.
</p>
<h2>Skills</h2>
<div class="skills-grid">
<div>
<strong>Languages</strong><br /><small
>Go, Rust, Python, SQL, JavaScript / TypeScript</small
>
</div>
<div>
<strong>Backend</strong><br /><small
>REST, GraphQL, gRPC, JWT Auth, WebSockets,
Middleware</small
>
</div>
<div>
<strong>Infrastructure</strong><br /><small
>Docker, Nginx, PostgreSQL, SQLite, Git Actions,
Linux</small
>
</div>
</div>
<h2>Projects</h2>
<Project class="border-b border-dotted">
<template v-slot:left>
<h4>
<a
href="https://www.adam-french.co.uk/gitea/adamf/web_server.git"
>
web_server.git
</a>
</h4>
</template>
<template v-slot:top>
<small>
Go, Gin, GraphQL, PostgreSQL, GORM, Docker, Nginx, JWT
Auth, Git Actions
</small>
<small>2025</small>
</template>
<p>
Self-hosted personal website with a Go backend serving a
GraphQL API, JWT authentication, Spotify OAuth integration,
and WebSocket messaging. Fully containerised with Docker
Compose and automated CI/CD via Git Actions.
</p>
</Project>
<Project class="border-b border-dotted">
<template v-slot:left>
<h4>
<a
href="https://www.adam-french.co.uk/gitea/adamf/tour.git"
>
tour.git
</a>
</h4>
</template>
<template v-slot:top>
<small>Rust, CLI, File I/O</small>
<small>2026</small>
</template>
<p>
CLI tool for building and navigating interactive code
tutorials, with version-traversal semantics inspired by Git.
Designed for robustness with comprehensive error handling and
structured file operations.
</p>
</Project>
<Project class="border-b border-dotted">
<template v-slot:left>
<h4>
<a
href="https://www.adam-french.co.uk/gitea/adamf/rust-raytracer.git"
>
rust-raytracer.git
</a>
</h4>
</template>
<template v-slot:top>
<small>Rust, Multithreading, Performance</small>
<small>2023</small>
</template>
<p>
Parallelised recursive ray tracer leveraging Rust's
ownership model for safe concurrency. Focused on algorithmic
efficiency, low-level memory management, and multi-core
utilisation.
</p>
</Project>
<Project>
<template #left>
<h4>
<a
class="text-center w-full"
href="https://community.wolfram.com/groups/-/m/t/3210947"
>
Wolfram Summer School
</a>
</h4>
</template>
<template #top>
<small>Wolfram Mathematica</small>
<small>2024</small>
</template>
<p>
Research project on Mobile Automata with data visualisation
and academic presentation. Delivered within a tight deadline
in collaboration with academic mentors.
</p>
</Project>
<h2>Education</h2>
<div class="w-full h-fit flex-row flex gap-5">
<div class="flex-1 border-r border-dotted pr-3">
<h3>
<a
href="https://www.adam-french.co.uk/pdf/transcript.pdf"
>
University of Leeds
</a>
</h3>
<div
class="flex-row flex place-content-between m-auto place-items-center"
>
<small>81.1% First Class Honours</small>
<small>20212025</small>
</div>
<small
>BSc Computer Science with Mathematics
(International)</small
>
<ul>
<li>Algorithms & Data Structures I & II</li>
<li>Compiler Design and Construction</li>
<li>Graph Algorithms & Complexity Theory</li>
<li>Formal Languages & Finite Automata</li>
<li>Databases · Computer Processors</li>
</ul>
</div>
<div class="flex-1 pl-3">
<h3>University of Waterloo</h3>
<div
class="flex-row flex place-content-between m-auto place-items-center"
>
<small>Year abroad</small>
<small>20232024</small>
</div>
<ul>
<li>Applied Cryptography</li>
<li>Introduction to Computer Graphics</li>
<li>
Introduction to Rings and Fields with Applications
</li>
</ul>
</div>
</div>
</div>
<div class="no-print w-full h-20"></div>
<div class="a4page">
<div class="flex-1 pl-3">
<h2>Experience</h2>
<Project>
<template #left>
<p>Hospitality</p>
</template>
<template #top>
<small>Cashier, Bartender, Waiter</small>
<small>20182023</small>
</template>
<p>
Worked at <em>Belgrave Music Hall</em>,
<em>The Crown and Anchor</em>, and
<em>BFI Riverfront Kitchen</em>. Developed
communication, composure under pressure, and reliability
in customer-facing roles.
</p>
</Project>
<h2>Interests</h2>
<ul>
<li>Leetcode daily competitive problem solving</li>
<li>Learning Mandarin</li>
<li>Rhythm Games</li>
<li>Climbing · Gym</li>
<li>Board games · Meetup.com</li>
</ul>
</div>
</div>
<div class="no-print w-full h-20"></div>
</main>
</template>
<style scoped>
/* Fonts */
@font-face {
font-family: "big_noodle_titling";
src: url("/fonts/big_noodle_titling.woff2") format("woff2");
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "CreatoDisplay";
src: url("/fonts/CreatoDisplay-Bold.woff2") format("woff2");
font-weight: normal;
font-style: normal;
font-display: swap;
}
/* Variables */
* {
--primary: black;
--secondary: #0000ff;
--tertiary: #ff0000;
--quaternary: #cccccc;
--background: white;
--font-heading: big_noodle_titling;
--font-text: CreatoDisplay;
--font-size-name: 2.5em;
--font-size-text: 100%;
--font-size-small: 0.9em;
--font-size-heading: 2.1em;
--font-size-subheading: 1.7em;
--font-size-subsubheading: 1.4em;
}
/* A4 Page */
.a4page {
line-height: 1.6;
font-family: var(--font-text);
width: 210mm;
height: 297mm;
padding: 5mm;
box-sizing: border-box;
background-color: var(--background);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
border: 1px solid var(--primary);
overflow: hidden;
margin: auto auto;
}
/* Component Styling */
main {
padding: 0px;
display: flex;
flex-direction: column;
height: fit-content;
background-color: white;
}
span {
height: 2em;
}
h1,
h2,
h3,
h4 {
border: none;
color: var(--primary);
font-family: var(--font-heading);
text-transform: capitalize;
}
h1 {
font-size: var(--font-size-heading);
}
h2 {
border-bottom: 1px solid var(--primary);
font-size: var(--font-size-subheading);
}
h3 {
font-size: var(--font-size-subsubheading);
}
a:hover {
color: var(--tertiary);
}
a {
background-color: transparent;
color: var(--secondary);
}
p {
margin-bottom: 0.2em;
color: var(--primary);
font-size: var(--font-size-text);
}
table {
color: var(--secondary);
border-collapse: collapse;
border: 1px solid black;
}
td {
color: var(--secondary);
border-top: 1px solid var(--tertiary);
padding: 1px 10px 1px 10px;
font-size: var(--font-size-text);
text-align: left;
}
th {
color: var(--secondary);
border: 2px solid var(--tertiary);
padding: 1px 0px 1px 7px;
font-family: var(--font-heading);
font-size: var(--font-size-subsubheading);
background-color: var(--quaternary);
text-align: left;
}
@media print {
.no-print {
display: none !important;
}
}
small {
font-size: var(--font-size-small);
color: var(--primary);
}
ul {
font-size: var(--font-size-small);
margin: 0;
padding-left: 1.2em;
}
li {
font-size: var(--font-size-small);
color: var(--primary);
}
.skills-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 0.3em 1em;
margin-bottom: 0.2em;
}
</style>

View File

@@ -0,0 +1,379 @@
<script setup>
import Project from "./Project.vue";
</script>
<template>
<main>
<div class="no-print w-full h-20"></div>
<div class="a4page">
<div class="flex flex-row justify-between">
<h1 class="name">Adam French</h1>
<div class="contact-details text-right">
<p>+447563266931</p>
<p>adam.a.french@outlook.com</p>
<h4>
<a href="https://www.adam-french.co.uk">
www.adam-french.co.uk
</a>
</h4>
</div>
</div>
<h2>Profile</h2>
<p>
First Class Honours graduate in Computer Science with
Mathematics from the University of Leeds (81.1%), with a year
abroad at the University of Waterloo. Passionate about crafting
responsive, accessible, and performant user interfaces.
Experienced across multiple frontend frameworks with a solid
understanding of the full stack.
</p>
<h2>Skills</h2>
<div class="skills-grid">
<div>
<strong>Frontend</strong><br /><small
>Vue, React / Redux, Svelte, Tailwind CSS, HTML / CSS,
WebAssembly</small
>
</div>
<div>
<strong>Languages</strong><br /><small
>JavaScript / TypeScript, Rust, Go, Python, SQL</small
>
</div>
<div>
<strong>Tooling / Infra</strong><br /><small
>Vite, Docker, Nginx, Git Actions, PostgreSQL,
Figma</small
>
</div>
</div>
<h2>Projects</h2>
<Project class="border-b border-dotted">
<template v-slot:left>
<h4>
<a
href="https://www.adam-french.co.uk/gitea/adamf/web_server.git"
>
web_server.git
</a>
</h4>
</template>
<template v-slot:top>
<small>
Vue 3, Tailwind CSS, Vite, Pinia, Responsive Design,
Rust Wasm
</small>
<small>2025</small>
</template>
<p>
Personal website SPA built with Vue 3, Tailwind CSS, and
Pinia for state management. Features responsive layouts,
dark mode, WebAssembly integration, and a custom component
library. Iterated through Svelte and React/Redux before
settling on Vue.
</p>
</Project>
<Project class="border-b border-dotted">
<template v-slot:left>
<h4>
<a
href="https://www.adam-french.co.uk/gitea/adamf/tour.git"
>
tour.git
</a>
</h4>
</template>
<template v-slot:top>
<small>Rust</small>
<small>2026</small>
</template>
<p>
CLI tool for building and navigating interactive code
tutorials, with version-traversal semantics inspired by Git.
</p>
</Project>
<Project class="border-b border-dotted">
<template v-slot:left>
<h4>
<a
href="https://www.adam-french.co.uk/gitea/adamf/rust-raytracer.git"
>
rust-raytracer.git
</a>
</h4>
</template>
<template v-slot:top>
<small>Rust, Linear Algebra, Multithreading</small>
<small>2023</small>
</template>
<p>
Parallelised recursive ray tracer for realistic 3D
rendering. Emphasised algorithmic efficiency and low-level
memory management in Rust.
</p>
</Project>
<Project>
<template #left>
<h4>
<a
class="text-center w-full"
href="https://community.wolfram.com/groups/-/m/t/3210947"
>
Wolfram Summer School
</a>
</h4>
</template>
<template #top>
<small>Wolfram Mathematica</small>
<small>2024</small>
</template>
<p>
Research project on Mobile Automata with data visualisation
and academic presentation. Delivered within a tight deadline
in collaboration with academic mentors.
</p>
</Project>
<h2>Education</h2>
<div class="w-full h-fit flex-row flex gap-5">
<div class="flex-1 border-r border-dotted pr-3">
<h3>
<a
href="https://www.adam-french.co.uk/pdf/transcript.pdf"
>
University of Leeds
</a>
</h3>
<div
class="flex-row flex place-content-between m-auto place-items-center"
>
<small>81.1% First Class Honours</small>
<small>20212025</small>
</div>
<small
>BSc Computer Science with Mathematics
(International)</small
>
<ul>
<li>Algorithms & Data Structures I & II</li>
<li>Compiler Design and Construction</li>
<li>Formal Languages & Finite Automata</li>
<li>Graph Algorithms & Complexity Theory</li>
<li>
Machine Learning · Databases · Computer Processors
</li>
</ul>
</div>
<div class="flex-1 pl-3">
<h3>University of Waterloo</h3>
<div
class="flex-row flex place-content-between m-auto place-items-center"
>
<small>Year abroad</small>
<small>20232024</small>
</div>
<ul>
<li>Applied Cryptography</li>
<li>Introduction to Computer Graphics</li>
<li>
Introduction to Rings and Fields with Applications
</li>
</ul>
</div>
</div>
</div>
<div class="no-print w-full h-20"></div>
<div class="a4page">
<div class="flex-1 pl-3">
<h2>Experience</h2>
<Project>
<template #left>
<p>Hospitality</p>
</template>
<template #top>
<small>Cashier, Bartender, Waiter</small>
<small>20182023</small>
</template>
<p>
Worked at <em>Belgrave Music Hall</em>,
<em>The Crown and Anchor</em>, and
<em>BFI Riverfront Kitchen</em>. Developed
communication, composure under pressure, and reliability
in customer-facing roles.
</p>
</Project>
<h2>Interests</h2>
<ul>
<li>Leetcode daily competitive problem solving</li>
<li>Learning Mandarin</li>
<li>Rhythm Games</li>
<li>Climbing · Gym</li>
<li>Board games · Meetup.com</li>
</ul>
</div>
</div>
<div class="no-print w-full h-20"></div>
</main>
</template>
<style scoped>
/* Fonts */
@font-face {
font-family: "big_noodle_titling";
src: url("/fonts/big_noodle_titling.woff2") format("woff2");
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "CreatoDisplay";
src: url("/fonts/CreatoDisplay-Bold.woff2") format("woff2");
font-weight: normal;
font-style: normal;
font-display: swap;
}
/* Variables */
* {
--primary: black;
--secondary: #0000ff;
--tertiary: #ff0000;
--quaternary: #cccccc;
--background: white;
--font-heading: big_noodle_titling;
--font-text: CreatoDisplay;
--font-size-name: 2.5em;
--font-size-text: 100%;
--font-size-small: 0.9em;
--font-size-heading: 2.1em;
--font-size-subheading: 1.7em;
--font-size-subsubheading: 1.4em;
}
/* A4 Page */
.a4page {
line-height: 1.6;
font-family: var(--font-text);
width: 210mm;
height: 297mm;
padding: 5mm;
box-sizing: border-box;
background-color: var(--background);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
border: 1px solid var(--primary);
overflow: hidden;
margin: auto auto;
}
/* Component Styling */
main {
padding: 0px;
display: flex;
flex-direction: column;
height: fit-content;
background-color: white;
}
span {
height: 2em;
}
h1,
h2,
h3,
h4 {
border: none;
color: var(--primary);
font-family: var(--font-heading);
text-transform: capitalize;
}
h1 {
font-size: var(--font-size-heading);
}
h2 {
border-bottom: 1px solid var(--primary);
font-size: var(--font-size-subheading);
}
h3 {
font-size: var(--font-size-subsubheading);
}
a:hover {
color: var(--tertiary);
}
a {
background-color: transparent;
color: var(--secondary);
}
p {
margin-bottom: 0.2em;
color: var(--primary);
font-size: var(--font-size-text);
}
table {
color: var(--secondary);
border-collapse: collapse;
border: 1px solid black;
}
td {
color: var(--secondary);
border-top: 1px solid var(--tertiary);
padding: 1px 10px 1px 10px;
font-size: var(--font-size-text);
text-align: left;
}
th {
color: var(--secondary);
border: 2px solid var(--tertiary);
padding: 1px 0px 1px 7px;
font-family: var(--font-heading);
font-size: var(--font-size-subsubheading);
background-color: var(--quaternary);
text-align: left;
}
@media print {
.no-print {
display: none !important;
}
}
small {
font-size: var(--font-size-small);
color: var(--primary);
}
ul {
font-size: var(--font-size-small);
margin: 0;
padding-left: 1.2em;
}
li {
font-size: var(--font-size-small);
color: var(--primary);
}
.skills-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 0.3em 1em;
margin-bottom: 0.2em;
}
</style>

View File

@@ -0,0 +1,378 @@
<script setup>
import Project from "./Project.vue";
</script>
<template>
<main>
<div class="no-print w-full h-20"></div>
<div class="a4page">
<div class="flex flex-row justify-between">
<h1 class="name">Adam French</h1>
<div class="contact-details text-right">
<p>+447563266931</p>
<p>adam.a.french@outlook.com</p>
<h4>
<a href="https://www.adam-french.co.uk">
www.adam-french.co.uk
</a>
</h4>
</div>
</div>
<h2>Profile</h2>
<p>
First Class Honours graduate in Computer Science with
Mathematics from the University of Leeds (81.1%), with a year
abroad at the University of Waterloo. Proficient in full-stack
development, systems programming, and CI/CD automation. Eager to
contribute to a collaborative engineering team, apply strong
academic foundations to real-world problems, and grow through
hands-on experience.
</p>
<h2>Skills</h2>
<div class="skills-grid">
<div>
<strong>Languages</strong><br /><small
>Go, Rust, Python, JavaScript / TypeScript, SQL</small
>
</div>
<div>
<strong>Frontend</strong><br /><small
>Vue, React / Redux, Svelte, Tailwind CSS,
WebAssembly</small
>
</div>
<div>
<strong>Backend / Infra</strong><br /><small
>Nginx, Docker, PostgreSQL, SQLite, JWT Auth, Git
Actions</small
>
</div>
</div>
<h2>Projects</h2>
<Project class="border-b border-dotted">
<template v-slot:left>
<h4>
<a
href="https://www.adam-french.co.uk/gitea/adamf/web_server.git"
>
web_server.git
</a>
</h4>
</template>
<template v-slot:top>
<small>
Nginx, Vue, Postgres, Docker, Go, Python, Rust Wasm,
Git Actions, JWT Auth
</small>
<small>2025</small>
</template>
<p>
Self-hosted personal website with a fully automated CI/CD
pipeline. Iterated across diverse tech stacks including
Svelte, React/Redux, SQLite, Rust Actix, and Deno.
</p>
</Project>
<Project class="border-b border-dotted">
<template v-slot:left>
<h4>
<a
href="https://www.adam-french.co.uk/gitea/adamf/tour.git"
>
tour.git
</a>
</h4>
</template>
<template v-slot:top>
<small>Rust</small>
<small>2026</small>
</template>
<p>
CLI tool for building and navigating interactive code
tutorials, with version-traversal semantics inspired by Git.
</p>
</Project>
<Project class="border-b border-dotted">
<template v-slot:left>
<h4>
<a
href="https://www.adam-french.co.uk/gitea/adamf/rust-raytracer.git"
>
rust-raytracer.git
</a>
</h4>
</template>
<template v-slot:top>
<small>Rust, Linear Algebra, Multithreading</small>
<small>2023</small>
</template>
<p>
Parallelised recursive ray tracer for realistic 3D
rendering. Emphasised algorithmic efficiency and low-level
memory management in Rust.
</p>
</Project>
<Project>
<template #left>
<h4>
<a
class="text-center w-full"
href="https://community.wolfram.com/groups/-/m/t/3210947"
>
Wolfram Summer School
</a>
</h4>
</template>
<template #top>
<small>Wolfram Mathematica</small>
<small>2024</small>
</template>
<p>
Research project on Mobile Automata with data visualisation
and academic presentation. Delivered within a tight deadline
in collaboration with academic mentors.
</p>
</Project>
<h2>Education</h2>
<div class="w-full h-fit flex-row flex gap-5">
<div class="flex-1 border-r border-dotted pr-3">
<h3>
<a
href="https://www.adam-french.co.uk/pdf/transcript.pdf"
>
University of Leeds
</a>
</h3>
<div
class="flex-row flex place-content-between m-auto place-items-center"
>
<small>81.1% First Class Honours</small>
<small>20212025</small>
</div>
<small
>BSc Computer Science with Mathematics
(International)</small
>
<ul>
<li>Algorithms & Data Structures I & II</li>
<li>Compiler Design and Construction</li>
<li>Formal Languages & Finite Automata</li>
<li>Graph Algorithms & Complexity Theory</li>
<li>
Machine Learning · Databases · Computer Processors
</li>
</ul>
</div>
<div class="flex-1 pl-3">
<h3>University of Waterloo</h3>
<div
class="flex-row flex place-content-between m-auto place-items-center"
>
<small>Year abroad</small>
<small>20232024</small>
</div>
<ul>
<li>Applied Cryptography</li>
<li>Introduction to Computer Graphics</li>
<li>
Introduction to Rings and Fields with Applications
</li>
</ul>
</div>
</div>
</div>
<div class="no-print w-full h-20"></div>
<div class="a4page">
<div class="flex-1 pl-3">
<h2>Experience</h2>
<Project>
<template #left>
<p>Hospitality</p>
</template>
<template #top>
<small>Cashier, Bartender, Waiter</small>
<small>20182023</small>
</template>
<p>
Worked at <em>Belgrave Music Hall</em>,
<em>The Crown and Anchor</em>, and
<em>BFI Riverfront Kitchen</em>. Developed
communication, composure under pressure, and reliability
in customer-facing roles.
</p>
</Project>
<h2>Interests</h2>
<ul>
<li>Leetcode daily competitive problem solving</li>
<li>Learning Mandarin</li>
<li>Rhythm Games</li>
<li>Climbing · Gym</li>
<li>Board games · Meetup.com</li>
</ul>
</div>
</div>
<div class="no-print w-full h-20"></div>
</main>
</template>
<style scoped>
/* Fonts */
@font-face {
font-family: "big_noodle_titling";
src: url("/fonts/big_noodle_titling.woff2") format("woff2");
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "CreatoDisplay";
src: url("/fonts/CreatoDisplay-Bold.woff2") format("woff2");
font-weight: normal;
font-style: normal;
font-display: swap;
}
/* Variables */
* {
--primary: black;
--secondary: #0000ff;
--tertiary: #ff0000;
--quaternary: #cccccc;
--background: white;
--font-heading: big_noodle_titling;
--font-text: CreatoDisplay;
--font-size-name: 2.5em;
--font-size-text: 100%;
--font-size-small: 0.9em;
--font-size-heading: 2.1em;
--font-size-subheading: 1.7em;
--font-size-subsubheading: 1.4em;
}
/* A4 Page */
.a4page {
line-height: 1.6;
font-family: var(--font-text);
width: 210mm;
height: 297mm;
padding: 5mm;
box-sizing: border-box;
background-color: var(--background);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
border: 1px solid var(--primary);
overflow: hidden;
margin: auto auto;
}
/* Component Styling */
main {
padding: 0px;
display: flex;
flex-direction: column;
height: fit-content;
background-color: white;
}
span {
height: 2em;
}
h1,
h2,
h3,
h4 {
border: none;
color: var(--primary);
font-family: var(--font-heading);
text-transform: capitalize;
}
h1 {
font-size: var(--font-size-heading);
}
h2 {
border-bottom: 1px solid var(--primary);
font-size: var(--font-size-subheading);
}
h3 {
font-size: var(--font-size-subsubheading);
}
a:hover {
color: var(--tertiary);
}
a {
background-color: transparent;
color: var(--secondary);
}
p {
margin-bottom: 0.2em;
color: var(--primary);
font-size: var(--font-size-text);
}
table {
color: var(--secondary);
border-collapse: collapse;
border: 1px solid black;
}
td {
color: var(--secondary);
border-top: 1px solid var(--tertiary);
padding: 1px 10px 1px 10px;
font-size: var(--font-size-text);
text-align: left;
}
th {
color: var(--secondary);
border: 2px solid var(--tertiary);
padding: 1px 0px 1px 7px;
font-family: var(--font-heading);
font-size: var(--font-size-subsubheading);
background-color: var(--quaternary);
text-align: left;
}
@media print {
.no-print {
display: none !important;
}
}
small {
font-size: var(--font-size-small);
color: var(--primary);
}
ul {
font-size: var(--font-size-small);
margin: 0;
padding-left: 1.2em;
}
li {
font-size: var(--font-size-small);
color: var(--primary);
}
.skills-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 0.3em 1em;
margin-bottom: 0.2em;
}
</style>

View File

@@ -0,0 +1,301 @@
<script setup>
import Project from "./Project.vue";
</script>
<template>
<main>
<div class="no-print w-full h-20"></div>
<div class="a4page">
<div class="flex flex-row justify-between">
<h1 class="name">Adam French</h1>
<div class="contact-details text-right">
<p>+447563266931</p>
<p>adam.a.french@outlook.com</p>
<h4>
<a href="https://www.adam-french.co.uk">
www.adam-french.co.uk
</a>
</h4>
</div>
</div>
<h2>Profile</h2>
<p>
First Class Honours graduate in Computer Science with
Mathematics from the University of Leeds (81.1%). Dependable
and personable team player with five years of hospitality
experience across busy bars, restaurants, and event venues.
Thrives under pressure, communicates clearly, and takes
pride in providing excellent customer service.
</p>
<h2>Experience</h2>
<Project class="border-b border-dotted">
<template #left>
<h4>Belgrave Music Hall</h4>
</template>
<template #top>
<small>Bartender & Waiter</small>
<small>20212023</small>
</template>
<p>
Served food and drinks in a high-volume live-music venue
in Leeds. Handled busy weekend shifts, managed multiple
tables simultaneously, and maintained a calm, friendly
demeanour during peak hours.
</p>
</Project>
<Project class="border-b border-dotted">
<template #left>
<h4>The Crown and Anchor</h4>
</template>
<template #top>
<small>Bartender & Waiter</small>
<small>20202021</small>
</template>
<p>
Worked front-of-house at a busy pub, pulling pints,
taking orders, and ensuring a welcoming atmosphere.
Built rapport with regulars and adapted quickly to
changing priorities during service.
</p>
</Project>
<Project class="border-b border-dotted">
<template #left>
<h4>BFI Riverfront Kitchen</h4>
</template>
<template #top>
<small>Cashier & Waiter</small>
<small>20182020</small>
</template>
<p>
Operated the till, served customers, and helped
coordinate table service at a café on London's South
Bank. Developed strong cash-handling accuracy and
customer interaction skills in a fast-paced environment.
</p>
</Project>
<h2>Skills</h2>
<div class="skills-grid">
<div>
<strong>Service</strong><br /><small
>Bar work, Table service, Cash handling, Till
operation, Food hygiene</small
>
</div>
<div>
<strong>Soft Skills</strong><br /><small
>Communication, Teamwork, Time management, Composure
under pressure</small
>
</div>
<div>
<strong>Technical</strong><br /><small
>EPOS systems, Stock management, Event
coordination</small
>
</div>
</div>
<h2>Education</h2>
<div class="w-full h-fit flex-row flex gap-5">
<div class="flex-1 border-r border-dotted pr-3">
<h3>
<a
href="https://www.adam-french.co.uk/pdf/transcript.pdf"
>
University of Leeds
</a>
</h3>
<div
class="flex-row flex place-content-between m-auto place-items-center"
>
<small>81.1% First Class Honours</small>
<small>20212025</small>
</div>
<small
>BSc Computer Science with Mathematics
(International)</small
>
</div>
<div class="flex-1 pl-3">
<h3>University of Waterloo</h3>
<div
class="flex-row flex place-content-between m-auto place-items-center"
>
<small>Year abroad</small>
<small>20232024</small>
</div>
</div>
</div>
<h2>Interests</h2>
<ul>
<li>Leetcode daily competitive problem solving</li>
<li>Learning Mandarin</li>
<li>Rhythm Games</li>
<li>Climbing · Gym</li>
<li>Board games · Meetup.com</li>
</ul>
</div>
<div class="no-print w-full h-20"></div>
</main>
</template>
<style scoped>
/* Fonts */
@font-face {
font-family: "big_noodle_titling";
src: url("/fonts/big_noodle_titling.woff2") format("woff2");
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "CreatoDisplay";
src: url("/fonts/CreatoDisplay-Bold.woff2") format("woff2");
font-weight: normal;
font-style: normal;
font-display: swap;
}
/* Variables */
* {
--primary: black;
--secondary: #0000ff;
--tertiary: #ff0000;
--quaternary: #cccccc;
--background: white;
--font-heading: big_noodle_titling;
--font-text: CreatoDisplay;
--font-size-name: 2.5em;
--font-size-text: 100%;
--font-size-small: 0.9em;
--font-size-heading: 2.1em;
--font-size-subheading: 1.7em;
--font-size-subsubheading: 1.4em;
}
/* A4 Page */
.a4page {
line-height: 1.6;
font-family: var(--font-text);
width: 210mm;
height: 297mm;
padding: 5mm;
box-sizing: border-box;
background-color: var(--background);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
border: 1px solid var(--primary);
overflow: hidden;
margin: auto auto;
}
/* Component Styling */
main {
padding: 0px;
display: flex;
flex-direction: column;
height: fit-content;
background-color: white;
}
span {
height: 2em;
}
h1,
h2,
h3,
h4 {
border: none;
color: var(--primary);
font-family: var(--font-heading);
text-transform: capitalize;
}
h1 {
font-size: var(--font-size-heading);
}
h2 {
border-bottom: 1px solid var(--primary);
font-size: var(--font-size-subheading);
}
h3 {
font-size: var(--font-size-subsubheading);
}
a:hover {
color: var(--tertiary);
}
a {
background-color: transparent;
color: var(--secondary);
}
p {
margin-bottom: 0.2em;
color: var(--primary);
font-size: var(--font-size-text);
}
table {
color: var(--secondary);
border-collapse: collapse;
border: 1px solid black;
}
td {
color: var(--secondary);
border-top: 1px solid var(--tertiary);
padding: 1px 10px 1px 10px;
font-size: var(--font-size-text);
text-align: left;
}
th {
color: var(--secondary);
border: 2px solid var(--tertiary);
padding: 1px 0px 1px 7px;
font-family: var(--font-heading);
font-size: var(--font-size-subsubheading);
background-color: var(--quaternary);
text-align: left;
}
@media print {
.no-print {
display: none !important;
}
}
small {
font-size: var(--font-size-small);
color: var(--primary);
}
ul {
font-size: var(--font-size-small);
margin: 0;
padding-left: 1.2em;
}
li {
font-size: var(--font-size-small);
color: var(--primary);
}
.skills-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 0.3em 1em;
margin-bottom: 0.2em;
}
</style>

View File

@@ -12,7 +12,7 @@ const links = [
</script> </script>
<template> <template>
<main class="halftone flex justify-center px-4 py-16"> <main class="flex justify-center px-4 py-16">
<div class="max-w-xl w-full flex flex-col gap-12"> <div class="max-w-xl w-full flex flex-col gap-12">
<section> <section>
<Header>Adam French</Header> <Header>Adam French</Header>

View File

@@ -60,7 +60,7 @@ onMounted(fetchFile);
<template> <template>
<main class="items-center flex flex-col"> <main class="items-center flex flex-col">
<div class="background halftone" /> <div class="background" />
<div <div
v-if="file" v-if="file"
class="a4page-portrait border-primary-1 flex flex-col relative overflow-scroll gap-1 bg-bg_primary" class="a4page-portrait border-primary-1 flex flex-col relative overflow-scroll gap-1 bg-bg_primary"

View File

@@ -10,7 +10,7 @@ const shrine_links = [
<template> <template>
<main class="items-center flex flex-col"> <main class="items-center flex flex-col">
<div class="background halftone" /> <div class="background" />
<div <div
class="a4page-portrait bdr-1 flex flex-col relative overflow-scroll gap-1" class="a4page-portrait bdr-1 flex flex-col relative overflow-scroll gap-1"
> >

View File

@@ -14,7 +14,7 @@ const auth = useAuthStore();
</script> </script>
<template> <template>
<main class="halftone justify-center flex flex-row w-full h-full"> <main class="justify-center flex flex-row w-full h-full">
<div class="bdr-1 flex flex-col"> <div class="bdr-1 flex flex-col">
<Login class="bdr-2 bg-bg_primary" /> <Login class="bdr-2 bg-bg_primary" />
<CreateUser class="bdr-2 bg-bg_primary" v-if="auth.loggedIn" /> <CreateUser class="bdr-2 bg-bg_primary" v-if="auth.loggedIn" />

View File

@@ -24,7 +24,7 @@ import Steam from "./Steam.vue";
</script> </script>
<template> <template>
<main class="halftone justify-center flex flex-row w-full h-full"> <main class="justify-center flex flex-row w-full h-full">
<div class="outerWrap flex flex-row"> <div class="outerWrap flex flex-row">
<div class="sidebar"> <div class="sidebar">
<Time class="sidebar-cell" /> <Time class="sidebar-cell" />
@@ -79,7 +79,7 @@ import Steam from "./Steam.vue";
} }
.sidebar-cell { .sidebar-cell {
background-color: var(--bg_secondary); background-color: var(--bg_primary);
border-width: 2px; border-width: 2px;
border-style: solid; border-style: solid;
border-color: var(--quaternary); border-color: var(--quaternary);