rzen e06e7311d6 site: layout width tiers + masonry glide — column changes animate instead of snapping
Named width tiers (mobile/narrow/regular/wide) with body max-width easing
between them; grid pages opt in via BaseLayout `wide`. Masonry is now
absolute-positioned with FLIP glides on column-count changes: targets are
computed from the *final* container width (reading --max, which flips
instantly while max-width tweens), mid-glide column changes retarget
instead of being dropped, and container height eases so content below
rides along.

Claude-Session: https://claude.ai/code/session_01ADR9r5rw5NuvvnCxfYbMuc
2026-07-11 13:42:23 -04:00

Unique

A small blog about delightful, unique things — products, apps, phenomena, oddities.

Built with Astro + MDX.

Key features

  • Four-collection pipeline: find (captured items, hidden from indexes), bundles (editorial roundups that reference find slugs in a grid), reviews (full-length, sometimes graduated from a find), posts (generic essays)
  • Autonomous daily pipeline under .claude/skills/: daily-pipeline (the unattended chain: capture → bundle → review graduation → pick of the day → build → commit → push → digest); daily-finds (sweeps every source in sources.json, writes one find file per qualifying item, marks a ranked shortlist of up to 12, adds Amazon links for matched physical products, and self-maintains sources.json); build-bundle (thematic bundle from the find pool; auto and weekly catch modes); cluster-bundles (batch-clusters the find pool into multiple bundles); build-review (graduates a find to a full review with a mandatory fact-check in auto mode, linking the two via fromFind / promotedTo)
  • Pick of the day driven by src/data/pick.json (falls back to the latest review) — the homepage hero is a one-line content edit, also settable from the dev-mode button on any review page
  • Sources catalog at sources.json with per-source fetch strategy (feed / webfetch / curl / skip, feeds preferred) and the /sources page rendering a 3-column card stream with a "Recent finds" sub-list per card
  • Strict find visibility: find items are reachable only via the bundles that reference them, the /sources cross-reference, or direct URL — they don't crowd the home feed, tag pages, or category pages
  • Backlinks computed at build time: each find page lists every bundle that references it; promoted finds show a forward link to the review; reviews show a "← First surfaced…" backlink to their originating find
  • Newspaper-style masthead on every page: live entry-count + last-updated stats on the left, large serif "Unique" wordmark centered between thin rules, About/Blog nav on the right; per-page italic subheader ("A small log of delightful things.") with last-updated date on listing pages
  • Pinterest-style home page: hero card for the latest review plus a 3-column masonry grid of remaining reviews with a "Load more" reveal; old plain-list home preserved at /index3/
  • Unified chronological feed at /index3/ across reviews + bundles + posts
  • SEO-friendly site footer with Browse / Collections (live counts) / About link columns and a centered legal line; consistent 76rem page width across the entire site (article prose constrained to a comfortable reading measure inside the wider layout)
  • Reviews render as two-line index entries: bold name + muted subtitle (Cotypist / Autocomplete that lives everywhere on your Mac)
  • Auto-generated /categories/<name>/ and /tags/<name>/ listing pages — finds excluded by design
  • Amazon affiliate handling in src/lib/affiliate.ts: outbound links to Amazon (amazon.com, amzn.to, a.co, smile.amazon.com) get the configured tracking ID appended automatically; the link gets rel="sponsored", an "Affiliate" badge appears next to it, and a one-line disclosure is shown beneath. Finds and reviews also take an optional amazonLink frontmatter field (canonical /dp/<ASIN> form) rendered as an "Also on Amazon" link — the capture pipeline populates it when a shortlisted physical product has an exact Amazon match. Add additional marketplace IDs (e.g. amazon.co.uk) to the AMAZON_TAGS map when needed
  • SEO surface: single <SEO> component owning canonical, Open Graph, Twitter Card, and JSON-LD; per-page structured data (Review, BlogPosting, Product, Article, CollectionPage, ItemList, BreadcrumbList, WebSite, Organization); auto-generated sitemap-index.xml (excludes /grid/ and /index3/); RSS feed at /rss.xml; robots.txt; static OG image fallbacks under /public/og/; promoted finds canonical to their review
  • Custom 404 page with recent-reviews and navigation fallback
  • Light/dark mode via prefers-color-scheme
  • Cloudflare Web Analytics beacon in BaseLayout.astro for cookieless, privacy-preserving page-view + referrer + Core Web Vitals tracking scoped to unique.rzen.dev
  • Otherwise no client-side framework — only the inline theme bootstrap and the analytics beacon
  • Static build, deployable anywhere

Local development

npm install
npm run dev      # local dev server
npm run build    # type-check + static build to ./dist
npm run preview  # serve the built site

Adding a review

Each review is a folder with index.mdx plus its media. The folder name is the slug:

src/content/reviews/cotypist/
├── index.mdx                # the article + frontmatter
├── hero.mp4                 # web-ready variants live here, alongside the article
├── screenshot.png
└── source/                  # heavy originals (archival; never bundled)
    └── hero.gif

index.mdx frontmatter:

---
name: "Cotypist"
subtitle: "Autocomplete that lives everywhere on your Mac"
date: 2026-05-03
category: writing
tags: [macos, productivity]
link: https://cotypist.app/        # optional product URL
linkText: "Visit in App Store"     # optional; defaults to the link's hostname (sans `www.`)
description: "Optional <meta> override; defaults to subtitle."
---

Body in markdown / MDX. You can import and use components here.

The folder name becomes the URL at /reviews/<slug>/. Media is referenced relatively:

![alt](./screenshot.png)

import demo from './hero.mp4';
<video src={demo} autoplay loop muted playsinline preload="metadata" />

Review heroes are auto-discovered: name the file hero.<ext> (any of png/jpg/jpeg/gif/webp/avif, or hero.mp4 for video) and the home/grid tiles pick it up — no registration. Only assets with non-hero filenames need an explicit entry in the overrides map in src/lib/hero.ts.

Media

Prefer real screenshots, GIFs, or short clips from the app's own site (downloaded locally — don't hotlink). Pair with a short italic caption that names what's being shown. Visuals should illustrate the virtue of the product, not decorate the page.

Source vs published media. Heavy originals live in <slug>/source/; web-ready variants live at <slug>/<file> and are what the site actually ships. Files inside source/ aren't imported anywhere, so Astro doesn't bundle them into dist/. To add or replace a video:

# 1. drop the original into source/
mv ~/Downloads/demo.mp4 src/content/reviews/<slug>/source/

# 2. encode a smaller web variant alongside (h264 CRF 28, ≤1280px, no audio)
ffmpeg -i src/content/reviews/<slug>/source/demo.mp4 \
  -c:v libx264 -preset slow -crf 28 \
  -movflags +faststart -pix_fmt yuv420p \
  -vf "scale='min(1280,iw)':-2,crop=trunc(iw/2)*2:trunc(ih/2)*2" \
  -an src/content/reviews/<slug>/demo.mp4

# 3. reference ./demo.mp4 from index.mdx or src/lib/hero.ts

For animated GIFs, use the same recipe with crf 26 and source .gif — the resulting MP4 is typically 515× smaller.

Adding a post (essay)

Create a folder + index.mdx under src/content/posts/<slug>/:

---
title: "Some essay title"
date: 2026-05-10
category: writing
tags: [meta, opinion]
description: "Optional <meta> tag override."
---

Body in markdown / MDX.

The folder name becomes the URL at /posts/<slug>/. Drop any media (images, screenshots) next to index.mdx and reference them as ./image.png. Same convention applies to the find/ and bundles/ collections — each entry is a folder with index.mdx and any sibling media.

Project layout

src/
  content.config.ts          # collection schemas (reviews, posts, find, bundles)
  content/
    reviews/<slug>/
      index.mdx              # article + frontmatter
      <media files>          # web-ready variants (hero, screenshots, demos)
      source/                # archival originals (not bundled)
    find/<year>/<month>/<slug>/
      index.mdx                # find folders are bucketed by capture date
      hero.{jpg,png,gif,webp}  # auto-resolved by src/lib/find-hero.ts; URLs stay flat at /find/<slug>/
    bundles/<slug>/index.mdx # editorial roundups (text intro + grid)
    posts/<slug>/index.mdx   # essays
  lib/
    entries.ts               # getAllEntries() — unified feed across collections
  components/
    EntryList.astro          # shared two-shape entry list
  layouts/
    BaseLayout.astro
  pages/
    index.astro              # Pinterest-style home (hero + masonry)
    index3.astro             # plain chronological list view
    about.astro              # About page
    blog.astro               # /posts index (essays)
    grid.astro               # full grid of every review
    sources.astro            # source catalog
    reviews/[...slug].astro
    posts/[...slug].astro
    categories/[category].astro
    tags/[tag].astro
public/
  favicon.svg

Adding a new content type

  1. Define the collection in src/content.config.ts.
  2. Extend the Entry union and getAllEntries() in src/lib/entries.ts so the new type joins the merged feed.
  3. Add a detail route at src/pages/<type>/[...slug].astro.

Listing pages (index, categories, tags) need no changes — they consume getAllEntries().

S
Description
No description provided
Readme
591 MiB
Languages
MDX 88.6%
Astro 8.7%
TypeScript 1.5%
JavaScript 1%
XSLT 0.2%