Refactor as SSG using Vite+React

This commit is contained in:
Michael Bradley 2025-01-03 00:50:52 +13:00
parent a22071815d
commit 6830bd41dd
Signed by: MichaelBradley
SSH key fingerprint: SHA256:cj/YZ5VT+QOKncqSkx+ibKTIn0Obg7OIzwzl9BL8EO8
25 changed files with 3076 additions and 282 deletions

5
.gitignore vendored
View file

@ -1,5 +1,4 @@
.idea/ .idea/
.vscode/ .vscode/
node_modules/
# Built files dist/
out/

5
.prettierrc.json Normal file
View file

@ -0,0 +1,5 @@
{
"useTabs": true,
"htmlWhitespaceSensitivity": "ignore",
"printWidth": 120
}

View file

@ -1,23 +0,0 @@
#!/bin/sh
build_dir="out"
assets_dir="assets"
mkdir -p "$build_dir"
minify -o "$build_dir" index.html style.css
cp -r "$assets_dir" "$build_dir"/
for file in "$assets_dir"/*; do
file_basename="$(basename "$file")"
file_name="${file_basename%%.*}"
file_extension="${file_basename#*.}"
printf "%s ->" "$file_basename"
for format in png jpeg webp jxl avif heic; do
if [ "$format" = "$file_extension" ]; then
continue
fi
printf " %s" "$format"
magick "$file" "${build_dir}/${assets_dir}/${file_name}.${format}"
done
echo
done

28
eslint.config.js Normal file
View file

@ -0,0 +1,28 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
)

View file

@ -1,173 +1,6 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <body id="root">
<meta charset="UTF-8" /> <script type="module" src="/src/entry-client.tsx"></script>
<link rel="stylesheet" href="style.css" /> </body>
<!--TODO: Optimize loading?-->
<!--<link href="https://fonts.cdnfonts.com/css/jetbrains-mono" rel="stylesheet" />-->
<title>Michael Bradley</title>
<meta lang="EN" />
<meta name="color-scheme" content="dark light" />
<meta name="theme-color" content="#000000" media="(prefers-color-scheme: dark)" />
<meta name="theme-color" content="#eaeaea" media="(prefers-color-scheme: light)" />
<!--TODO: Find better icon than GH avatar-->
<!--<link rel="icon" href="https://avatars.githubusercontent.com/u/68788583?s=48&v=4" />-->
<link rel="icon" href="data:,">
<meta name="keywords" content="Michael Bradley,personal website,programmer,coder,software developer" />
<meta name="description" content="Michael Bradley's personal website" />
<meta name="subject" content="Self-advertisement" />
<meta name="robots" content="index,follow" />
<meta name="summary" content="Michael Bradley is a graduating CS student looking for a full-time job starting in February" />
<meta name="url" content="https://mmbradley.ca" />
<meta property="og:type" content="website" />
<meta property="og:country-name" content="CA" />
<meta property="og:region" content="ON" />
<meta property="og:local" content="en_CA" />
<meta property="og:local:alternate" content="en_US" />
<meta property="og:url" content="https://mmbradley.ca" />
<meta property="og:site" content="Michael Bradley" />
<meta property="og:title" content="Michael's personal website" />
<meta property="og:description" content="A summary of my education, skills, and some projects I've worked on." />
<meta property="og:image" content="https://mmbradley.ca/assets/thumb.webp" />
<meta property="og:image:secure_url" content="https://mmbradley.ca/assets/thumb.webp" />
<meta property="og:image:alt" content="Michael Bradley is a CS student looking for a full-time job starting in February" />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="4203" />
<meta property="og:image:height" content="2253" />
<meta name="twitter:url" content="https://mmbradley.ca" />
<meta name="twitter:title" content="Michael Bradley" />
<meta name="twitter:description" content="My personal website." />
<meta name="twitter:image" content="https://mmbradley.ca/assets/thumb.webp" />
<meta name="twitter:card" content="summary_large_image" />
</head>
<body>
<div id="fetch-container">
<div id="fetch">
<!--TODO: Automate screenshot of this div as thumb.png?-->
<div id="michael-photo-container">
<picture>
<source srcset="assets/MichaelBradley.jxl" type="image/jxl">
<source srcset="assets/MichaelBradley.avif" type="image/avif">
<source srcset="assets/MichaelBradley.webp" type="image/webp">
<img src="assets/MichaelBradley.jpeg" alt="A picture of me in a suit smiling" id="michael-photo">
</picture>
</div>
<div id="basic-info">
<div class="invisible-div">
<p><span class="text-highlight">website</span>@<span class="text-highlight">MichaelBradley</span></p>
<p>----------------------</p>
<p><span class="text-highlight">Degree</span>: Bachelor of Computer Science</p>
<p><span class="text-highlight">University</span>: Carleton</p>
<p><span class="text-highlight">Major CGPA</span>: 11.50/12 (A+)</p>
<p><span class="text-highlight">Languages</span>: C/C++, Python, TypeScript</p>
<p><span class="text-highlight">Skills</span>: Linux, Git, Testing</p>
<p><span class="text-highlight">Work Experience</span>: 2 years</p>
<p><span class="text-highlight">Applying for</span>: Full-time job</p>
<p><span class="text-highlight">Location</span>: Toronto or Remote</p>
</div>
<p><br /></p>
<p class="blocks">
<span style="color: var(--color0)">███</span><span style="color: var(--color1)">███</span><span style="color: var(--color2)">███</span><span style="color: var(--color3)">███</span><span style="color: var(--color4)">███</span><span style="color: var(--color5)">███</span><span style="color: var(--color6)">███</span><span style="color: var(--color7)">███</span>
</p>
<p class="blocks">
<span style="color: var(--color8)">███</span><span style="color: var(--color9)">███</span><span style="color: var(--color10)">███</span><span style="color: var(--color11)">███</span><span style="color: var(--color12)">███</span><span style="color: var(--color13)">███</span><span style="color: var(--color14)">███</span><span style="color: var(--color15)">███</span>
</p>
</div>
</div>
</div>
<div class="links">
<a href="https://github.com/MichaelMBradley" title="GitHub account">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512" aria-description="GitHub icon">
<!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->
<path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/>
</svg>
</a>
<a href="https://www.linkedin.com/in/michaelmbradley/" title="LinkedIn account">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" aria-description="LinkedIn icon">
<!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->
<path d="M416 32H31.9C14.3 32 0 46.5 0 64.3v383.4C0 465.5 14.3 480 31.9 480H416c17.6 0 32-14.5 32-32.3V64.3c0-17.8-14.4-32.3-32-32.3zM135.4 416H69V202.2h66.5V416zm-33.2-243c-21.3 0-38.5-17.3-38.5-38.5S80.9 96 102.2 96c21.2 0 38.5 17.3 38.5 38.5 0 21.3-17.2 38.5-38.5 38.5zm282.1 243h-66.4V312c0-24.8-.5-56.7-34.5-56.7-34.6 0-39.9 27-39.9 54.9V416h-66.4V202.2h63.7v29.2h.9c8.9-16.8 30.6-34.5 62.9-34.5 67.2 0 79.7 44.3 79.7 101.9V416z"/>
</svg>
</a>
<a href="https://mmbradley.ca/resume.pdf" title="My Résumé">
<!-- From: https://www.svgrepo.com/svg/483015/resume-4 -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" aria-description="CV icon">
<g>
<path class="st0" d="M276.239,252.183c-6.37,2.127-13.165,3.308-20.239,3.308c-7.074,0-13.87-1.181-20.24-3.308 c-46.272,7.599-70.489,41.608-70.489,82.877H256h90.728C346.728,293.791,322.515,259.782,276.239,252.183z" />
<path class="st0" d="M256,240.788c27.43,0,49.658-22.24,49.658-49.666v-14.087c0-27.426-22.228-49.659-49.658-49.659 c-27.43,0-49.658,22.233-49.658,49.659v14.087C206.342,218.548,228.57,240.788,256,240.788z" />
<path class="st0" d="M378.4,0H133.582C86.234,0,47.7,38.542,47.7,85.899v340.22C47.7,473.476,86.234,512,133.582,512h205.695 h13.175l9.318-9.301l93.229-93.229l9.301-9.31v-13.174V85.899C464.3,38.542,425.766,0,378.4,0z M432.497,386.985H384.35 c-24.882,0-45.074,20.183-45.074,45.073v48.139H133.582c-29.866,0-54.078-24.221-54.078-54.078V85.899 c0-29.874,24.212-54.096,54.078-54.096H378.4c29.876,0,54.096,24.222,54.096,54.096V386.985z" />
</g>
</svg>
</a>
<a rel="me" href="https://mstdn.ca/@michaelbradley" title="Mastodon account">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" aria-description="Mastodon icon">
<!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->
<path d="M433 179.1c0-97.2-63.7-125.7-63.7-125.7-62.5-28.7-228.6-28.4-290.5 0 0 0-63.7 28.5-63.7 125.7 0 115.7-6.6 259.4 105.6 289.1 40.5 10.7 75.3 13 103.3 11.4 50.8-2.8 79.3-18.1 79.3-18.1l-1.7-36.9s-36.3 11.4-77.1 10.1c-40.4-1.4-83-4.4-89.6-54a102.5 102.5 0 0 1 -.9-13.9c85.6 20.9 158.7 9.1 178.8 6.7 56.1-6.7 105-41.3 111.2-72.9 9.8-49.8 9-121.5 9-121.5zm-75.1 125.2h-46.6v-114.2c0-49.7-64-51.6-64 6.9v62.5h-46.3V197c0-58.5-64-56.6-64-6.9v114.2H90.2c0-122.1-5.2-147.9 18.4-175 25.9-28.9 79.8-30.8 103.8 6.1l11.6 19.5 11.6-19.5c24.1-37.1 78.1-34.8 103.8-6.1 23.7 27.3 18.4 53 18.4 175z"/>
</svg>
</a>
<input id="dark-mode-toggle" type="checkbox" aria-label="Toggle dark mode" />
<label for="dark-mode-toggle" id="dark-mode-toggle-label" title="Toggle dark mode">
<svg viewBox="0 0 544 544" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="dark-mode-toggle-sky" x1="0" x2="0" y1="0.4" y2="0.6">
<stop offset="0%" stop-color="deepskyblue" />
<stop offset="100%" stop-color="black" />
</linearGradient>
</defs>
<!-- From: https://www.svgrepo.com/svg/484307/moon -->
<rect class="dark-mode-svg" height="1280" width="544" fill="url(#dark-mode-toggle-sky)" x="-16px" y="-16px"/>
<g class="dark-mode-svg">
<path transform="translate(0, 768)" style="fill: rgb(244, 244, 245);" d="M426.655,444.491c-85.064,74.278-206.9,83.839-299.319,29.581 c-22.308-13.074-42.982-29.907-60.958-50.499C56,411.723,46.93,399.058,39.085,385.82C15.143,345.045,3.539,298.958,3.784,252.953 c0.49-71.582,29.989-142.754,87.026-192.6C138.776,18.433,197.855-1.096,256.69,0.047c45.597,0.817,91.03,13.973,131.069,38.733 c22.063,13.564,42.41,30.724,60.305,51.153c9.724,11.114,18.386,22.799,25.822,34.974 C537.623,227.785,521.117,361.878,426.655,444.491z" />
<path transform="translate(0, 768)" style="fill: rgb(237, 237, 236);" d="M107.7,89.244c99.915-87.35,248.817-74.175,333.815,23.051 c84.998,97.226,75.388,243.379-24.528,330.729c-99.915,87.35-251.727,82.317-336.725-14.908S7.784,176.594,107.7,89.244z" />
<path transform="translate(0, 768)" style="fill: rgb(216, 216, 216);" d="M244.029,141.49c-17.92,37.27-63.032,51.341-100.302,33.421 c-37.27-17.92-53.234-61.357-35.315-98.627c17.92-37.27,62.835-54.046,100.105-36.126 C245.787,58.078,261.948,104.22,244.029,141.49z" />
<path transform="translate(0, 768)" style="opacity: 0.06; fill: rgb(4, 0, 0);" d="M128.086,97.65c17.92-37.27,62.835-54.046,100.105-36.126 c4.127,1.984,7.994,4.316,11.586,6.942c-7.335-11.909-17.95-21.909-31.26-28.308c-37.27-17.92-82.185-1.144-100.105,36.126 c-15.805,32.872-5.247,70.538,23.036,91.265C118.963,147.091,116.789,121.146,128.086,97.65z" />
<path transform="translate(0, 768)" style="fill: rgb(216, 216, 216);" d="M217.121,218.367c-1.17-5.733,2.71-11.178,8.442-12.348c5.733-1.17,11.248,2.359,12.418,8.091 c1.17,5.733-2.456,11.466-8.189,12.635C224.06,227.916,218.291,224.099,217.121,218.367z" />
<path transform="translate(0, 768)" style="opacity: 0.5; fill: rgb(255, 255, 255);" d="M363.151,96.945c-1.17-5.733,2.71-11.178,8.442-12.348s11.248,2.359,12.418,8.091 c1.17,5.733-2.456,11.466-8.189,12.636C370.089,106.493,364.32,102.677,363.151,96.945z" />
<path transform="translate(0, 768)" style="fill: rgb(216, 216, 216);" d="M282.752,398.389c8.691-7.598,21.813-6.256,29.411,2.435c7.598,8.691,6.926,21.591-1.765,29.189 c-8.691,7.598-22.059,6.972-29.657-1.719C273.143,419.603,274.061,405.987,282.752,398.389z" />
<path transform="translate(0, 768)" style="opacity: 0.5; fill: rgb(255, 255, 255);" d="M58.327,220.961c-1.17-5.733,2.71-11.178,8.442-12.348 c5.733-1.17,11.248,2.359,12.418,8.091s-2.456,11.466-8.189,12.636C65.265,230.51,59.496,226.694,58.327,220.961z" />
<path transform="translate(0, 768)" style="fill: rgb(216, 216, 216);" d="M468.947,281.701c-3.725,36.649-37.256,62.098-73.905,58.373 c-36.649-3.725-63.177-35.279-59.452-71.928c3.725-36.649,36.272-64.305,72.921-60.58 C445.16,211.292,472.673,245.052,468.947,281.701z" />
<path transform="translate(0, 768)" style="fill: rgb(216, 216, 216);" d="M173.239,331.136c14.631,25.328,4.867,57.294-20.461,71.925 c-25.328,14.631-57.07,6.642-71.701-18.686c-14.631-25.328-6.526-58.257,18.802-72.888 C125.206,296.855,158.608,305.808,173.239,331.136z" />
<path transform="translate(0, 768)" style="opacity: 0.06; fill: rgb(4, 0, 0);" d="M112.818,324.329c18.464-10.666,41.21-8.787,57.855,2.82 c-15.693-22.238-46.847-29.497-70.794-15.663c-25.328,14.631-33.433,47.561-18.802,72.888c4.04,6.993,9.388,12.657,15.541,16.895 c-0.915-1.299-1.788-2.644-2.602-4.052C79.385,371.89,87.49,338.96,112.818,324.329z" />
<path transform="translate(0, 768)" style="opacity: 0.06; fill: rgb(4, 0, 0);" d="M349.701,282.093c3.725-36.649,36.272-64.305,72.921-60.579 c12.217,1.242,23.415,5.824,32.783,12.735c-11.007-14.534-27.694-24.73-46.893-26.682c-36.649-3.725-69.196,23.93-72.921,60.579 c-2.465,24.247,8.316,46.261,26.506,59.464C352.777,315.06,347.969,299.128,349.701,282.093z" />
<path transform="translate(0, 768)" style="opacity: 0.1; fill: rgb(4, 0, 0);" d="M254.81,381.707c-105.358,0-198.419-52.064-254.72-131.654 c-2.703,99.72,55.552,194.334,153.936,236.742c128.773,55.507,279.648,1.534,335.155-127.239 c15.267-35.419,21.657-72.747,20.288-109.416C453.162,329.68,360.13,381.707,254.81,381.707z" />
</g>
<!-- From: https://www.svgrepo.com/svg/484356/sun -->
<g class="dark-mode-svg">
<path style="fill: rgb(191, 182, 30);" d="M258.373,448.122c-11.783,0-21.337,1.395-21.337,18.136c0,8.131,9.553,45.742,21.337,45.742 c11.784,0,21.336-37.611,21.336-45.742C279.709,449.518,270.156,448.122,258.373,448.122z" />
<path style="fill: rgb(191, 182, 30);" d="M352.653,422.86c-10.205,5.891-17.78,11.876-9.41,26.374c4.065,7.041,31.144,34.837,41.349,28.945 c10.205-5.892-0.328-43.241-4.393-50.282C371.829,413.4,362.858,416.968,352.653,422.86z" />
<path style="fill: rgb(191, 182, 30);" d="M448.046,344.432c-14.498-8.37-20.483-0.795-26.375,9.41c-5.892,10.205-9.46,19.176,5.038,27.546 c7.041,4.065,44.39,14.598,50.282,4.393C482.883,375.576,455.087,348.497,448.046,344.432z" />
<path style="fill: rgb(191, 182, 30);" d="M465.07,238.225c-16.741,0-18.136,9.553-18.136,21.337c0,11.784,1.396,21.336,18.136,21.336 c8.13,0,45.742-9.553,45.742-21.336C510.812,247.777,473.2,238.225,465.07,238.225z" />
<path style="fill: rgb(191, 182, 30);" d="M426.71,137.735c-14.498,8.37-10.93,17.341-5.038,27.546c5.892,10.204,11.877,17.78,26.375,9.41 c7.041-4.065,34.837-31.144,28.945-41.349C471.099,123.137,433.75,133.67,426.71,137.735z" />
<path style="fill: rgb(191, 182, 30);" d="M164.092,422.86c-10.205-5.892-19.176-9.46-27.546,5.038c-4.065,7.041-14.598,44.39-4.393,50.282 c10.205,5.892,37.283-21.904,41.349-28.945C181.872,434.737,174.297,428.752,164.092,422.86z" />
<path style="fill: rgb(191, 182, 30);" d="M424.226,259.561c0-45.799-18.564-87.263-48.577-117.276L141.097,376.837 c30.013,30.013,71.477,48.578,117.276,48.578C349.971,425.415,424.226,351.159,424.226,259.561z" />
<path style="fill: rgb(198, 186, 86);" d="M164.11,96.239c-10.143,5.855-19.05,9.401-27.297-4.618c-0.082-0.083-0.165-0.247-0.248-0.412 c-4.123-7.009-14.596-44.367-4.453-50.305c7.669-4.454,25.07,10.308,34.719,20.781c3.298,3.464,5.69,6.433,6.68,8.164 C181.84,84.364,174.336,90.384,164.11,96.239z" />
<path style="fill: rgb(198, 186, 86);" d="M279.729,52.861v0.577c-0.248,16.164-9.732,17.566-21.359,17.566 c-9.319,0-17.236-0.907-20.122-9.483c-0.824-2.227-1.237-5.113-1.237-8.66c0-5.03,3.629-21.276,9.154-32.987 c3.546-7.257,7.752-12.782,12.205-12.782c1.319,0,2.639,0.495,3.876,1.402C272.225,15.174,279.729,45.604,279.729,52.861z" />
<path style="fill: rgb(198, 186, 86);" d="M95.085,165.264c-5.938,10.226-11.875,17.813-26.39,9.401 c-3.958-2.227-14.432-11.793-21.854-21.524c-0.082-0.083-0.165-0.165-0.165-0.248c-5.69-7.504-9.484-15.091-6.928-19.545 c5.938-10.226,43.213,0.33,50.305,4.371c1.237,0.742,2.391,1.484,3.381,2.226C103.909,147.699,100.445,155.945,95.085,165.264z" />
<path style="fill: rgb(198, 186, 86);" d="M69.85,259.524c0,11.546-1.32,21.03-17.236,21.359h-0.907c-7.834,0-43.13-8.907-45.605-20.122 c-0.082,0-0.082,0-0.082,0c0-0.412-0.083-0.824-0.083-1.237c0-4.536,5.69-8.824,13.112-12.205 c11.711-5.525,27.709-9.071,32.657-9.071c4.701,0,8.164,0.742,10.721,2.062C69.108,243.773,69.85,251.113,69.85,259.524z" />
<path style="fill: rgb(198, 186, 86);" d="M68.7,344.432c-7.041,4.065-34.837,31.144-28.945,41.349c5.892,10.205,43.241-0.328,50.281-4.393 c14.498-8.37,10.93-17.341,5.038-27.546C89.183,343.637,83.197,336.062,68.7,344.432z" />
<path style="fill: rgb(198, 186, 86);" d="M352.653,96.263c10.205,5.892,19.176,9.46,27.546-5.038c4.065-7.041,14.598-44.39,4.393-50.282 c-10.205-5.892-37.284,21.904-41.349,28.945C334.873,84.386,342.448,90.371,352.653,96.263z" />
<path style="fill: rgb(198, 186, 86);" d="M258.373,93.708c-91.598,0-165.853,74.255-165.853,165.853 c0,45.799,18.563,87.262,48.577,117.276l234.552-234.552C345.635,112.271,304.172,93.708,258.373,93.708z" />
<path style="fill: rgb(239, 231, 72);" d="M252.408,440.964c-11.783,0-21.337,1.395-21.337,18.136c0,8.131,9.553,45.742,21.337,45.742 s21.336-37.611,21.336-45.742C273.744,442.36,264.191,440.964,252.408,440.964z" />
<path style="fill: rgb(239, 231, 72);" d="M346.688,415.702c-10.205,5.892-17.78,11.877-9.41,26.375c4.065,7.041,31.144,34.837,41.349,28.945 c10.205-5.892-0.328-43.241-4.393-50.282C365.864,406.242,356.893,409.81,346.688,415.702z" />
<path style="fill: rgb(239, 231, 72);" d="M442.081,337.274c-14.498-8.37-20.483-0.795-26.375,9.41c-5.892,10.205-9.46,19.176,5.038,27.546 c7.041,4.065,44.39,14.598,50.282,4.393C476.918,368.418,449.122,341.339,442.081,337.274z" />
<path style="fill: rgb(239, 231, 72);" d="M459.105,231.066c-16.741,0-18.136,9.553-18.136,21.337c0,11.784,1.395,21.336,18.136,21.336 c8.13,0,45.742-9.553,45.742-21.336C504.846,240.619,467.235,231.066,459.105,231.066z" />
<path style="fill: rgb(239, 231, 72);" d="M420.744,130.577c-14.497,8.37-10.93,17.341-5.038,27.546c5.892,10.205,11.877,17.78,26.375,9.41 c7.041-4.065,34.837-31.144,28.945-41.349C465.134,115.979,427.785,126.511,420.744,130.577z" />
<path style="fill: rgb(239, 231, 72);" d="M158.127,415.702c-10.205-5.892-19.176-9.46-27.546,5.038c-4.065,7.041-14.598,44.39-4.392,50.282 c10.205,5.892,37.283-21.904,41.349-28.945C175.907,427.578,168.332,421.594,158.127,415.702z" />
<path style="fill: rgb(239, 231, 72);" d="M418.261,252.403c0-45.799-18.564-87.263-48.577-117.276L135.132,369.679 c30.014,30.013,71.477,48.578,117.276,48.578C344.006,418.257,418.261,344.001,418.261,252.403z" />
<path style="fill: rgb(250, 242, 175);" d="M158.09,89.065c-7.67,4.453-14.679,7.587-21.277,2.557c-2.144-1.567-4.206-4.041-6.268-7.587 c-4.041-7.01-14.597-44.367-4.371-50.223c9.814-5.69,34.967,19.545,40.657,27.874c0.33,0.412,0.577,0.742,0.742,1.072 C175.903,77.189,168.316,83.209,158.09,89.065z" />
<path style="fill: rgb(250, 242, 175);" d="M273.709,45.687c0,0.577,0,1.155-0.083,1.65c-0.577,15.174-9.814,16.493-21.194,16.493 c-4.288,0-8.247-0.165-11.628-1.237c-0.907-0.247-1.732-0.659-2.556-1.072c-2.722-1.402-4.866-3.711-6.02-7.422 c-0.083-0.083,0-0.083,0-0.083c-0.742-2.227-1.155-5.03-1.155-8.329c0-4.865,3.464-20.452,8.824-32.08 c3.216-7.01,7.175-12.617,11.381-13.442C251.69,0.083,252.02,0,252.432,0c3.547,0,6.927,3.463,9.814,8.494 c0.99,1.649,1.897,3.464,2.804,5.443C270.328,25.482,273.709,40.904,273.709,45.687z" />
<path style="fill: rgb(250, 242, 175);" d="M89.147,158.09c-5.937,10.226-11.875,17.813-26.39,9.484c-2.969-1.732-9.648-7.505-15.916-14.432 c-0.082-0.083-0.165-0.165-0.165-0.248c-8.577-9.401-16.246-20.864-12.865-26.719c5.855-10.226,43.213,0.33,50.222,4.371 c5.195,3.051,8.164,6.185,9.401,9.401C95.662,145.637,92.858,151.658,89.147,158.09z" />
<path style="fill: rgb(250, 242, 175);" d="M63.83,252.432c0,11.793-1.402,21.277-18.142,21.277H45.44c-5.03-0.083-20.122-3.382-31.503-8.577 c-2.886-1.402-5.608-2.804-7.835-4.371c-0.082,0-0.082,0-0.082,0C2.309,258.205,0,255.401,0,252.432c0-0.33,0-0.742,0.165-1.072 c0.742-4.041,5.69-7.669,12.04-10.886c11.793-5.608,28.451-9.401,33.482-9.401c1.897,0,3.629,0.165,5.196,0.412 c6.762,0.99,9.978,4.288,11.545,8.824c0.412,1.072,0.66,2.309,0.825,3.546C63.748,246.412,63.83,249.381,63.83,252.432z" />
<path style="fill: rgb(250, 242, 175);" d="M84.034,374.237c-5.196,3.051-27.379,9.649-40.739,8.576c-1.567-0.083-2.969-0.33-4.288-0.742 c-2.392-0.66-4.206-1.815-5.195-3.464c-0.908-1.567-1.072-3.629-0.577-5.855c2.804-12.206,23.503-32.08,29.523-35.461 c2.969-1.732,5.608-2.804,7.917-3.216c7.917-1.732,12.453,2.969,16.659,9.566c0.577,0.99,1.237,1.979,1.814,3.051 c2.062,3.711,3.959,7.175,4.701,10.556C95.25,363.268,93.353,368.876,84.034,374.237z" />
<path style="fill: rgb(250, 242, 175);" d="M346.688,89.104c10.205,5.892,19.176,9.46,27.546-5.038c4.065-7.041,14.598-44.39,4.393-50.282 c-10.205-5.892-37.284,21.904-41.349,28.945C328.908,77.228,336.483,83.213,346.688,89.104z" />
<path style="fill: rgb(250, 242, 175);" d="M369.701,135.164l-0.743,0.742l-3.381,3.381L135.164,369.701 c-2.474-2.474-4.783-4.948-7.092-7.587c-6.185-7.009-11.793-14.514-16.741-22.513c-15.668-25.318-24.74-55.171-24.74-87.168 c0-91.621,74.221-165.842,165.842-165.842c29.936,0,58.057,7.917,82.385,21.936C347.6,115.784,359.31,124.773,369.701,135.164z" />
</g>
</svg>
</label>
</div>
</body>
</html> </html>

38
package.json Normal file
View file

@ -0,0 +1,38 @@
{
"name": "mmbradley-home",
"private": "true",
"version": "2.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "yarn build:client && yarn build:server",
"build:client": "tsc -b && vite build --outDir dist/client",
"build:server": "tsc -b && vite build --outDir dist/server --ssr src/entry-server.tsx",
"lint": "eslint .",
"preview": "vite preview"
},
"devDependencies": {
"@eslint/js": "^9.17.0",
"@svgr/core": "^8.1.0",
"@svgr/plugin-jsx": "^8.1.0",
"@svgr/plugin-svgo": "^8.1.0",
"@types/react": "^19.0.2",
"@types/react-dom": "^19.0.2",
"@vitejs/plugin-react": "^4.3.4",
"eslint": "^9.17.0",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.16",
"globals": "^15.14.0",
"prettier": "^3.4.2",
"typescript": "^5.7.2",
"typescript-eslint": "^8.19.0",
"vite": "^6.0.6",
"vite-imagetools": "^7.0.5",
"vite-plugin-html": "^3.2.2",
"vite-plugin-svgr": "^4.3.0"
},
"dependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0"
}
}

17
render.sh Executable file
View file

@ -0,0 +1,17 @@
#!/bin/sh
yarn build
PROD='dist/prod'
rm -r "$PROD"
mkdir -p "$PROD"
echo 'Rendering webpage...'
node -e 'import("./dist/server/entry-server.js").then(({render}) => console.log(render().html))' > "$PROD"/index.html
echo "Preparing $PROD..."
# Copy used assets
cp -r dist/client/assets "$PROD"/
# Not needed: Pre-rendered
rm "$PROD"/assets/*.js
# Not needed: Inlined
rm "$PROD"/assets/*.css

114
src/Body.tsx Normal file
View file

@ -0,0 +1,114 @@
import "./style.css";
import hero from "./assets/MichaelBradley.jpeg?format=avif;webp;jpeg&as=picture";
import CV from "./assets/cv.svg?react";
import GitHub from "./assets/github.svg?react";
import LinkedIn from "./assets/linkedin.svg?react";
import Mastodon from "./assets/mastodon.svg?react";
import DarkModeToggleIcon from "./assets/darkmodetoggle.svg?react";
// TODO: Automate screenshot of info for thumbnail?
const Fetch = () => {
return (
<div id="fetch-container">
<div id="fetch">
<div id="michael-photo-container">
<picture>
{Object.entries(hero.sources).map(([format, src]) => (
<source srcSet={src} type={`image/${format}`} key={format} />
))}
<img src={hero.img.src} alt="A picture of me in a suit smiling" id="michael-photo" />
</picture>
</div>
<div id="basic-info">
<div className="invisible-div">
<p>
<span className="text-highlight">website</span>@<span className="text-highlight">MichaelBradley</span>
</p>
<p>----------------------</p>
<p>
<span className="text-highlight">Degree</span>: Bachelor of Computer Science
</p>
<p>
<span className="text-highlight">University</span>: Carleton
</p>
<p>
<span className="text-highlight">Major CGPA</span>: 11.50/12 (A+)
</p>
<p>
<span className="text-highlight">Languages</span>: C/C++, Python, TypeScript
</p>
<p>
<span className="text-highlight">Skills</span>: Linux, Git, Testing
</p>
<p>
<span className="text-highlight">Work Experience</span>: 2 years
</p>
<p>
<span className="text-highlight">Applying for</span>: Full-time job
</p>
<p>
<span className="text-highlight">Location</span>: Toronto or Remote
</p>
</div>
<p>
<br />
</p>
<p className="blocks">
<span style={{ color: "var(--color0)" }}></span>
<span style={{ color: "var(--color1)" }}></span>
<span style={{ color: "var(--color2)" }}></span>
<span style={{ color: "var(--color3)" }}></span>
<span style={{ color: "var(--color4)" }}></span>
<span style={{ color: "var(--color5)" }}></span>
<span style={{ color: "var(--color6)" }}></span>
<span style={{ color: "var(--color7)" }}></span>
</p>
<p className="blocks">
<span style={{ color: "var(--color8)" }}></span>
<span style={{ color: "var(--color9)" }}></span>
<span style={{ color: "var(--color10)" }}></span>
<span style={{ color: "var(--color11)" }}></span>
<span style={{ color: "var(--color12)" }}></span>
<span style={{ color: "var(--color13)" }}></span>
<span style={{ color: "var(--color14)" }}></span>
<span style={{ color: "var(--color15)" }}></span>
</p>
</div>
</div>
</div>
);
};
const Links = () => (
<div className="links">
<a href="https://github.com/MichaelMBradley" title="GitHub account">
<GitHub />
</a>
<a href="https://www.linkedin.com/in/michaelmbradley/" title="LinkedIn account">
<LinkedIn />
</a>
<a href="https://mmbradley.ca/resume.pdf" title="My Résumé">
<CV />
</a>
<a rel="me" href="https://mstdn.ca/@michaelbradley" title="Mastodon account">
<Mastodon />
</a>
<input id="dark-mode-toggle" type="checkbox" aria-label="Toggle dark mode" />
<label htmlFor="dark-mode-toggle" id="dark-mode-toggle-label" title="Toggle dark mode">
<DarkModeToggleIcon />
</label>
</div>
);
const Body = () => {
return (
<>
<Fetch />
<Links />
</>
);
};
export default Body;

72
src/Head.tsx Normal file
View file

@ -0,0 +1,72 @@
import thumb from "./assets/thumb.png?format=webp&as=metadata";
const DESCRIPTION = "Michael Bradley is a computer scientist looking for a full-time job";
const Preamble = () => (
<>
<meta charSet="UTF-8" />
<title>Michael Bradley</title>
<meta lang="EN" />
</>
);
const Styles = () => (
<>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="color-scheme" content="dark light" />
<meta name="theme-color" content="#000000" media="(prefers-color-scheme: dark)" />
<meta name="theme-color" content="#eaeaea" media="(prefers-color-scheme: light)" />
</>
);
// TODO: Find better icon than GH avatar
// <link rel="icon" href="https://avatars.githubusercontent.com/u/68788583?s=48&v=4" />
const SEO = () => (
<>
<link rel="icon" href="data:," />
<meta name="keywords" content="Michael Bradley,personal website,programmer,coder,software developer" />
<meta name="description" content="Michael Bradley's personal website" />
<meta name="subject" content="Self-advertisement" />
<meta name="robots" content="index,follow" />
<meta name="summary" content={DESCRIPTION} />
<meta name="url" content={import.meta.env.BASE_URL} />
</>
);
const OpenGraph = () => (
<>
<meta property="og:type" content="website" />
<meta property="og:country-name" content="CA" />
<meta property="og:region" content="ON" />
<meta property="og:local" content="en_CA" />
<meta property="og:local:alternate" content="en_US" />
<meta property="og:url" content={import.meta.env.BASE_URL} />
<meta property="og:site" content="Michael Bradley" />
<meta property="og:title" content="Michael's personal website" />
<meta property="og:description" content="A summary of my education, skills, and some projects I've worked on." />
<meta property="og:image" content={thumb.src} />
<meta property="og:image:secure_url" content={thumb.src} />
<meta property="og:image:alt" content={DESCRIPTION} />
<meta property="og:image:type" content={`image/${thumb.format}`} />
<meta property="og:image:width" content={`${thumb.width}`} />
<meta property="og:image:height" content={`${thumb.height}`} />
<meta name="twitter:url" content={import.meta.env.BASE_URL} />
<meta name="twitter:title" content="Michael Bradley" />
<meta name="twitter:description" content="My personal website." />
<meta name="twitter:image" content={thumb.src} />
<meta name="twitter:card" content="summary_large_image" />
</>
);
const Head = () => (
<>
<Preamble />
<Styles />
<SEO />
<OpenGraph />
</>
);
export default Head;

View file

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 131 KiB

Before After
Before After

17
src/assets/cv.svg Normal file
View file

@ -0,0 +1,17 @@
<!-- From: https://www.svgrepo.com/svg/483015/resume-4 -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" aria-description="CV icon">
<g>
<path
class="st0"
d="M276.239,252.183c-6.37,2.127-13.165,3.308-20.239,3.308c-7.074,0-13.87-1.181-20.24-3.308 c-46.272,7.599-70.489,41.608-70.489,82.877H256h90.728C346.728,293.791,322.515,259.782,276.239,252.183z"
/>
<path
class="st0"
d="M256,240.788c27.43,0,49.658-22.24,49.658-49.666v-14.087c0-27.426-22.228-49.659-49.658-49.659 c-27.43,0-49.658,22.233-49.658,49.659v14.087C206.342,218.548,228.57,240.788,256,240.788z"
/>
<path
class="st0"
d="M378.4,0H133.582C86.234,0,47.7,38.542,47.7,85.899v340.22C47.7,473.476,86.234,512,133.582,512h205.695 h13.175l9.318-9.301l93.229-93.229l9.301-9.31v-13.174V85.899C464.3,38.542,425.766,0,378.4,0z M432.497,386.985H384.35 c-24.882,0-45.074,20.183-45.074,45.073v48.139H133.582c-29.866,0-54.078-24.221-54.078-54.078V85.899 c0-29.874,24.212-54.096,54.078-54.096H378.4c29.876,0,54.096,24.222,54.096,54.096V386.985z"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,192 @@
<svg viewBox="0 0 544 544" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="dark-mode-toggle-sky" x1="0" x2="0" y1="0.4" y2="0.6">
<stop offset="0%" stop-color="deepskyblue" />
<stop offset="100%" stop-color="black" />
</linearGradient>
</defs>
<!-- From: https://www.svgrepo.com/svg/484307/moon -->
<rect class="dark-mode-svg" height="1280" width="544" fill="url(#dark-mode-toggle-sky)" x="-16px" y="-16px" />
<g class="dark-mode-svg">
<path
transform="translate(0, 768)"
style="fill: rgb(244, 244, 245)"
d="M426.655,444.491c-85.064,74.278-206.9,83.839-299.319,29.581 c-22.308-13.074-42.982-29.907-60.958-50.499C56,411.723,46.93,399.058,39.085,385.82C15.143,345.045,3.539,298.958,3.784,252.953 c0.49-71.582,29.989-142.754,87.026-192.6C138.776,18.433,197.855-1.096,256.69,0.047c45.597,0.817,91.03,13.973,131.069,38.733 c22.063,13.564,42.41,30.724,60.305,51.153c9.724,11.114,18.386,22.799,25.822,34.974 C537.623,227.785,521.117,361.878,426.655,444.491z"
/>
<path
transform="translate(0, 768)"
style="fill: rgb(237, 237, 236)"
d="M107.7,89.244c99.915-87.35,248.817-74.175,333.815,23.051 c84.998,97.226,75.388,243.379-24.528,330.729c-99.915,87.35-251.727,82.317-336.725-14.908S7.784,176.594,107.7,89.244z"
/>
<path
transform="translate(0, 768)"
style="fill: rgb(216, 216, 216)"
d="M244.029,141.49c-17.92,37.27-63.032,51.341-100.302,33.421 c-37.27-17.92-53.234-61.357-35.315-98.627c17.92-37.27,62.835-54.046,100.105-36.126 C245.787,58.078,261.948,104.22,244.029,141.49z"
/>
<path
transform="translate(0, 768)"
style="opacity: 0.06; fill: rgb(4, 0, 0)"
d="M128.086,97.65c17.92-37.27,62.835-54.046,100.105-36.126 c4.127,1.984,7.994,4.316,11.586,6.942c-7.335-11.909-17.95-21.909-31.26-28.308c-37.27-17.92-82.185-1.144-100.105,36.126 c-15.805,32.872-5.247,70.538,23.036,91.265C118.963,147.091,116.789,121.146,128.086,97.65z"
/>
<path
transform="translate(0, 768)"
style="fill: rgb(216, 216, 216)"
d="M217.121,218.367c-1.17-5.733,2.71-11.178,8.442-12.348c5.733-1.17,11.248,2.359,12.418,8.091 c1.17,5.733-2.456,11.466-8.189,12.635C224.06,227.916,218.291,224.099,217.121,218.367z"
/>
<path
transform="translate(0, 768)"
style="opacity: 0.5; fill: rgb(255, 255, 255)"
d="M363.151,96.945c-1.17-5.733,2.71-11.178,8.442-12.348s11.248,2.359,12.418,8.091 c1.17,5.733-2.456,11.466-8.189,12.636C370.089,106.493,364.32,102.677,363.151,96.945z"
/>
<path
transform="translate(0, 768)"
style="fill: rgb(216, 216, 216)"
d="M282.752,398.389c8.691-7.598,21.813-6.256,29.411,2.435c7.598,8.691,6.926,21.591-1.765,29.189 c-8.691,7.598-22.059,6.972-29.657-1.719C273.143,419.603,274.061,405.987,282.752,398.389z"
/>
<path
transform="translate(0, 768)"
style="opacity: 0.5; fill: rgb(255, 255, 255)"
d="M58.327,220.961c-1.17-5.733,2.71-11.178,8.442-12.348 c5.733-1.17,11.248,2.359,12.418,8.091s-2.456,11.466-8.189,12.636C65.265,230.51,59.496,226.694,58.327,220.961z"
/>
<path
transform="translate(0, 768)"
style="fill: rgb(216, 216, 216)"
d="M468.947,281.701c-3.725,36.649-37.256,62.098-73.905,58.373 c-36.649-3.725-63.177-35.279-59.452-71.928c3.725-36.649,36.272-64.305,72.921-60.58 C445.16,211.292,472.673,245.052,468.947,281.701z"
/>
<path
transform="translate(0, 768)"
style="fill: rgb(216, 216, 216)"
d="M173.239,331.136c14.631,25.328,4.867,57.294-20.461,71.925 c-25.328,14.631-57.07,6.642-71.701-18.686c-14.631-25.328-6.526-58.257,18.802-72.888 C125.206,296.855,158.608,305.808,173.239,331.136z"
/>
<path
transform="translate(0, 768)"
style="opacity: 0.06; fill: rgb(4, 0, 0)"
d="M112.818,324.329c18.464-10.666,41.21-8.787,57.855,2.82 c-15.693-22.238-46.847-29.497-70.794-15.663c-25.328,14.631-33.433,47.561-18.802,72.888c4.04,6.993,9.388,12.657,15.541,16.895 c-0.915-1.299-1.788-2.644-2.602-4.052C79.385,371.89,87.49,338.96,112.818,324.329z"
/>
<path
transform="translate(0, 768)"
style="opacity: 0.06; fill: rgb(4, 0, 0)"
d="M349.701,282.093c3.725-36.649,36.272-64.305,72.921-60.579 c12.217,1.242,23.415,5.824,32.783,12.735c-11.007-14.534-27.694-24.73-46.893-26.682c-36.649-3.725-69.196,23.93-72.921,60.579 c-2.465,24.247,8.316,46.261,26.506,59.464C352.777,315.06,347.969,299.128,349.701,282.093z"
/>
<path
transform="translate(0, 768)"
style="opacity: 0.1; fill: rgb(4, 0, 0)"
d="M254.81,381.707c-105.358,0-198.419-52.064-254.72-131.654 c-2.703,99.72,55.552,194.334,153.936,236.742c128.773,55.507,279.648,1.534,335.155-127.239 c15.267-35.419,21.657-72.747,20.288-109.416C453.162,329.68,360.13,381.707,254.81,381.707z"
/>
</g>
<!-- From: https://www.svgrepo.com/svg/484356/sun -->
<g class="dark-mode-svg">
<path
style="fill: rgb(191, 182, 30)"
d="M258.373,448.122c-11.783,0-21.337,1.395-21.337,18.136c0,8.131,9.553,45.742,21.337,45.742 c11.784,0,21.336-37.611,21.336-45.742C279.709,449.518,270.156,448.122,258.373,448.122z"
/>
<path
style="fill: rgb(191, 182, 30)"
d="M352.653,422.86c-10.205,5.891-17.78,11.876-9.41,26.374c4.065,7.041,31.144,34.837,41.349,28.945 c10.205-5.892-0.328-43.241-4.393-50.282C371.829,413.4,362.858,416.968,352.653,422.86z"
/>
<path
style="fill: rgb(191, 182, 30)"
d="M448.046,344.432c-14.498-8.37-20.483-0.795-26.375,9.41c-5.892,10.205-9.46,19.176,5.038,27.546 c7.041,4.065,44.39,14.598,50.282,4.393C482.883,375.576,455.087,348.497,448.046,344.432z"
/>
<path
style="fill: rgb(191, 182, 30)"
d="M465.07,238.225c-16.741,0-18.136,9.553-18.136,21.337c0,11.784,1.396,21.336,18.136,21.336 c8.13,0,45.742-9.553,45.742-21.336C510.812,247.777,473.2,238.225,465.07,238.225z"
/>
<path
style="fill: rgb(191, 182, 30)"
d="M426.71,137.735c-14.498,8.37-10.93,17.341-5.038,27.546c5.892,10.204,11.877,17.78,26.375,9.41 c7.041-4.065,34.837-31.144,28.945-41.349C471.099,123.137,433.75,133.67,426.71,137.735z"
/>
<path
style="fill: rgb(191, 182, 30)"
d="M164.092,422.86c-10.205-5.892-19.176-9.46-27.546,5.038c-4.065,7.041-14.598,44.39-4.393,50.282 c10.205,5.892,37.283-21.904,41.349-28.945C181.872,434.737,174.297,428.752,164.092,422.86z"
/>
<path
style="fill: rgb(191, 182, 30)"
d="M424.226,259.561c0-45.799-18.564-87.263-48.577-117.276L141.097,376.837 c30.013,30.013,71.477,48.578,117.276,48.578C349.971,425.415,424.226,351.159,424.226,259.561z"
/>
<path
style="fill: rgb(198, 186, 86)"
d="M164.11,96.239c-10.143,5.855-19.05,9.401-27.297-4.618c-0.082-0.083-0.165-0.247-0.248-0.412 c-4.123-7.009-14.596-44.367-4.453-50.305c7.669-4.454,25.07,10.308,34.719,20.781c3.298,3.464,5.69,6.433,6.68,8.164 C181.84,84.364,174.336,90.384,164.11,96.239z"
/>
<path
style="fill: rgb(198, 186, 86)"
d="M279.729,52.861v0.577c-0.248,16.164-9.732,17.566-21.359,17.566 c-9.319,0-17.236-0.907-20.122-9.483c-0.824-2.227-1.237-5.113-1.237-8.66c0-5.03,3.629-21.276,9.154-32.987 c3.546-7.257,7.752-12.782,12.205-12.782c1.319,0,2.639,0.495,3.876,1.402C272.225,15.174,279.729,45.604,279.729,52.861z"
/>
<path
style="fill: rgb(198, 186, 86)"
d="M95.085,165.264c-5.938,10.226-11.875,17.813-26.39,9.401 c-3.958-2.227-14.432-11.793-21.854-21.524c-0.082-0.083-0.165-0.165-0.165-0.248c-5.69-7.504-9.484-15.091-6.928-19.545 c5.938-10.226,43.213,0.33,50.305,4.371c1.237,0.742,2.391,1.484,3.381,2.226C103.909,147.699,100.445,155.945,95.085,165.264z"
/>
<path
style="fill: rgb(198, 186, 86)"
d="M69.85,259.524c0,11.546-1.32,21.03-17.236,21.359h-0.907c-7.834,0-43.13-8.907-45.605-20.122 c-0.082,0-0.082,0-0.082,0c0-0.412-0.083-0.824-0.083-1.237c0-4.536,5.69-8.824,13.112-12.205 c11.711-5.525,27.709-9.071,32.657-9.071c4.701,0,8.164,0.742,10.721,2.062C69.108,243.773,69.85,251.113,69.85,259.524z"
/>
<path
style="fill: rgb(198, 186, 86)"
d="M68.7,344.432c-7.041,4.065-34.837,31.144-28.945,41.349c5.892,10.205,43.241-0.328,50.281-4.393 c14.498-8.37,10.93-17.341,5.038-27.546C89.183,343.637,83.197,336.062,68.7,344.432z"
/>
<path
style="fill: rgb(198, 186, 86)"
d="M352.653,96.263c10.205,5.892,19.176,9.46,27.546-5.038c4.065-7.041,14.598-44.39,4.393-50.282 c-10.205-5.892-37.284,21.904-41.349,28.945C334.873,84.386,342.448,90.371,352.653,96.263z"
/>
<path
style="fill: rgb(198, 186, 86)"
d="M258.373,93.708c-91.598,0-165.853,74.255-165.853,165.853 c0,45.799,18.563,87.262,48.577,117.276l234.552-234.552C345.635,112.271,304.172,93.708,258.373,93.708z"
/>
<path
style="fill: rgb(239, 231, 72)"
d="M252.408,440.964c-11.783,0-21.337,1.395-21.337,18.136c0,8.131,9.553,45.742,21.337,45.742 s21.336-37.611,21.336-45.742C273.744,442.36,264.191,440.964,252.408,440.964z"
/>
<path
style="fill: rgb(239, 231, 72)"
d="M346.688,415.702c-10.205,5.892-17.78,11.877-9.41,26.375c4.065,7.041,31.144,34.837,41.349,28.945 c10.205-5.892-0.328-43.241-4.393-50.282C365.864,406.242,356.893,409.81,346.688,415.702z"
/>
<path
style="fill: rgb(239, 231, 72)"
d="M442.081,337.274c-14.498-8.37-20.483-0.795-26.375,9.41c-5.892,10.205-9.46,19.176,5.038,27.546 c7.041,4.065,44.39,14.598,50.282,4.393C476.918,368.418,449.122,341.339,442.081,337.274z"
/>
<path
style="fill: rgb(239, 231, 72)"
d="M459.105,231.066c-16.741,0-18.136,9.553-18.136,21.337c0,11.784,1.395,21.336,18.136,21.336 c8.13,0,45.742-9.553,45.742-21.336C504.846,240.619,467.235,231.066,459.105,231.066z"
/>
<path
style="fill: rgb(239, 231, 72)"
d="M420.744,130.577c-14.497,8.37-10.93,17.341-5.038,27.546c5.892,10.205,11.877,17.78,26.375,9.41 c7.041-4.065,34.837-31.144,28.945-41.349C465.134,115.979,427.785,126.511,420.744,130.577z"
/>
<path
style="fill: rgb(239, 231, 72)"
d="M158.127,415.702c-10.205-5.892-19.176-9.46-27.546,5.038c-4.065,7.041-14.598,44.39-4.392,50.282 c10.205,5.892,37.283-21.904,41.349-28.945C175.907,427.578,168.332,421.594,158.127,415.702z"
/>
<path
style="fill: rgb(239, 231, 72)"
d="M418.261,252.403c0-45.799-18.564-87.263-48.577-117.276L135.132,369.679 c30.014,30.013,71.477,48.578,117.276,48.578C344.006,418.257,418.261,344.001,418.261,252.403z"
/>
<path
style="fill: rgb(250, 242, 175)"
d="M158.09,89.065c-7.67,4.453-14.679,7.587-21.277,2.557c-2.144-1.567-4.206-4.041-6.268-7.587 c-4.041-7.01-14.597-44.367-4.371-50.223c9.814-5.69,34.967,19.545,40.657,27.874c0.33,0.412,0.577,0.742,0.742,1.072 C175.903,77.189,168.316,83.209,158.09,89.065z"
/>
<path
style="fill: rgb(250, 242, 175)"
d="M273.709,45.687c0,0.577,0,1.155-0.083,1.65c-0.577,15.174-9.814,16.493-21.194,16.493 c-4.288,0-8.247-0.165-11.628-1.237c-0.907-0.247-1.732-0.659-2.556-1.072c-2.722-1.402-4.866-3.711-6.02-7.422 c-0.083-0.083,0-0.083,0-0.083c-0.742-2.227-1.155-5.03-1.155-8.329c0-4.865,3.464-20.452,8.824-32.08 c3.216-7.01,7.175-12.617,11.381-13.442C251.69,0.083,252.02,0,252.432,0c3.547,0,6.927,3.463,9.814,8.494 c0.99,1.649,1.897,3.464,2.804,5.443C270.328,25.482,273.709,40.904,273.709,45.687z"
/>
<path
style="fill: rgb(250, 242, 175)"
d="M89.147,158.09c-5.937,10.226-11.875,17.813-26.39,9.484c-2.969-1.732-9.648-7.505-15.916-14.432 c-0.082-0.083-0.165-0.165-0.165-0.248c-8.577-9.401-16.246-20.864-12.865-26.719c5.855-10.226,43.213,0.33,50.222,4.371 c5.195,3.051,8.164,6.185,9.401,9.401C95.662,145.637,92.858,151.658,89.147,158.09z"
/>
<path
style="fill: rgb(250, 242, 175)"
d="M63.83,252.432c0,11.793-1.402,21.277-18.142,21.277H45.44c-5.03-0.083-20.122-3.382-31.503-8.577 c-2.886-1.402-5.608-2.804-7.835-4.371c-0.082,0-0.082,0-0.082,0C2.309,258.205,0,255.401,0,252.432c0-0.33,0-0.742,0.165-1.072 c0.742-4.041,5.69-7.669,12.04-10.886c11.793-5.608,28.451-9.401,33.482-9.401c1.897,0,3.629,0.165,5.196,0.412 c6.762,0.99,9.978,4.288,11.545,8.824c0.412,1.072,0.66,2.309,0.825,3.546C63.748,246.412,63.83,249.381,63.83,252.432z"
/>
<path
style="fill: rgb(250, 242, 175)"
d="M84.034,374.237c-5.196,3.051-27.379,9.649-40.739,8.576c-1.567-0.083-2.969-0.33-4.288-0.742 c-2.392-0.66-4.206-1.815-5.195-3.464c-0.908-1.567-1.072-3.629-0.577-5.855c2.804-12.206,23.503-32.08,29.523-35.461 c2.969-1.732,5.608-2.804,7.917-3.216c7.917-1.732,12.453,2.969,16.659,9.566c0.577,0.99,1.237,1.979,1.814,3.051 c2.062,3.711,3.959,7.175,4.701,10.556C95.25,363.268,93.353,368.876,84.034,374.237z"
/>
<path
style="fill: rgb(250, 242, 175)"
d="M346.688,89.104c10.205,5.892,19.176,9.46,27.546-5.038c4.065-7.041,14.598-44.39,4.393-50.282 c-10.205-5.892-37.284,21.904-41.349,28.945C328.908,77.228,336.483,83.213,346.688,89.104z"
/>
<path
style="fill: rgb(250, 242, 175)"
d="M369.701,135.164l-0.743,0.742l-3.381,3.381L135.164,369.701 c-2.474-2.474-4.783-4.948-7.092-7.587c-6.185-7.009-11.793-14.514-16.741-22.513c-15.668-25.318-24.74-55.171-24.74-87.168 c0-91.621,74.221-165.842,165.842-165.842c29.936,0,58.057,7.917,82.385,21.936C347.6,115.784,359.31,124.773,369.701,135.164z"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

6
src/assets/github.svg Normal file
View file

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512" aria-description="GitHub icon">
<!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->
<path
d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

6
src/assets/linkedin.svg Normal file
View file

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" aria-description="LinkedIn icon">
<!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->
<path
d="M416 32H31.9C14.3 32 0 46.5 0 64.3v383.4C0 465.5 14.3 480 31.9 480H416c17.6 0 32-14.5 32-32.3V64.3c0-17.8-14.4-32.3-32-32.3zM135.4 416H69V202.2h66.5V416zm-33.2-243c-21.3 0-38.5-17.3-38.5-38.5S80.9 96 102.2 96c21.2 0 38.5 17.3 38.5 38.5 0 21.3-17.2 38.5-38.5 38.5zm282.1 243h-66.4V312c0-24.8-.5-56.7-34.5-56.7-34.6 0-39.9 27-39.9 54.9V416h-66.4V202.2h63.7v29.2h.9c8.9-16.8 30.6-34.5 62.9-34.5 67.2 0 79.7 44.3 79.7 101.9V416z"
/>
</svg>

After

Width:  |  Height:  |  Size: 698 B

6
src/assets/mastodon.svg Normal file
View file

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" aria-description="Mastodon icon">
<!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->
<path
d="M433 179.1c0-97.2-63.7-125.7-63.7-125.7-62.5-28.7-228.6-28.4-290.5 0 0 0-63.7 28.5-63.7 125.7 0 115.7-6.6 259.4 105.6 289.1 40.5 10.7 75.3 13 103.3 11.4 50.8-2.8 79.3-18.1 79.3-18.1l-1.7-36.9s-36.3 11.4-77.1 10.1c-40.4-1.4-83-4.4-89.6-54a102.5 102.5 0 0 1 -.9-13.9c85.6 20.9 158.7 9.1 178.8 6.7 56.1-6.7 105-41.3 111.2-72.9 9.8-49.8 9-121.5 9-121.5zm-75.1 125.2h-46.6v-114.2c0-49.7-64-51.6-64 6.9v62.5h-46.3V197c0-58.5-64-56.6-64-6.9v114.2H90.2c0-122.1-5.2-147.9 18.4-175 25.9-28.9 79.8-30.8 103.8 6.1l11.6 19.5 11.6-19.5c24.1-37.1 78.1-34.8 103.8-6.1 23.7 27.3 18.4 53 18.4 175z"
/>
</svg>

After

Width:  |  Height:  |  Size: 853 B

View file

Before

Width:  |  Height:  |  Size: 166 KiB

After

Width:  |  Height:  |  Size: 166 KiB

Before After
Before After

14
src/entry-client.tsx Normal file
View file

@ -0,0 +1,14 @@
// Entry point for development
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import Body from "./Body";
import Head from "./Head";
if (!import.meta.env.SSR) {
createRoot(document.getElementById("root")!).render(
<StrictMode>
<Head />
<Body />
</StrictMode>,
);
}

22
src/entry-server.tsx Normal file
View file

@ -0,0 +1,22 @@
// Entry point for SSG
import { renderToString } from "react-dom/server";
import Body from "./Body";
import Head from "./Head";
import style from "./style.css?inline";
export const render = () => {
const html =
"<!doctype html>" +
renderToString(
<html lang="en">
<head>
<Head />
<style type="text/css">{style}</style>
</head>
<body>
<Body />
</body>
</html>,
);
return { html };
};

202
style.css → src/style.css Executable file → Normal file
View file

@ -1,22 +1,51 @@
:root {
font-family:
system-ui,
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
Roboto,
Oxygen,
Ubuntu,
Cantarell,
"Open Sans",
"Helvetica Neue",
sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
:root { :root {
font-size: 1.5rem; font-size: 1.5rem;
color-scheme: light dark;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
/* Material Light (https://github.com/dexpota/kitty-themes/blob/master/themes/Material.conf) */ /* Material Light (https://github.com/dexpota/kitty-themes/blob/master/themes/Material.conf) */
--light-color0: #212121; --light-color0: #212121;
--light-color8: #424242; --light-color8: #424242;
--light-color1: #b7141e; --light-color1: #b7141e;
--light-color9: #e83a3f; --light-color9: #e83a3f;
--light-color2: #457b23; --light-color2: #457b23;
--light-color10: #7aba39; --light-color10: #7aba39;
--light-color3: #f5971d; --light-color3: #f5971d;
--light-color11: #fee92e; --light-color11: #fee92e;
--light-color4: #134eb2; --light-color4: #134eb2;
--light-color12: #53a4f3; --light-color12: #53a4f3;
--light-color5: #550087; --light-color5: #550087;
--light-color13: #a94dbb; --light-color13: #a94dbb;
--light-color6: #0e707c; --light-color6: #0e707c;
--light-color14: #26bad1; --light-color14: #26bad1;
--light-color7: #eeeeee; --light-color7: #eeeeee;
--light-color15: #d8d8d8; --light-color15: #d8d8d8;
--light-background: #eaeaea; --light-background: #eaeaea;
--light-foreground: #222221; --light-foreground: #222221;
@ -24,21 +53,21 @@
--light-toggle-transform: 16px; --light-toggle-transform: 16px;
/* Default kitty theme (https://sw.kovidgoyal.net/kitty/conf/#the-color-table) */ /* Default kitty theme (https://sw.kovidgoyal.net/kitty/conf/#the-color-table) */
--dark-color0: #000000; --dark-color0: #000000;
--dark-color8: #767676; --dark-color8: #767676;
--dark-color1: #cc0403; --dark-color1: #cc0403;
--dark-color9: #f2201f; --dark-color9: #f2201f;
--dark-color2: #19cb00; --dark-color2: #19cb00;
--dark-color10: #23fd00; --dark-color10: #23fd00;
--dark-color3: #cecb00; --dark-color3: #cecb00;
--dark-color11: #fffd00; --dark-color11: #fffd00;
--dark-color4: #0d73cc; --dark-color4: #0d73cc;
--dark-color12: #1a8fff; --dark-color12: #1a8fff;
--dark-color5: #cb1ed1; --dark-color5: #cb1ed1;
--dark-color13: #fd28ff; --dark-color13: #fd28ff;
--dark-color6: #0dcdcd; --dark-color6: #0dcdcd;
--dark-color14: #14ffff; --dark-color14: #14ffff;
--dark-color7: #dddddd; --dark-color7: #dddddd;
--dark-color15: #ffffff; --dark-color15: #ffffff;
--dark-foreground: #dddddd; --dark-foreground: #dddddd;
--dark-background: #000000; --dark-background: #000000;
@ -46,40 +75,40 @@
--dark-toggle-transform: -768px; --dark-toggle-transform: -768px;
/* Default to light */ /* Default to light */
--main-color0: var(--light-color0); --main-color0: var(--light-color0);
--main-color8: var(--light-color8); --main-color8: var(--light-color8);
--main-color1: var(--light-color1); --main-color1: var(--light-color1);
--main-color9: var(--light-color9); --main-color9: var(--light-color9);
--main-color2: var(--light-color2); --main-color2: var(--light-color2);
--main-color10: var(--light-color10); --main-color10: var(--light-color10);
--main-color3: var(--light-color3); --main-color3: var(--light-color3);
--main-color11: var(--light-color11); --main-color11: var(--light-color11);
--main-color4: var(--light-color4); --main-color4: var(--light-color4);
--main-color12: var(--light-color12); --main-color12: var(--light-color12);
--main-color5: var(--light-color5); --main-color5: var(--light-color5);
--main-color13: var(--light-color13); --main-color13: var(--light-color13);
--main-color6: var(--light-color6); --main-color6: var(--light-color6);
--main-color14: var(--light-color14); --main-color14: var(--light-color14);
--main-color7: var(--light-color7); --main-color7: var(--light-color7);
--main-color15: var(--light-color15); --main-color15: var(--light-color15);
--main-foreground: var(--light-foreground); --main-foreground: var(--light-foreground);
--main-background: var(--light-background); --main-background: var(--light-background);
--main-toggle-transform: var(--light-toggle-transform); --main-toggle-transform: var(--light-toggle-transform);
--alt-color0: var(--dark-color0); --alt-color0: var(--dark-color0);
--alt-color8: var(--dark-color8); --alt-color8: var(--dark-color8);
--alt-color1: var(--dark-color1); --alt-color1: var(--dark-color1);
--alt-color9: var(--dark-color9); --alt-color9: var(--dark-color9);
--alt-color2: var(--dark-color2); --alt-color2: var(--dark-color2);
--alt-color10: var(--dark-color10); --alt-color10: var(--dark-color10);
--alt-color3: var(--dark-color3); --alt-color3: var(--dark-color3);
--alt-color11: var(--dark-color11); --alt-color11: var(--dark-color11);
--alt-color4: var(--dark-color4); --alt-color4: var(--dark-color4);
--alt-color12: var(--dark-color12); --alt-color12: var(--dark-color12);
--alt-color5: var(--dark-color5); --alt-color5: var(--dark-color5);
--alt-color13: var(--dark-color13); --alt-color13: var(--dark-color13);
--alt-color6: var(--dark-color6); --alt-color6: var(--dark-color6);
--alt-color14: var(--dark-color14); --alt-color14: var(--dark-color14);
--alt-color7: var(--dark-color7); --alt-color7: var(--dark-color7);
--alt-color15: var(--dark-color15); --alt-color15: var(--dark-color15);
--alt-foreground: var(--dark-foreground); --alt-foreground: var(--dark-foreground);
--alt-background: var(--dark-background); --alt-background: var(--dark-background);
@ -88,40 +117,40 @@
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
:root { :root {
--main-color0: var(--dark-color0); --main-color0: var(--dark-color0);
--main-color8: var(--dark-color8); --main-color8: var(--dark-color8);
--main-color1: var(--dark-color1); --main-color1: var(--dark-color1);
--main-color9: var(--dark-color9); --main-color9: var(--dark-color9);
--main-color2: var(--dark-color2); --main-color2: var(--dark-color2);
--main-color10: var(--dark-color10); --main-color10: var(--dark-color10);
--main-color3: var(--dark-color3); --main-color3: var(--dark-color3);
--main-color11: var(--dark-color11); --main-color11: var(--dark-color11);
--main-color4: var(--dark-color4); --main-color4: var(--dark-color4);
--main-color12: var(--dark-color12); --main-color12: var(--dark-color12);
--main-color5: var(--dark-color5); --main-color5: var(--dark-color5);
--main-color13: var(--dark-color13); --main-color13: var(--dark-color13);
--main-color6: var(--dark-color6); --main-color6: var(--dark-color6);
--main-color14: var(--dark-color14); --main-color14: var(--dark-color14);
--main-color7: var(--dark-color7); --main-color7: var(--dark-color7);
--main-color15: var(--dark-color15); --main-color15: var(--dark-color15);
--main-foreground: var(--dark-foreground); --main-foreground: var(--dark-foreground);
--main-background: var(--dark-background); --main-background: var(--dark-background);
--main-toggle-transform: var(--dark-toggle-transform); --main-toggle-transform: var(--dark-toggle-transform);
--alt-color0: var(--light-color0); --alt-color0: var(--light-color0);
--alt-color8: var(--light-color8); --alt-color8: var(--light-color8);
--alt-color1: var(--light-color1); --alt-color1: var(--light-color1);
--alt-color9: var(--light-color9); --alt-color9: var(--light-color9);
--alt-color2: var(--light-color2); --alt-color2: var(--light-color2);
--alt-color10: var(--light-color10); --alt-color10: var(--light-color10);
--alt-color3: var(--light-color3); --alt-color3: var(--light-color3);
--alt-color11: var(--light-color11); --alt-color11: var(--light-color11);
--alt-color4: var(--light-color4); --alt-color4: var(--light-color4);
--alt-color12: var(--light-color12); --alt-color12: var(--light-color12);
--alt-color5: var(--light-color5); --alt-color5: var(--light-color5);
--alt-color13: var(--light-color13); --alt-color13: var(--light-color13);
--alt-color6: var(--light-color6); --alt-color6: var(--light-color6);
--alt-color14: var(--light-color14); --alt-color14: var(--light-color14);
--alt-color7: var(--light-color7); --alt-color7: var(--light-color7);
--alt-color15: var(--light-color15); --alt-color15: var(--light-color15);
--alt-foreground: var(--light-foreground); --alt-foreground: var(--light-foreground);
--alt-background: var(--light-background); --alt-background: var(--light-background);
@ -131,21 +160,21 @@
:root:has(#dark-mode-toggle:not(:checked)) { :root:has(#dark-mode-toggle:not(:checked)) {
/* Default to light */ /* Default to light */
--color0: var(--main-color0) !important; --color0: var(--main-color0) !important;
--color8: var(--main-color8) !important; --color8: var(--main-color8) !important;
--color1: var(--main-color1) !important; --color1: var(--main-color1) !important;
--color9: var(--main-color9) !important; --color9: var(--main-color9) !important;
--color2: var(--main-color2) !important; --color2: var(--main-color2) !important;
--color10: var(--main-color10) !important; --color10: var(--main-color10) !important;
--color3: var(--main-color3) !important; --color3: var(--main-color3) !important;
--color11: var(--main-color11) !important; --color11: var(--main-color11) !important;
--color4: var(--main-color4) !important; --color4: var(--main-color4) !important;
--color12: var(--main-color12) !important; --color12: var(--main-color12) !important;
--color5: var(--main-color5) !important; --color5: var(--main-color5) !important;
--color13: var(--main-color13) !important; --color13: var(--main-color13) !important;
--color6: var(--main-color6) !important; --color6: var(--main-color6) !important;
--color14: var(--main-color14) !important; --color14: var(--main-color14) !important;
--color7: var(--main-color7) !important; --color7: var(--main-color7) !important;
--color15: var(--main-color15) !important; --color15: var(--main-color15) !important;
--foreground: var(--main-foreground) !important; --foreground: var(--main-foreground) !important;
--background: var(--main-background) !important; --background: var(--main-background) !important;
@ -159,21 +188,21 @@
} }
:root:has(#dark-mode-toggle:checked) { :root:has(#dark-mode-toggle:checked) {
--color0: var(--alt-color0) !important; --color0: var(--alt-color0) !important;
--color8: var(--alt-color8) !important; --color8: var(--alt-color8) !important;
--color1: var(--alt-color1) !important; --color1: var(--alt-color1) !important;
--color9: var(--alt-color9) !important; --color9: var(--alt-color9) !important;
--color2: var(--alt-color2) !important; --color2: var(--alt-color2) !important;
--color10: var(--alt-color10) !important; --color10: var(--alt-color10) !important;
--color3: var(--alt-color3) !important; --color3: var(--alt-color3) !important;
--color11: var(--alt-color11) !important; --color11: var(--alt-color11) !important;
--color4: var(--alt-color4) !important; --color4: var(--alt-color4) !important;
--color12: var(--alt-color12) !important; --color12: var(--alt-color12) !important;
--color5: var(--alt-color5) !important; --color5: var(--alt-color5) !important;
--color13: var(--alt-color13) !important; --color13: var(--alt-color13) !important;
--color6: var(--alt-color6) !important; --color6: var(--alt-color6) !important;
--color14: var(--alt-color14) !important; --color14: var(--alt-color14) !important;
--color7: var(--alt-color7) !important; --color7: var(--alt-color7) !important;
--color15: var(--alt-color15) !important; --color15: var(--alt-color15) !important;
--foreground: var(--alt-foreground) !important; --foreground: var(--alt-foreground) !important;
--background: var(--alt-background) !important; --background: var(--alt-background) !important;
@ -187,7 +216,6 @@
} }
body { body {
/* Actual useful styles */
--highlight: var(--color6); --highlight: var(--color6);
color: var(--foreground); color: var(--foreground);
background: var(--background); background: var(--background);
@ -201,12 +229,13 @@ body {
align-items: center; align-items: center;
justify-content: space-evenly; justify-content: space-evenly;
margin: 0; margin: 0;
font-family: sans-serif;
--anim-duration: 0.25s; --anim-duration: 0.25s;
@media (prefers-reduced-motion: no-preference) { @media (prefers-reduced-motion: no-preference) {
transition: color var(--anim-duration) ease, background-color var(--anim-duration) ease; transition:
color var(--anim-duration) ease,
background-color var(--anim-duration) ease;
} }
} }
@ -289,7 +318,7 @@ p {
.blocks { .blocks {
letter-spacing: -1px; letter-spacing: -1px;
line-height: 1lh; line-height: 0.9lh;
} }
br { br {
@ -297,7 +326,7 @@ br {
margin: 0; margin: 0;
line-height: calc(1lh - 1px); line-height: calc(1lh - 1px);
:after { :after {
content: '\0200B'; content: "\0200B";
} }
} }
@ -344,6 +373,9 @@ span {
cursor: grab; cursor: grab;
transition-duration: var(--anim-duration); transition-duration: var(--anim-duration);
svg {
transition: all var(--anim-duration) ease;
}
svg:hover { svg:hover {
filter: brightness(85%); filter: brightness(85%);
} }

34
src/vite-env.d.ts vendored Normal file
View file

@ -0,0 +1,34 @@
/// <reference types="vite/client" />
/// <reference types="vite-plugin-svgr/client" />
// Imagetools types inspired by: https://github.com/JonasKruckenberg/imagetools/issues/160
// These only work for my specific imports
declare module "*as=metadata" {
interface Metadata {
src: string;
width: number;
height: number;
format: string;
}
const output: Metadata;
export default output;
}
declare module "*as=picture" {
type Primaries = {
avif: string;
// jxl: string; // Broken right now
webp: string;
};
type Fallback = {
src: string;
w: number;
h: number;
};
type Picture = {
sources: Primaries;
img: Fallback;
};
const output: Picture;
export default output;
}

26
tsconfig.app.json Normal file
View file

@ -0,0 +1,26 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src"]
}

7
tsconfig.json Normal file
View file

@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}

24
tsconfig.node.json Normal file
View file

@ -0,0 +1,24 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}

25
vite.config.ts Normal file
View file

@ -0,0 +1,25 @@
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
import { createHtmlPlugin } from "vite-plugin-html";
import { imagetools } from "vite-imagetools";
import svgr from "vite-plugin-svgr";
export default defineConfig({
base: "https://mmbradley.ca/",
clearScreen: false,
plugins: [
createHtmlPlugin({
minify: true,
}),
imagetools(),
react(),
svgr({
svgrOptions: {
plugins: ["@svgr/plugin-svgo", "@svgr/plugin-jsx"],
svgoConfig: {
floatPrecision: 2,
},
},
}),
],
});

2300
yarn.lock Normal file

File diff suppressed because it is too large Load diff