initial commit
This commit is contained in:
commit
3ba76b4f02
90 changed files with 12507 additions and 0 deletions
233
src/pages/blog/index.astro
Normal file
233
src/pages/blog/index.astro
Normal 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>
|
||||
Loading…
Add table
Add a link
Reference in a new issue