The thing that got me was the value. I wanted to make a group card for someone at work. The kind where everyone contributes a message and you send it all at once. Kudoboard.com wanted a minimum of $5.99 per board (lite board). A Premium board gets you more features for $8.99 and an even higher Milestone board for $19.99. I paid the $5.99, but never again. I set to building my own version.
One day later I had my own minimum viable product (MVP) live at ricoordonio.com/kudos. I continued to work on it over the next three days.
Building it meant consciously breaking two of my own rules. Rule one: everything on the site should be browser-first, no API costs. A group greeting card fundamentally can’t be browser-only — cards have to live somewhere everyone can reach. I needed a real backend. Rule two: the whole site runs a blueprint-industrial aesthetic. Dense, navy, utilitarian. The kudos board is pastels, Fredoka font, and a confetti burst when you open a board for the first time.
Both rules broken. I figure it’s ok for now because this is only a temporary home. If this kudos thing is worth it, I’ll move it to its own site and ricoordonio.com will have continuity once again.
The stack is Astro 5 static for the shell with Svelte 5 components handling the interactive bits, backed by Supabase for the database and storage. For the MVP, the entire security model is a random 10-character slug. That’s it. If you have the link, you can post a card. If you don’t, you can’t find the board. For a greeting card that lives for 30 days and then gets deleted, that’s fine. There’s nothing in there worth protecting with real auth.
The 30-day cleanup was one of the first things I wired up. I didn’t want a graveyard of stale boards accumulating in the database. Supabase has pg_cron built in, so I set up a job that runs at 3 AM UTC and deletes any board that hasn’t been viewed in 30 days. Simple, automatic, no Lambda needed.
The thing that bit me hardest was a CloudFront quirk I did not see coming. The board URL looks like /kudos/board/?slug=abc123. That trailing slash before the query string is load-bearing. CloudFront redirects /kudos/board?slug=abc123 (no trailing slash) to /kudos/board/ — and in the redirect, it silently drops the query string. You end up on a blank board page with no slug, staring at nothing. The bug is completely invisible in local dev because Astro’s dev server doesn’t reproduce it. I only found it in production after deploying and wondering why my slug had vanished.
The Giphy picker was the feature I added purely because I wanted it. Text cards are fine. But sometimes you want to send someone a very specific reaction GIF. It took maybe thirty minutes to wire up: Giphy’s search API, a PG-13 content filter on the results, store the URL rather than re-hosting the file. Now it’s one of the more genuinely fun parts of posting a card.
Where it stands right now: you can create a board, share the link, people can post text kudos, GIFs and videos. It persists. It auto-deletes after 30 days. That’s the MVP.
What’s not built yet is a longer list than what is. No creator auth, so you can’t delete a card once it’s posted. No scheduled delivery where you lock the board and reveal everything at once. No way to recover boards across devices if you clear localStorage. These weren’t surprises, they were always meant for future phases.
End goal is its own domain with an AWS-native backend. The Supabase setup is MVP-grade and I knew that going in. It’s not meant to survive a real scale event, it’s meant to survive the part where you figure out if the thing is worth scaling.
That’s the actual value of building on a sub-path of your personal site first. No separate deploy pipeline, no DNS to wrangle, no pressure to make the architecture perfect before you’ve shipped a single card. You just build the thing and see if it works.
Feel free to test out your own board at ricoordonio.com/kudos. If you like what you see, maybe you could drop me a kudo.