Cookbook Club
Ten friends, 95 recipes, 50 places in the book
Home, a ballot, and a round’s ranking. Captured from a local copy with test ballots.
The problem: voting on our own time
A vote worth turning up for
My cookbook club cooked 95 recipes from nine cookbooks over a year, and the ten of us are turning the best 50 into a physical cookbook, a keepsake for everyone. That meant collecting a lot of information, and there was no good way for ten busy people to vote on their own time. The vote had to work on a phone in about two minutes a round, and ballots had to stay private. Ninety-five recipes is too many to rank at once, so it runs as 19 short rounds, and every dish is scored twice: once against its category and once against its book.
How 95 recipes became 19 rounds
Each round lasts three days, one at a time. Ten rounds group dishes by kind, such as poultry mains or cookies. Nine group them by the cookbook they came from.
The rules were set out in a written specification, I designed the screens in Figma, and I built it with Claude Code in nine days. The first round opened on October 3, 2026.
- Friends
- 10
- Recipes
- 95
- Rounds
- 19
- Places in the book
- 50
Designing the vote
One round
-
Read the rules
One point for every dish in the round.
-
Give them all away
Pile them on a favourite or spread them out. You can’t submit until every point is spent.
-
See where it stands
Results unlock once you’ve voted. Change your mind any time before the round locks.
One secret
Tap minus on a dish that already has zero and you find the −1 vote. Once per round, you can dock one recipe a point.
The rules of the −1
It does not come out of your budget and it never adds to it. A popup explains it the first time, and nothing on the ballot mentions it before then.
A leaderboard you can’t play
The leaderboard counts only rounds that have locked, so nobody can watch an open round’s totals and vote around them. The top-50 line appears only after the last round.
Keeping it fair
If someone misses a round, its points are scaled to the whole club so those dishes are not discounted. Totals only, throughout: nobody sees who gave what.
Building and testing it
The rules live in the database
No table can be read or written directly; every action goes through a database function that checks who is asking. That is how “you only see results after you vote” is enforced, and not just hidden in the interface.
Testing before anyone voted
The site is static pages on shared hosting, with Supabase for sign-in and data. Sign-in is Google, checked against a list of ten email addresses.
I wanted everything tested before anyone voted. The whole database also runs inside the browser and in automated tests, using Postgres compiled to WebAssembly, so the real rules could be exercised without touching the real data: 79 database tests and 16 for the recipe arithmetic.
A test mode adds time travel, viewing the site as any member, and filling in everyone else’s ballots. A go-live script switches it off and refuses to run once voting has started.
Admin in test mode: section links, and a pretend clock that can jump to any round.
Then, the recipes
The recipe archive came second, and it is what the club will use after the vote: the place we share recipes and cook from them at future meetings. Each recipe can be scaled, with fractions and units handled properly, and the screen stays awake while you cook.
Why you can’t see one here
The recipes are copyrighted text shared only within the club, so they live in the database behind sign-in and never in the public build.
Launch and loose ends
Where it stands
The app is live for the ten members and private to them, so there is no link here. The first round opened on October 3, everyone voted, and nothing broke. The project is unfinished: the remaining rounds decide the 50 recipes in the book, and the archive grows into what we cook from next. The screens here were captured from a local copy running on test ballots; the point totals are not real results.
What went wrong
The first upload broke another site I host on the same account. The server rules I had written for this domain applied to every site in the same folder. They are now scoped to the one hostname.