Skip to content

Latest commit

 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Pushups

A private push-up tracker that installs to an iPhone home screen. No account, no server, no analytics — every rep you log stays in your phone's browser storage.

Live at https://lilburdeh.github.io/pushups/

What it does

  • Today — one big button logs your usual set. A ring fills toward your daily goal and keeps going past it (a second gold arc) when you beat it. Tap any set to edit its reps or time; tap × to delete with an undo.
  • History — a 26-week heatmap, plus 7-day, 30-day, and all-time totals. Tap any square to see that day's sets, and any set to correct it.
  • Settings — daily goal, the quick-add value, and backup.

Both Today and History fit one screen; only the set lists scroll.

Custom amount or time… opens a sheet with reps and a date/time, so you can log a set you forgot, or re-add one you deleted by mistake, at the time it actually happened.

Installing on an iPhone

Open the URL in Safari → Share → Add to Home Screen. It then launches full-screen with no address bar, and works offline.

Making changes

There is no build step. Edit a file, commit, push:

git add -A && git commit -m "your change" && git push

GitHub Pages redeploys in about a minute. The phone picks the change up next launch.

Bump the cache version when you deploy. sw.js starts with:

const CACHE = 'pushups-v1.0.0';

Change that string on any release. The service worker is network-first, so a deploy lands even if you forget — but bumping it evicts the old cache properly.

Running it locally

python3 -m http.server 8765 --directory .

Then open http://localhost:8765. A plain file:// open will not work: ES modules and service workers both require a real origin.

How it is put together

Plain HTML, CSS, and ES modules. No framework, no dependencies, nothing to update.

File Role
js/dates.js All day-bucketing. The single definition of "a day".
js/store.js localStorage read/write, derived totals, export/import.
js/today.js Ring, today's set list, quick-add.
js/history.js Heatmap, range stats, day detail.
js/settings.js Goal, quick-add values, backup.
js/app.js Tab routing, day-rollover watch, service worker registration.
js/ui.js Undo toast and bottom sheets.
sw.js Offline cache. Network-first, so deploys always land.

Two things worth knowing before you change anything:

  • Every date question goes through dates.js. It uses local midnight, never toISOString() — that's UTC and would push late-evening sets onto the next day's square. setClock() exists so tests can freeze time.
  • Views never touch localStorage. They read from store.js and re-render on its events. Any change re-renders all three tabs; at this data size that's cheaper than tracking what went stale.

Typography

Display type is Nippo Bold from Fontshare; small UI text stays on the system font, which is more legible at 11–14px than a geometric display face.

The font file is deliberately not in this repo. The ITF Free Font License permits self-hosting on your own site but prohibits distributing the font through a "repository" or "publicly accessible servers" — and this repo is public. So styles.css points @font-face at Fontshare's own CDN, which is the route the licence explicitly endorses, and sw.js allow-lists cdn.fontshare.com so the font is cached on-device for offline use. Caching on the user's own device is not redistribution.

If Fontshare ever rotates that URL, the app falls back to the system font rather than breaking — that is what font-display: swap and the fallback stack are for.

Known trade-off: Nippo has no tabular figures. font-variant-numeric: tabular-nums is a no-op on it, so digits are proportionally spaced — "111" is noticeably narrower than "888". Numbers are centred in the ring and in the stat cards so it reads as gentle breathing rather than misalignment, but if it ever grates, the fix is to drop var(--font-display) from .ring-total, .stat-value and .set-reps and let the system font handle numerals.

Data and backups

Everything lives under the pushups.sets.v1 and pushups.settings.v1 keys in localStorage. Three layers of protection, in order of how much they actually help:

  1. navigator.storage.persist() is requested on launch. Safari generally grants this to an installed home-screen app, which stops iOS clearing the data to reclaim space. It does not survive a manual "Clear History and Website Data".
  2. Save backup… uses the Web Share API, so on iOS it opens the share sheet and the file can go straight to iCloud Drive or Files. This is the only route from a web app into cloud storage — there is no automatic sync without a server and an account.
  3. Last-backup tracking. Settings shows how long it has been and turns amber past two weeks.

Import merges on set id rather than overwriting, so restoring an old backup can never destroy anything logged since.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages