Initial portfolio site: Astro + Tailwind MVP

Outcome-led hero, about, grouped skills, experience summary, featured
projects + /projects index, static contact, SEO/OG, dark/light theme.
Dockerfile + nginx config + build script for homelab deploy.
This commit is contained in:
2026-06-17 16:22:53 +10:00
commit 2d4b6ea097
38 changed files with 8232 additions and 0 deletions
+35
View File
@@ -0,0 +1,35 @@
---
import Layout from "../layouts/Layout.astro";
---
<Layout title="404 — Not found" path="/404">
<section class="nf">
<div class="container nf__inner">
<p class="eyebrow">Error 404</p>
<h1 class="nf__code mono">404</h1>
<p class="nf__msg">
This route returned no endpoint. The page you're looking for isn't here.
</p>
<a class="btn btn--primary" href="/">← Back home</a>
</div>
</section>
</Layout>
<style>
.nf {
min-height: 64vh;
display: grid;
place-items: center;
text-align: center;
}
.nf__code {
font-size: var(--step-5);
color: var(--accent);
margin-block: var(--space-3);
}
.nf__msg {
color: var(--text-dim);
max-width: 38ch;
margin: 0 auto var(--space-6);
}
</style>
+55
View File
@@ -0,0 +1,55 @@
---
import Layout from "../layouts/Layout.astro";
import Hero from "../components/Hero.astro";
import Section from "../components/Section.astro";
import About from "../components/About.astro";
import SkillGroup from "../components/SkillGroup.astro";
import ProjectCard from "../components/ProjectCard.astro";
import Contact from "../components/Contact.astro";
import { skills } from "../data/skills";
import { featuredProjects } from "../data/projects";
---
<Layout path="/">
<Hero />
<Section id="about" eyebrow="About" index="01" title="Reliability you can reason about.">
<About />
</Section>
<Section id="skills" eyebrow="Capabilities" index="02" title="What I work with, grouped by what it's for.">
<div class="grid grid--skills">
{skills.map((group) => <SkillGroup group={group} />)}
</div>
</Section>
<Section id="projects" eyebrow="Selected work" index="03" title="Platforms built to keep working unattended.">
<div class="grid grid--projects">
{featuredProjects.map((project) => <ProjectCard project={project} />)}
</div>
<p class="projects__more" data-reveal>
<a class="btn" href="/projects">All projects →</a>
</p>
</Section>
<Section id="contact" eyebrow="Get in touch" index="04" title="Let's talk infrastructure.">
<Contact />
</Section>
</Layout>
<style>
.grid {
display: grid;
gap: var(--space-5);
}
.grid--skills {
grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
}
.grid--projects {
grid-template-columns: repeat(auto-fit, minmax(min(100%, 19rem), 1fr));
}
.projects__more {
margin-top: var(--space-6);
text-align: center;
}
</style>
+30
View File
@@ -0,0 +1,30 @@
---
import Layout from "../../layouts/Layout.astro";
import Section from "../../components/Section.astro";
import ProjectCard from "../../components/ProjectCard.astro";
import { projects } from "../../data/projects";
---
<Layout title="Projects" path="/projects" description="Selected platform & infrastructure projects by Jonathon Wright.">
<Section id="all-projects" eyebrow="Projects" index="*" title="Everything I've built worth writing about.">
<p class="lead projects__intro" data-reveal>
Edge Kubernetes, GPU inference, self-hosted AI, and the automation that ties
it together. Full case studies are on the way.
</p>
<div class="grid">
{projects.map((project) => <ProjectCard project={project} />)}
</div>
</Section>
</Layout>
<style>
.projects__intro {
max-width: 50ch;
margin-bottom: var(--space-6);
}
.grid {
display: grid;
gap: var(--space-5);
grid-template-columns: repeat(auto-fit, minmax(min(100%, 19rem), 1fr));
}
</style>