# Unique A small blog about delightful, unique things — products, apps, phenomena, oddities. Built with [Astro](https://astro.build) + MDX. ## Key features - **Four-collection pipeline**: `find` (captured items, hidden from indexes), `finds` (editorial superposts that reference find slugs in a grid), `reviews` (full-length, sometimes graduated from a find), `posts` (generic essays) - **Daily capture skills** under `.claude/skills/`: `daily-finds` (sweeps every source in `sources.json`, writes one find file per qualifying item — capture-only, no editorial selection); `build-finds` (drafts a thematic superpost from the find pool); `build-review` (graduates a find to a full review, linking the two via `fromFind` / `promotedTo`) - **Sources catalog** at `sources.json` with per-source fetch strategy (`webfetch` / `curl` / `skip`) 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 superposts 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 superpost that references it; promoted finds show a forward link to the review; reviews show a "← First surfaced…" backlink to their originating find - **Unified chronological feed** on the home page across reviews + finds (superposts) + posts - Reviews render as two-line index entries: bold name + muted subtitle (`Cotypist` / *Autocomplete that lives everywhere on your Mac*) - Auto-generated `/categories//` and `/tags//` listing pages — finds excluded by design - Light/dark mode via `prefers-color-scheme` - No JavaScript shipped to the client - Static build, deployable anywhere ## Local development ```bash 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 Create an MDX file under `src/content/reviews/.mdx`: ```mdx --- 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 override; defaults to subtitle." --- Body in markdown / MDX. You can import and use components here. ``` The slug becomes the URL at `/reviews//`. ## Media Visuals for a review live at `src/assets/reviews//`, mirroring the slug. Reference them from MDX with relative paths so Astro's image pipeline picks them up (responsive variants, lazy loading, modern formats): ```mdx ![alt text](../../assets/reviews//feature.png) ``` 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. ## Adding a post (essay) Create an MDX file under `src/content/posts/.mdx`: ```mdx --- title: "Some essay title" date: 2026-05-10 category: writing tags: [meta, opinion] description: "Optional tag override." --- Body in markdown / MDX. ``` The slug becomes the URL at `/posts//`. ## Project layout ``` src/ content.config.ts # collection schemas (reviews, posts) assets/ reviews// # hero images and screenshots per review content/ reviews/*.mdx # product reviews posts/*.mdx # essays (empty for now) lib/ entries.ts # getAllEntries() — unified feed across collections components/ EntryList.astro # shared two-shape entry list layouts/ BaseLayout.astro pages/ index.astro # unified feed 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//[...slug].astro`. Listing pages (index, categories, tags) need no changes — they consume `getAllEntries()`.