COLOPHON
A printer’s colophon is the note at the back of a book saying how the book was made. This is that note, for this site. What it runs on, what I used to build it, and the decisions underneath it.
HOW THIS WAS MADE
This project started life as a NextJS scaffold. The UI primitives in components/ui are shadcn/ui, dropped in and largely untouched. I have used AI assistance across most of this codebase, and I am not going to pretend otherwise.
What that assistance did was help type faster than I can on my own. What it did not do was decide how crawler numbers get issued, or why the card deck is data instead of code, or which of my own schemas needed to be thrown out and rebuilt. Those are the parts below, and those are the parts that took six months.
STACK AND CREDITS
- Framework
- Next.js (App Router, RSC), React, TypeScript
- Styling
- Tailwind, shadcn/ui (new-york), Radix, lucide-react
- Database
- Serverless Postgres
- Type
- Space Grotesk, Geist Mono, Cal Sans, Coral Pixels
DECISIONS
Crawler numbers are issued, not generated
Every Crawler designation is a globally sequential registry number, and the sequence is the point: it is a record of order of entry, so it has to be unique, gapless in intent, and impossible to hand out twice.
That rules out generating one client-side, or picking one and hoping. Numbers are issued by the database itself, under conditions that make two people registering in the same second a non-event, and certain numbers and ranges can be reserved or withheld rather than handed out. The interesting work was getting that to hold under concurrency; the exact mechanism is the sort of thing a colophon is worse for describing than a code review is.
Collections are modeled as Work, Edition, Image
The scaffold shipped collections as a single flat table, one row per thing you own. I kept it for about two months before admitting it was wrong. A book is not one object. A book is a work, which has editions (hardcover, paperback, audio, a signed printing), and every edition has its own images: cover, back, spine, interior, poster. None of that fits in one row, and no amount of columns was going to save it.
So the table got dropped and the feature rebuilt as a normalized schema across works, editions, images, authors, series, genres, and tags. The migration that deletes the generated version is still in the repo, sitting in the sequence where it happened. It is probably the most honest file in the project: the point where what got handed to me stopped being good enough and I had to know why.
The consent banner had to gate the fonts too
Two third parties get contacted on this site: Vercel Analytics and Google Fonts. Without consent, the analytics component is never mounted at all, so nothing is requested.
The fonts were the harder half. They were originally pulled in with an @import in the stylesheet, which fires while the CSS is being parsed, long before React can mount a banner and ask anyone anything. A consent gate in front of that is decorative. So the stylesheet link is now injected only after consent is given, and until then headings fall back to the self-hosted faces.
THE RECORD
- Application code
- ~33,300 lines
- Unmodified shadcn/ui primitives
- ~6,400 lines
- Routes
- 46
- SQL migrations
- 16 files, ~1,040 lines
- Board game engine
- ~3,200 lines
- In development since
- 28 February 2026
Sixteen migrations, one of which exists purely to tear out the schema I started from. The stylesheet has been rewritten across nineteen separate commits, the profile view across sixteen. Generated code tends to get committed once. Things you care about get committed over and over, which is the only real evidence I can offer that someone was here making choices.
WHAT THIS IS
WelcomeCrawler is an unofficial, non-commercial fan project inspired by the Dungeon Crawler Carl series by Matt Dinniman. It is not affiliated with, endorsed by, or connected to the author, the publishers, or any official party. All rights in the source material remain with their owners.
The separation between engine and content described above is deliberate for this reason. See the about page for the full disclaimer and a contact route for questions, concerns, or takedown requests.
Last updated September 2026.