DEV Community

CountMyDays
CountMyDays

Posted on

Building a small web app to last: the future of CountMyDays

Over the last few posts I've written about the unglamorous parts of building CountMyDays: counting days without off-by-one bugs, streak counters that survive midnight and DST, and "tomorrow" vs "in 2 days" labels that respect the reader's time zone. This one is different. It's less about code and more about intent: what this little app is for, and how I want it to age.

What CountMyDays is

CountMyDays is a free web app for two simple questions:

  • How long until something? A wedding, a trip, an exam, a birthday.
  • How long since something? A new habit, a quit date, an anniversary.

You type a name and a date, and you get a countdown or a count-up with a progress bar. You can share it with one link, add it to your calendar, put it in fullscreen on a big screen, or install it to your home screen like an app. There are no ads, and you don't need an account to start. If you want your countdowns on more than one device, you can sign in with a passwordless magic link.

It's built by one developer in Iceland on React 19, Supabase and Vercel. That's the whole team.

Why "futureproof" matters for a countdown app

A countdown is a promise about the future. Someone creates one for a wedding that's 400 days away and sends the link to their family. That link has to still work, and still show the right number, on day 1 and on day 400.

That's a higher bar than it sounds. Most small web apps are judged on day one: does it look nice, is it fast? A countdown is judged on the day the event arrives. So the question I keep asking is not "what can I add?" but "what will still be true in two years?"

Here are the principles I want CountMyDays to keep, whatever else changes.

1. The core stays free and needs no account

Creating a countdown, sharing it and counting days since something should never sit behind a sign-up or a paywall. Guest mode isn't a trial; it's the product. Accounts exist for convenience (sync across devices), not as a gate.

This also keeps the app honest. If the basic thing works without me knowing who you are, there's less to go wrong and less to protect.

2. Your data is yours

A countdown is personal. You should be able to take your data with you. Today the app has a backup code and a downloadable text archive with one-click restore. I want that kind of exit door to stay a first-class feature: plain, readable formats over clever ones, and no lock-in.

3. Links that keep working

A shared link is a small contract. The direction is simple:

  • Share URLs should be stable. If the app changes shape, old links should still land somewhere sensible.
  • The number on the page has to be computed in the viewer's time zone, using calendar days, not raw milliseconds. (That's what the earlier posts in this series were really about.)
  • A finished countdown shouldn't just break. It should move into a "passed" state and become a memory rather than an error page.

4. Boring, standard web tech

I'd rather lean on the platform than on a pile of dependencies. A few examples from this series:

// Wording comes from the platform, in any locale the browser supports
new Intl.RelativeTimeFormat('pl', { numeric: 'auto' }).format(1, 'day');
// "jutro"

// Calendar-day maths in UTC, where every day is exactly 24 hours
const dayIndex = (y, m, d) => Date.UTC(y, m - 1, d) / 86400000;
Enter fullscreen mode Exit fullscreen mode

Intl, Date.UTC, localStorage, .ics calendar files, service workers and web app manifests are standards. They'll still be here long after today's favourite library has a new major version. When the Temporal API is widely available, the date code can get even simpler without changing what users see.

The stack is deliberately small: a React front end, a Postgres database with row-level security, and static hosting with a few server routes. Small enough for one person to understand completely, which matters more than any single feature.

5. Offline and installable, as a habit of thought

A countdown is something you glance at, often on your phone, sometimes on bad Wi-Fi. Thinking "what happens with no network?" early makes the whole app better: local-first data for guests, cached assets, and sync as a bonus rather than a requirement. Progressive Web App support is part of that, and keeping it working well is ongoing work rather than a box I ticked once.

6. Accessible and calm

No ads, no pop-ups, no dark patterns. Big, readable numbers. A compact view for people with many countdowns and a spacious one for people with a single important date. Accessibility is never "done", and I'd like it to keep improving with every change rather than in one big push.

Resilience: fixing things in public

Here's my favourite part of writing on dev.to. After my first post about the app's architecture, a reader left a thoughtful comment pointing out a weakness in how the live-updating shared view used Supabase Realtime broadcasts. In short: on a public channel, the app trusted the content of broadcast messages a bit too much.

They were right. Nothing in the database could be changed or lost, but it's worth fixing properly, and that fix is underway. The idea behind it is a good general lesson, so here it is:

Treat a realtime message as a signal, not as data. When something says "this changed", go and fetch the real state from the database, where access rules apply.

channel.on('broadcast', { event: '*' }, () => {
  // Don't render the payload. Just refetch the source of truth.
  refetchSharedItem();
});
Enter fullscreen mode Exit fullscreen mode

With that change, viewers still see updates live, but nothing on screen comes from an untrusted message. Tighter channel permissions are a possible next step after that.

So, a sincere thank you to that reader, and to everyone who has read, commented on or shared these posts. A small app gets more resilient when the community is generous enough to point out its weak spots.

What's next

I won't give you a roadmap with dates, because roadmaps for one-person projects are mostly fiction. What I can promise is a direction:

  • keep the free, no-account core
  • keep shared links working and numbers correct
  • keep the stack small and standard
  • keep improving offline support, accessibility and privacy
  • keep fixing things openly when people find problems

If any of that sounds useful, try it: create a countdown to something you're looking forward to, or a "days since" for a habit you're proud of, at countmydays.to. If you find a bug or have an idea, I'd genuinely love to hear it in the comments or on X at @countmydays_to.

Thanks for reading, and for counting along.

Top comments (0)