Track your daily intake. Hit your goal. Simple, private, and free.
A beautiful, mobile-first web app for logging what you eat and staying on top of your calorie goals β with weekly insights, a saved-foods library, and optional sync across devices.
π Try it live β developerdii.github.io/cal-goal
No sign-up required β it works instantly in your browser, and your data stays on your device until you choose to sync.
- Zero setup β open it and start logging. No account, no paywall, no onboarding.
- Private by default β everything lives in your browser's local storage. Only if you sign in does your data sync to the cloud.
- Fast & focused β built for quick daily logging, not for wrestling with spreadsheets.
- Works everywhere β a responsive web app that runs on any device, desktop or mobile.
- π½οΈ Log anything in seconds β add foods one by one or bundle them into meals (e.g. "Breakfast").
- βοΈ Per-unit calories β enter "100 g = 110 kcal", then type how much you ate; it does the math.
- β Foods library β save your go-to foods and meals, then quick-add them in one tap.
- π― Goal tracking β instantly see if you're under or over your daily target.
- π Weekly analysis β total surplus/deficit plus an estimated weight change (7,000 kcal β 1 kg).
- π History preserved β jump between days and weeks; nothing is ever lost.
- π Dark mode β auto / light / dark, no flash.
- π Turkish & English β switchable from the UI.
- π Optional cloud sync β sign in to sync across devices (Supabase).
- Open developerdii.github.io/cal-goal.
- Tap "Add entry" and log your first food.
- Set your daily goal in Settings.
That's it β your data is saved locally and survives refreshes.
Vue 3 Β· Vite Β· Pinia Β· Vue Router Β· vue-i18n Β· Tailwind CSS Β· localStorage Β· Supabase Β· Vitest
npm install
npm run dev # start dev server
npm run build # production build
npm run preview # preview the production build
npm test # run unit tests- All development happens on
develop. Never push directly tomain. developβmainis merged only when the maintainer approves.mainis the production branch β pushing to it triggers the GitHub Pages deploy.
git checkout develop
# ... make changes ...
git add -A && git commit -m "type: description"
git push origin developWhen a batch of work is ready to ship, the maintainer merges develop into main.
Hosted on GitHub Pages; deploys are handled by
.github/workflows/deploy.yml.
- Auto: pushing to
maintriggers a build + deploy. - Manual: the workflow can also be run from the repo's Actions tab.
Live URL: https://developerdii.github.io/cal-goal/
The app works offline-first with localStorage for guests. Optionally sign in with
Supabase to sync your data across devices:
-
Create a Supabase project, then run the SQL in
supabase/schema.sql(Supabase Dashboard β SQL editor). This creates theuser_datatable with Row Level Security. -
Copy your Project URL and anon key (Dashboard β Project Settings β API) into
.env.local:VITE_SUPABASE_URL=https://xxxx.supabase.co VITE_SUPABASE_ANON_KEY=eyJ...
-
(Optional) To let users sign in immediately after sign-up, disable "Confirm email" under Supabase β Authentication β Providers β Email.
-
Restart the dev server β a "Sign in" button appears in the header.
When signed in, all data (days, settings, foods, preferences) is stored as a single JSON document per user in Supabase. The first sign-in migrates any existing local data to the cloud; signing out returns to local storage.
For the GitHub Pages deploy, add
VITE_SUPABASE_URLandVITE_SUPABASE_ANON_KEYas repository secrets (they are injected at build time by the workflow).
components βββΆ stores (Pinia) βββΆ storageService βββΆ localStorage
src/services/storageService.jsis the only persistence boundary. Guests write tolocalStorage; signed-in users write to Supabase. It keeps a stable, mostly synchronous interface β onlyinit()(load) andflush()(force save) are async.src/storeshold app state (prefs, settings, diary) and orchestrate persistence.src/utilscontain pure, unit-tested domain logic (dates, weekly analysis, formatting).
A single versioned localStorage key calgoal:
Weeks start on Monday and are derived from date keys β no separate week records are needed, and history is implicitly preserved by keeping days keyed by date.
{ "version": 3, "days": { "2026-09-07": { "date": "2026-09-07", "entries": [ { "id": "uuid", "type": "item", "name": "Banana", "calories": 105, "createdAt": "ISO-8601" }, { "id": "uuid", "type": "item", "name": "Chicken", "calories": 275, "unit": "g", "amount": 100, "perKcal": 110, "quantity": 250 }, { "id": "uuid", "type": "group", "name": "Breakfast", "items": [{ "id": "uuid", "name": "Oats", "calories": 300 }] } ] } }, "settings": { "maintenanceCalories": 2000, "goalType": "maintain", "goalAmount": 0, "currentWeight": 70 }, "prefs": { "locale": "en", "theme": "light" }, "foods": [ { "id": "uuid", "type": "food", "name": "Chicken", "unit": "g", "amount": 100, "perKcal": 110 }, { "id": "uuid", "type": "food", "name": "Banana", "calories": 105 } ] }