Skip to content
Eric Blanchard

All work

Step contest

Logging a day should take two taps and a number. Behind that simple screen is a role system, a live leaderboard derived from raw entries, and a privacy model that the database enforces rather than the UI.

Type
Personal
Status
In use
When
2026
Role
Design and engineering
  • Next.js 16
  • TypeScript
  • Firebase Auth
  • Firestore
  • Cloud Storage
  • Cloud Functions
  • Netlify
  • PWA

Decisions worth a look

  • Emails are never visible to other participants. Leaderboard data is denormalised onto participant documents, and user documents are readable only by their owner and site admins.
  • Private evidence is split into its own collection, and Storage rules check Firestore with a cross-service lookup, so even a leaked image URL is unreadable.
  • Entries lock at midnight on the last day in the contest's own time zone, compared against server time, so a device with the wrong clock cannot buy extra days.
  • Lifetime totals use an atomic increment, so two phones logging at once cannot overwrite each other.
  • One hourly scheduled function sends evening reminders at 7pm in each contest's time zone and prunes dead push tokens as it goes.

Roles

A person can hold any combination of these across any number of contests.

RoleScopeGranted by
Site adminWhole instanceAnother site admin (the first is set by hand)
Contest adminOne contestThe contest creator, or another contest admin
ParticipantOne contestJoining with an invite code

The leaderboard

The leaderboard is derived live from entry documents with Firestore snapshots, so it updates the moment anyone logs, edits or deletes. Alongside the ranking there is a cumulative race chart, streaks, the gap to the person above you, and highlights such as longest streak and biggest single day.

Each highlight goes to a different person on purpose, so the top spot is not the only thing worth winning.

Privacy, enforced by the rules files

Two promises are kept by Firestore and Storage security rules, not by hiding buttons.

  • Each entry is split into a public part (date and step count, which the leaderboard needs) and an evidence part (note and screenshot).
  • Whether evidence is public is chosen when a contest is created and cannot be changed afterwards, because people upload screenshots on that promise.
  • Edits append to a changelog every member can read, including corrections made by admins after the contest closes.

Cost

For a group of friends it runs at no monthly cost. Screenshots are downscaled to 1600px and re-encoded as JPEG in the browser before upload, which keeps each one under roughly 300 KB and the whole project inside the free tiers.