Wardian docs

Habit tracker

Try it

The app runs here in your browser. Nothing you type leaves it; what it saves stays in this browser, and Claude is off here. Open it on its own.

Download habit-tracker.wardian Import it in Wardian: Settings → Import.

A Wardian suite: six sealed apps that track daily habits together. It is plain JavaScript, with no WebAssembly. It shows storage (saved state kept by Wardian) and retained topics.

AppSlotJobContract
log—Owns the check-ins and the clock. Ticks and unticks days, forgets a deleted habit's days, restores a backup. Moves every view to the new day at midnight.provides set, forget, restore; emits log:changed (retained); storage
habitsasideAdd, rename, recolour, reorder and delete habits. Owns the list.emits habits:changed (retained); listens log:changed; needs log.forget; provides restore; storage
todaymainTick today and the six days before it, one press each. Arrow keys move between boxes.listens habits:changed, log:changed; needs log.set
streaksmainCurrent streak, longest streak, and how often you kept each habit in the last 30 and 90 days.listens habits:changed, log:changed
calendarmainA year of squares per habit, and one for all habits together. Keyboard: arrows move, Enter ticks.listens habits:changed, log:changed; needs log.set
backupmainDownloads everything as JSON. Reads such a file back, checks it in full, and asks before it replaces anything.listens habits:changed, log:changed; needs habits.restore, log.restore; claude:downloads

How a change flows: you press a box in today → it calls log.set → log saves the check-in and emits log:changed → today, streaks, calendar, backup and habits each redraw. A new habit goes the same way through habits and habits:changed.

Each piece of data has one owner. habits alone writes the list of habits; log alone writes the check-ins. Other parts ask the owner through a method. So two panels can never save different versions of the same thing.

Both topics are retained. A part that starts late, or after a reload, gets the latest list and check-ins at once. On a reload the owners read ctx.store and emit again.

Days are local calendar days, kept as text such as 2026-10-08. Day arithmetic uses the day's numbers, not clock time, so summer time never skips or repeats a day. log checks the date at midnight, every minute, and when the page comes back into view.

shared/days.js (days), shared/model.js (the data's shape and its checks) and shared/stats.js (streaks) are listed in suite.json "scripts", so every frame gets its own copy.

The backup file#

{ "format": "wardian-habit-tracker", "version": 1, "exported": "2026-10-08T21:04:00.000Z",
  "habits": [{ "id": "hm1x2y3", "name": "Read 20 pages", "color": "#1971c2", "created": "2026-10-01" }],
  "days": { "hm1x2y3": ["2026-10-07", "2026-10-08"] } }

A restore refuses a file that is not JSON, is not this format, has a habit with no name or a repeated id, or has a day that does not exist, such as 2026-02-30. Nothing changes until you press Replace my data.

Limits#

This page is apps/habit-tracker/README.md in the repository. Something wrong or missing? Change that file.