Wordmark
Live source ↗A simple clam silhouette used as the site mark in the header and in the mobile nav disclosure. Same SVG, two states — closed and open. Scales by a size prop.
Used at:
SiteHeader.astro , OG cards
Lab
Every custom component built for this site — the ones in active use and the ones I've taken back out. Each is rendered live below, with a status, the routes it appears on, and a link to its source. New components land here in the branch that introduces them, so the lab stays current without me having to remember.
New experiment → The Costume Department — toggle the entire site into a different persona (colors, name, mark, and voice).
New experiment → Coast — full-bleed ecosystem bands joined by transitional dividers. The composition is the grammar.
New experiment → OG share cards — the link-preview card art: the wave, and coastal-band variants (water / stone / trees).
New experiment → Overlay playground — the matrix, but live: bring an image, drag the headline, watch the measured contrast update.
New experiment → The Drift — an ASMR micro-interaction garden: a scroll descent from the coastal shallows to the abyss, alive with sound, motion, and touch.
New experiment → Micro-interactions — a specimen board of hover effects: eight link patterns and two button behaviors, from the everyday underline slide to the lightsaber and the proximity magnet, each annotated with the technique that powers it.
A simple clam silhouette used as the site mark in the header and in the mobile nav disclosure. Same SVG, two states — closed and open. Scales by a size prop.
Used at:
SiteHeader.astro , OG cards
Mobile navigation as a native <details> disclosure. The Wordmark sits in the summary; opening rotates the top of the clam shell up. Zero JavaScript — the disclosure is the clam, and the clam is the disclosure.
Used at:
SiteHeader.astro (visible at < sm)
Click the clam to open.
The standard listing entry for a post. Renders a draft pill when post.data.draft is true so drafts are visible during npm run dev but not in production.
Used at:
index.astro (more-writing list) , blogs/index.astro (by year)
A single inline line of muted text — formatted date and reading time. Date format is UTC-stable to match what's in frontmatter regardless of build timezone.
Used at:
PostLayout.astro
3 min read
The 'currently building / reading / playing / listening' widget on the homepage. Reads from src/data/currently.json; entries that are null don't render.
Used at:
index.astro
A typographic affordance applied to the first paragraph of long-form prose when the .prose-leading class is present. Lives in global.css — not a component, but worth showing alongside everything else.
Used at:
PostLayout.astro (default) , index.astro (featured-post block)
The drop cap is set in Fraunces at 3.6em, floated left, color-keyed to the oxidized teal accent. It hooks the first letter of the first paragraph in any .prose-leading container. The rest of the paragraph flows around it as you'd expect from any newspaper or magazine you've read in your life.
An image masked to a clam silhouette. Three variants — half, closed, side — and three rim options — none, hairline, thick — for restraint vs emphasis. Default is the most subtle pairing (half dome, hairline rim in oxidized teal). Built to be used in exactly one place; here for documentation.
Used at:
(not yet placed on a live page)
variant: half · rim: hairline (default)
variant: closed · rim: none
variant: side · rim: thick
Text on image with five swappable contrast techniques (gradient | overlay | letterbox | shadow | blur-pad), five text positions, light/dark text, and container-query type scaling. Every demo cell is contrast-measured at build time by `npm run contrast`, which samples the image region under the text and reports the WCAG ratio.
Used at:
/lab/text-image-overlay/
A directional eased scrim anchored at the text position.
Full demo → the matrix — five techniques × five hostile images, each cell contrast-measured, plus the ways CMS content breaks them. Or take it for a spin in the playground .
A small sine-wave next to the homepage location: a marker rides the current half of the tide cycle (climbing on the flood, falling on the ebb) for NOAA station 8418150 (Portland, ME — the reporting station for Brunswick). 30 days of high/low predictions are fetched at build time; a small client script (math in src/lib/tide.ts) redraws the arc and repositions the marker every minute, and the spoken phrase lives on as the element's hover / screen-reader label. If the build-time fetch fails, the component renders nothing.
Used at:
index.astro (location eyebrow)
The four dials at the bottom of measured pages. Reads from src/data/lighthouse.json — written by scripts/lighthouse.mjs — and renders nothing for routes without an entry, so unmeasured pages stay clean.
Used at:
BaseLayout.astro (every page, when route is measured)
updated
Per-post 1200×630 PNG share cards generated at build with Satori + @resvg/resvg-js. Two Fraunces TTFs vendored in src/og/fonts/ because Satori doesn't read WOFF2. Lives on a sibling branch; the preview here is the design, rendered at this page's load.
Used at:
BaseLayout.astro og:image (after merge)
Four weeks into a layoff that doesn't quite feel like one.
May 11, 2026 · 3 min read
A reader-facing toggle that switches the *voice* a passage is written in — the same conceit as light/dark theming, but the payload is prose. All variants ship in the HTML (crawlers and no-JS readers get every version, stacked); an inline script collapses them into a WAI-ARIA tablist with roving tabindex and arrow-key nav. Demoed here on the 'every yes is a no' theme in four registers.
Used at:
(component only — destined for a blog post via MDX)
Every yes is a no to something else. A to-do list looks like a plan, but it's really a record of everything you're not getting to today. Prioritizing isn't ranking — it's choosing, out loud, what you're willing to give up.
Every "aye" be a "nay," ye barnacle-brained bilge rat. Two ships on the horizon and we run down but the one — chase both and ye'll catch neither, and the crew feeds the crabs. Pick yer prize. Burn the rest from yer mind. And if ye cannot choose, I'll choose for ye. With the plank.
You believe choice is a freedom. It is not. To say yes to one road is to salt every other, and the powers keep a ledger more honest than any you have kept. So I choose. One door. One sacrifice, willing and exact. The rest I let fall into the dark where unmade things go to keen.
You cannot keep everything, dear. You simply can't. Every little yes is a tiny goodbye to something else, and the sooner you make peace with the goodbyes the sweeter your days go. I said yes to the garden and no to a great many things. I've no regrets. Well. One. But he had it coming, and weren't the roses magnificent that year.
Click a voice, or focus a tab and use the arrow keys.
updated