initial commit

This commit is contained in:
Josh Bairstow 2026-06-01 19:35:56 +10:00
commit 3ba76b4f02
90 changed files with 12507 additions and 0 deletions

233
src/pages/blog/index.astro Normal file
View file

@ -0,0 +1,233 @@
---
import BaseLayout from "~/layouts/BaseLayout.astro";
import Eyebrow from "~/components/atoms/Eyebrow.astro";
import Mark from "~/components/atoms/Mark.astro";
import PostRow from "~/components/molecules/PostRow.astro";
import YearHead from "~/components/molecules/YearHead.astro";
import InkArrow from "~/components/atoms/InkArrow.astro";
import { blog } from "~/lib/urls";
import { getCollection } from "astro:content";
const posts = await getCollection("posts", ({ data }) => !data.draft);
posts.sort((a, b) => b.data.date.getTime() - a.data.date.getTime());
const [featured, ...rest] = posts;
const dateFmtMonthYear = new Intl.DateTimeFormat("en-AU", { month: "short", year: "numeric" });
const dateFmtMonthYearLong = new Intl.DateTimeFormat("en-AU", { month: "long", year: "numeric" });
// Group the archive by year.
const byYear = new Map<number, typeof rest>();
for (const post of rest) {
const year = post.data.date.getFullYear();
const bucket = byYear.get(year) ?? [];
bucket.push(post);
byYear.set(year, bucket);
}
const yearGroups = Array.from(byYear.entries()).sort(([a], [b]) => b - a);
---
<BaseLayout
title="Writing — Josh Bairstow"
description="Short pieces on craft, the ocean, and the quiet discipline of making small software."
current="blog"
>
<Fragment slot="marks">
<Mark kind="dots" width={54} top="18%" right="4%" />
<Mark kind="concentric" width={40} top="64%" left="3%" rotate={6} />
<Mark kind="pipes" width={26} bottom="12%" right="9%" />
<Mark kind="ringdots" width={72} bottom="26%" left="46%" rotate={-4} />
</Fragment>
<header class="masthead">
<div>
<Eyebrow>Journal · field notes</Eyebrow>
<h1 class="title">Writing<span style="color:var(--accent-deep)">.</span></h1>
<p class="count">{posts.length} entries · since 2021</p>
</div>
<p class="blurb">
Short pieces on craft, the ocean, and the quiet discipline of making small
software. Mostly written early, before the light is up.
</p>
</header>
{
featured && (
<article class="feature">
<div class="cutframe img">
<div class="grain" />
<img class="wm" src="/assets/mark-rings.svg" alt="" />
</div>
<div>
<div class="meta">
{featured.data.tag && <span class="tag">{featured.data.tag}</span>}
<span class="date">
{dateFmtMonthYearLong.format(featured.data.date)}
{featured.data.readingMinutes && ` · ${featured.data.readingMinutes} min`}
</span>
</div>
<h2>
<a href={blog(`/posts/${featured.id}/`)}>{featured.data.title}</a>
</h2>
{featured.data.standfirst && <p>{featured.data.standfirst}</p>}
<a class="read" href={blog(`/posts/${featured.id}/`)}>
<span class="u">Read the piece</span>
<InkArrow />
</a>
</div>
</article>
)
}
<section class="archive">
{
yearGroups.map(([year, items]) => (
<>
<YearHead year={String(year)} count={items.length} />
{items.map((post) => (
<PostRow
date={dateFmtMonthYear.format(post.data.date)}
title={post.data.title}
href={blog(`/posts/${post.id}/`)}
deck={post.data.standfirst}
tag={post.data.tag}
/>
))}
</>
))
}
</section>
</BaseLayout>
<style>
.masthead {
display: grid;
grid-template-columns: 1.1fr 0.9fr;
gap: clamp(30px, 6vw, 80px);
align-items: end;
padding: clamp(40px, 8vh, 96px) 0 clamp(30px, 5vh, 52px);
}
.masthead .title {
font-family: var(--font-display);
font-weight: 500;
font-size: clamp(56px, 9vw, 132px);
line-height: 0.9;
letter-spacing: -0.03em;
margin: 12px 0 0;
color: var(--fg-on-dark-1);
}
:global(body:not(.dark)) .masthead .title { color: var(--fg-1); }
.masthead .blurb {
font-family: var(--font-body);
font-weight: 300;
font-size: clamp(15px, 1.4vw, 18px);
line-height: 1.62;
color: var(--fg-on-dark-2);
max-width: 38ch;
margin: 0 0 6px;
}
:global(body:not(.dark)) .masthead .blurb { color: var(--fg-2); }
.masthead .count {
font-family: var(--font-mono);
font-size: 11px;
letter-spacing: 0.08em;
color: var(--fg-on-dark-3);
margin-top: 16px;
font-feature-settings: "onum" 1;
}
.feature {
display: grid;
grid-template-columns: 0.92fr 1.08fr;
gap: clamp(28px, 5vw, 64px);
align-items: center;
padding: clamp(26px, 4.5vh, 48px) 0;
border-top: 1px solid var(--hairline-on-dark);
border-bottom: 1px solid var(--hairline-on-dark);
}
:global(body:not(.dark)) .feature { border-color: var(--hairline); }
.feature .img { aspect-ratio: 5 / 4; }
.meta {
display: flex;
gap: 14px;
align-items: center;
margin-bottom: 18px;
}
.tag {
font-family: var(--font-body);
font-weight: 500;
font-size: 11px;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--accent);
white-space: nowrap;
}
:global(body:not(.dark)) .tag { color: var(--accent-deep); }
.date {
font-family: var(--font-mono);
font-size: 11px;
color: var(--fg-on-dark-3);
font-feature-settings: "onum" 1;
letter-spacing: 0.04em;
}
:global(body:not(.dark)) .date { color: var(--fg-3); }
.feature h2 {
font-family: var(--font-display);
font-weight: 500;
font-size: clamp(28px, 3.4vw, 46px);
line-height: 1.04;
letter-spacing: -0.02em;
margin: 0;
color: var(--fg-on-dark-1);
text-wrap: balance;
}
:global(body:not(.dark)) .feature h2 { color: var(--fg-1); }
.feature h2 a { color: inherit; text-decoration: none; }
.feature p {
font-family: var(--font-body);
font-weight: 300;
font-size: 16.5px;
line-height: 1.62;
color: var(--fg-on-dark-2);
margin: 18px 0 0;
max-width: 48ch;
}
:global(body:not(.dark)) .feature p { color: var(--fg-2); }
.read {
display: inline-flex;
align-items: baseline;
gap: 10px;
margin-top: 24px;
font-family: var(--font-body);
font-weight: 500;
font-size: 13px;
letter-spacing: 0.04em;
text-decoration: none;
color: var(--fg-on-dark-1);
white-space: nowrap;
}
:global(body:not(.dark)) .read { color: var(--fg-1); }
.read .u {
position: relative;
padding-bottom: 3px;
}
.read .u::after {
content: "";
position: absolute;
left: 0;
bottom: 0;
height: 1px;
width: 100%;
background: var(--accent);
}
.archive {
padding: clamp(34px, 6vh, 72px) 0 0;
}
@media (max-width: 800px) {
.masthead,
.feature { grid-template-columns: 1fr; }
.feature .img { order: -1; aspect-ratio: 16 / 9; }
}
</style>