Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,23 @@
- package mgr: pnpm
- github: gh
- web automation: `agent-browser` (see `agent-browser --help`)
- use `agent-browser` for UI validation only after larger UI changes or a batch of small tweaks; skip for tiny single change if confident
- frontend tasks: use `/frontend-design`
- framework: Astro (SSG sites), React + TanStack Start if app needed

## Code style
- TS strict, no `any`, no default exports, inline `export`
- avoid new abstractions unless needed; prefer clear names over comments
- avoid helpers for trivial expressions; avoid `useEffect` unless required
- avoid helpers for trivial expressions
- no `try/catch` unless necessary
- file names: kebab-case for `.ts`/`.tsx`/`.jsx`
- Astro: use frontmatter (`---`), keep markup semantic
- formatting: Prettier (astro + tailwind plugins)

## UI + styling
- Tailwind v4 only; use built-ins; rare globals; use `cn()` in `src/utils.ts`
- Tailwind v4 only; use built-ins; rare globals
- avoid custom CSS classes/vars for typography if Tailwind utilities suffice; prefer components with inline utilities
- colors: Radix gray scale `--gray-1`…`--gray-12`
- fonts: Commit Mono (mono), Work Sans (sans)
- dark mode: `prefers-color-scheme`
- UI lib: project DS first; else `shadcn/ui` w/ Base UI

## Content
- blog: `src/content/blog/` (MD/MDX + frontmatter)
Expand Down
9 changes: 5 additions & 4 deletions src/components/ActivityGraph/BarGraph.astro
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,20 @@ type Props = {

const { activities } = Astro.props;

const normalizeValue = (value: number) => (value === 0 ? null : value);
const chartData = {
labels: activities.map((day) => formatDate(day.date)),
swimData: activities.map((day) => (day.swim ?? 0) / 1000),
runData: activities.map((day) => day.run ?? 0),
rideData: activities.map((day) => day.ride ?? 0),
swimData: activities.map((day) => normalizeValue((day.swim ?? 0) / 1000)),
runData: activities.map((day) => normalizeValue(day.run ?? 0)),
rideData: activities.map((day) => normalizeValue(day.ride ?? 0)),
};
---

<div class="scrollbar-hide relative w-full overflow-x-auto">
<canvas id="activityChart" data-chart={JSON.stringify(chartData)}></canvas>
<div
id="chartTooltip"
class="text-gray-1100 pointer-events-none absolute hidden rounded-md border border-gray-600 bg-gray-200 px-3 py-2 font-mono text-sm whitespace-nowrap"
class="text-gray-1100 border-ledger-line bg-page-bg pointer-events-none absolute hidden rounded-md border px-3 py-2 font-mono text-sm whitespace-nowrap"
>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/ActivityGraph/SummaryStats.astro
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const activeDays = activities.filter(
---

<div
class="flex flex-col gap-1 pb-6 font-mono text-sm sm:flex-row sm:justify-between"
class="flex flex-col gap-2 pb-4 font-mono text-sm sm:flex-row sm:items-center sm:justify-between"
>
<span>{activeDays} days</span>
<div class="text-gray-1100 flex gap-6 text-sm">
Expand Down
7 changes: 4 additions & 3 deletions src/components/Header.astro
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
---
import { SITE_TITLE } from "../consts";
import LedgerLabel from "./LedgerLabel.astro";
---

<header class="mb-32 flex flex-col items-start">
<a class="text-medium inline-block font-medium no-underline" href="/">
<header class="flex flex-col pb-4">
<a class="inline-block text-lg font-medium no-underline" href="/">
{SITE_TITLE}
</a>
<span class="text-gray-1100 leading-4 font-medium">Frontend Engineer</span>
<LedgerLabel as="span">Frontend Engineer</LedgerLabel>
</header>
26 changes: 26 additions & 0 deletions src/components/LedgerLabel.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
interface Props {
as?: "h2" | "span" | "div";
href?: string;
class?: string;
}

const { as: Tag = "span", href, class: className } = Astro.props;
const classes =
"text-ledger-muted font-sans text-sm font-medium uppercase tracking-wide leading-6";
const classList = [classes, className];
---

{
href ? (
<Tag class:list={classList}>
<a class="hover:text-gray-1100" href={href}>
<slot />
</a>
</Tag>
) : (
<Tag class:list={classList}>
<slot />
</Tag>
)
}
35 changes: 19 additions & 16 deletions src/components/PostCard.astro
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,25 @@ interface Props {
const { title, description, pubDate, href, featured = false } = Astro.props;
---

<li
class="group -mx-3 flex flex-col rounded-md p-3 no-underline transition-colors hover:cursor-pointer hover:bg-gray-200"
>
<a href={href} class="text-gray-1100 group-hover:text-gray-1200">
<div class="flex items-start gap-2">
<span class="flex-1 font-medium">{title}</span>
{
featured && (
<span class="text-gray-1100 shrink-0 rounded-full border border-gray-400 bg-gray-200 px-2 py-0.5 text-xs">
Featured
</span>
)
}
<li class="pt-3 pb-3 first:pt-0 last:pb-0">
<a
href={href}
class="group text-gray-1100 hover:text-gray-1200 transition-colors"
>
<div class="flex items-baseline justify-between gap-4">
<div class="flex items-baseline gap-3">
<span class="font-medium">{title}</span>
{
featured && (
<span class="text-gray-1000 border-ledger-line rounded-full border px-2 py-0.5 font-mono text-[10px] tracking-[0.18em] uppercase">
Featured
</span>
)
}
</div>
<span class="font-mono text-xs text-gray-900">
<FormattedDate date={pubDate} />
</span>
</div>
{
description && (
Expand All @@ -33,8 +39,5 @@ const { title, description, pubDate, href, featured = false } = Astro.props;
</span>
)
}
<span class="mt-2 block text-xs text-gray-900">
<FormattedDate date={pubDate} />
</span>
</a>
</li>
14 changes: 3 additions & 11 deletions src/components/SectionHeading.astro
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
---
import LedgerLabel from "./LedgerLabel.astro";

interface Props {
title: string;
href?: string;
Expand All @@ -7,14 +9,4 @@ interface Props {
const { title, href } = Astro.props;
---

<h2 class="mb-5 font-medium sm:mb-4">
{
Boolean(href) ? (
<a class="hover:text-gray-1100" href={href}>
{title}
</a>
) : (
<span>{title}</span>
)
}
</h2>
<LedgerLabel as="h2" href={href}>{title}</LedgerLabel>
2 changes: 1 addition & 1 deletion src/components/SubtleLink.astro
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const { href, target } = Astro.props;
href={href}
target={target}
rel="noopener noreferrer"
class="hover:text-gray-1200 underline transition-colors"
class="decoration-ledger-line hover:text-gray-1200 underline underline-offset-4 transition-colors"
>
<slot /></a
>
19 changes: 4 additions & 15 deletions src/layouts/BaseLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,14 @@
import Header from "../components/Header.astro";
---

<body class="mb-8 bg-gray-100 sm:mb-16">
<!-- Grid pattern background -->
<body class="bg-page-bg text-gray-1200 antialiased">
<div
class="pointer-events-none fixed inset-0 bg-[radial-gradient(var(--color-gray-600)_1px,transparent_1px)] bg-size-[16px_16px] opacity-40"
class="bg-page-bg pointer-events-none fixed top-0 left-0 z-50 h-14 w-full backdrop-blur-xl [-webkit-mask-image:linear-gradient(to_bottom,black,transparent)]"
>
</div>

<!-- Top fade -->
<div
class="pointer-events-none fixed top-0 left-0 z-50 h-12 w-full bg-gray-100 backdrop-blur-xl [-webkit-mask-image:linear-gradient(to_bottom,black,transparent)]"
>
</div>

<!-- Content -->
<div
class="text-gray-1200 relative mx-auto my-24 max-w-[692px] px-6 antialiased sm:my-16"
>
<div class="mx-auto w-full max-w-[980px] px-6 py-16 sm:py-20">
<Header />
<main>
<main class="mt-10 space-y-6">
<slot />
</main>
</div>
Expand Down
39 changes: 25 additions & 14 deletions src/layouts/BlogPost.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { CollectionEntry } from "astro:content";
import BaseHead from "../components/BaseHead.astro";
import FormattedDate from "../components/FormattedDate.astro";
import BaseLayout from "../layouts/BaseLayout.astro";
import LedgerLabel from "../components/LedgerLabel.astro";

type Props = CollectionEntry<"blog">["data"];

Expand All @@ -15,19 +16,29 @@ const { title, description, pubDate, updatedDate } = Astro.props;
</head>

<BaseLayout>
<article class="prose dark:prose-invert">
<h1 class="mb-0">{title}</h1>
<div>
<FormattedDate date={pubDate} />
{
updatedDate && (
<div class="italic">
Last updated on <FormattedDate date={updatedDate} />
</div>
)
}
</div>
<slot />
</article>
<div class="space-y-6">
<section class="grid grid-cols-[140px_1fr] gap-6">
<LedgerLabel as="div">Meta</LedgerLabel>
<div class="space-y-2">
<h1 class="text-gray-1200 text-2xl font-medium">{title}</h1>
<div class="text-gray-1100 font-mono text-sm">
<FormattedDate date={pubDate} />
</div>
{
updatedDate && (
<div class="text-gray-1100 text-sm italic">
Last updated on <FormattedDate date={updatedDate} />
</div>
)
}
</div>
</section>
<section class="grid grid-cols-[140px_1fr] gap-6">
<LedgerLabel as="div">Content</LedgerLabel>
<article class="prose dark:prose-invert">
<slot />
</article>
</section>
</div>
</BaseLayout>
</html>
93 changes: 52 additions & 41 deletions src/layouts/Talk.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { CollectionEntry } from "astro:content";
import BaseHead from "../components/BaseHead.astro";
import FormattedDate from "../components/FormattedDate.astro";
import BaseLayout from "../layouts/BaseLayout.astro";
import LedgerLabel from "../components/LedgerLabel.astro";

type Props = CollectionEntry<"talks">["data"];

Expand All @@ -16,46 +17,56 @@ const { title, description, pubDate, updatedDate, event, slides, recording } =
</head>

<BaseLayout>
<article class="prose dark:prose-invert">
<h1 class="mb-0">{title}</h1>
<div class="space-y-1">
<FormattedDate date={pubDate} />
{event && <div class="text-gray-1100 text-sm">Event: {event}</div>}
{
(slides || recording) && (
<div class="flex gap-4 text-sm">
{slides && (
<a
href={slides}
target="_blank"
rel="noopener noreferrer"
class="text-gray-1100 hover:text-gray-1200 transition-colors"
>
Slides →
</a>
)}
{recording && (
<a
href={recording}
target="_blank"
rel="noopener noreferrer"
class="text-gray-1100 hover:text-gray-1200 transition-colors"
>
Recording →
</a>
)}
</div>
)
}
{
updatedDate && (
<div class="italic">
Last updated on <FormattedDate date={updatedDate} />
</div>
)
}
</div>
<slot />
</article>
<div class="space-y-6">
<section class="grid grid-cols-[140px_1fr] gap-6">
<LedgerLabel as="div">Meta</LedgerLabel>
<div class="space-y-2">
<h1 class="text-gray-1200 text-2xl font-medium">{title}</h1>
<div class="text-gray-1100 font-mono text-sm">
<FormattedDate date={pubDate} />
</div>
{event && <div class="text-gray-1100 text-sm">Event: {event}</div>}
{
(slides || recording) && (
<div class="flex gap-4 text-sm">
{slides && (
<a
href={slides}
target="_blank"
rel="noopener noreferrer"
class="text-gray-1100 hover:text-gray-1200 transition-colors"
>
Slides →
</a>
)}
{recording && (
<a
href={recording}
target="_blank"
rel="noopener noreferrer"
class="text-gray-1100 hover:text-gray-1200 transition-colors"
>
Recording →
</a>
)}
</div>
)
}
{
updatedDate && (
<div class="text-gray-1100 text-sm italic">
Last updated on <FormattedDate date={updatedDate} />
</div>
)
}
</div>
</section>
<section class="grid grid-cols-[140px_1fr] gap-6">
<LedgerLabel as="div">Content</LedgerLabel>
<article class="prose dark:prose-invert">
<slot />
</article>
</section>
</div>
</BaseLayout>
</html>
Loading