Sports Mini
A daily crossword where every clue is about sports.
THE STORY
I like the NYT Mini. I also have opinions about basketball nicknames, F1 circuits, and which ESPN shows are actually good. Sports trivia doesn't really show up in the Mini — not often, not densely — so I built a daily crossword that's entirely about sports. The attractive part was that it's bounded: a small grid, a once-a-day cadence, a focused vocabulary. But it gets un-bounded fast once you actually start authoring. You need a word library with real metadata, pattern search that's faster than scrolling a spreadsheet, and a grid editor that doesn't fight you while you're trying to fit GIANNIS into a corner. Most of the two days ended up being the admin tools, not the game. Everything ships on Vercel and Supabase. A cron fires at 7am ET, a Postgres function archives yesterday's puzzle and promotes the next one from the queue, and anonymous device IDs in localStorage track solves without requiring an account. Authoring happens in a visual builder that cross-references a hand-curated glossary of ~2,700 sports entries — I spent more time than I'd like to admit tagging things like BARSPIN (cycling, action) and BASELINE (tennis, stat).
KEY DECISIONS
Solves, progress, and streaks live in localStorage keyed to an anonymous device ID. Killing login/signup friction was the single biggest lever to ship in a weekend. The tradeoff is real: no cross-device streaks, no leaderboards, no personal history beyond the browser you're currently in. All three need accounts, and all three can wait until there's someone asking for them.
The glossary isn't a word list, it's a library — ~2,700 rows, each tagged with sport, type (player, stat, nickname, venue, weightclass...), team, and alternate names. Every clue in every puzzle links back to a glossary row. That metadata powers the builder's filters, the freshness logic, and eventually themed weeks and daily difficulty tuning. The tradeoff is ongoing curation overhead. Building the library took as long as building the app.
Puzzles can be created two ways: paste a DSL string generated from Claude into the admin, or build visually in the grid editor with live glossary search and pattern matching. DSL is faster when I'm on my phone; the visual builder is what I reach for when I actually care about the result. Shipping both meant I could iterate from a train and still have the proper tool waiting at home.
run_daily_publish() does it all atomically: archives the currently-published puzzle, promotes queue position 1 to published with today's date, decrements the rest of the queue. Vercel Cron fires at 11:00 UTC and calls one endpoint that invokes the RPC. No pg_cron, no background workers, no state machine. One function, idempotent, easy to test by hand.
RESULT
Live at sportsmini.vercel.app. A new puzzle auto-publishes every morning at 7am ET. Solvers can play in a browser or on mobile, use CHECK and HINT affordances, and copy an emoji-grid share card on completion. The admin side has a queue, a scheduler, a DSL paste-in, a visual builder with glossary search and pattern matching, and a history view. Two days from empty repo to running in production.
STACK