Build Cronometer Gold

KINDAreplaces $10.99/mosaves $131.88/yrback to the verdict

0%0 of 20 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A food log backed by USDA FoodData Central, which is public domain data: search foods, record portions with real units, see full micronutrient totals rather than only calories, view trends over time in neutral language, and export everything you have logged.

estimated effort multi-daythe files for this build are in the project pack

RuntimeTypeScript with Vite, built as an installable progressive web applicationFood dataUSDA FoodData Central, cached locallyStorageIndexedDB for the log and the food cacheUnitsA unit conversion layer over gram-based nutrient dataChartsHand-drawn SVG

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Runs the build and the copy lint.

    Get it nodejs.org, or nvm install 22. open ↗

    Verify node --version prints v22 or higher

  2. API keyfree; roughly one thousand requests per hour per address

    Why Without it there is no food data. The demo key exists but is rate limited far below anything usable.

    Get it Sign up at fdc.nal.usda.gov/api-key-signup, which issues a free data.gov key by email in a few minutes. No payment details are required. open ↗

    Verify A search request returns results rather than a rate limit error

  3. have readyfree

    Why Logging happens away from a desk, and one-handed use and offline behaviour cannot be judged in a desktop browser.

    Get it Serve over HTTPS to your phone with a tunnelling tool and install to the home screen.

    Verify It installs to the home screen and opens without browser chrome

  4. decidefree

    Why Phase 6 needs a second reader for tone. You cannot audit your own wording for pressure, and this subject matter makes that audit worth more than usual.

    Get it Ask one person, hand them the printed strings, and ask specifically whether anything reads as judgement or instruction.

  5. decidefree

    Why Showing population reference figures next to personal totals invites reading them as targets, which is exactly what this application refuses to do.

    Get it Decide before Phase 5 whether to show them at all. Omitting them is simpler and safer; showing them requires careful labelling and no comparative colouring.

    Verify The decision and its reasoning are written in the README

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- foods(fdc_id, description, data_type, brand, serving_json, nutrients_json, cached_at) · cached from the API, keyed by its identifier
- portions(id, fdc_id, label, gram_weight) · the named servings the database provides, plus any you add
- entries(id, at, fdc_id, quantity, portion_id, grams, meal) · grams is always computed and stored, so a portion definition changing later does not rewrite history
- recipes(id, name), recipe_items(recipe_id, fdc_id, grams) · a recipe is a food composed of other foods
- nutrient_defs(id, name, unit, display_group) · so the interface can show a full panel rather than four headline numbers
- Everything is stored in grams. Portions are an input convenience and a display preference, never the stored quantity.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
VITE_FDC_API_KEYsecretrequiredreplace-with-your-data-gov-keyIssued free by fdc.nal.usda.gov/api-key-signup. Proxy requests through your own small endpoint if you deploy this anywhere public, so the key is not in the client bundle.
VITE_FDC_CACHE_MAXrequired5000How many foods to keep cached locally. Higher means fewer API calls and more storage.
VITE_SEARCH_DEBOUNCE_MSrequired400Search-as-you-type debounce. Lower values burn through the hourly rate limit.
VITE_SHOW_REFERENCE_INTAKESrequiredfalseWhether population reference figures appear at all. False is the safer default and requires no careful labelling.
VITE_EXPORT_REMINDER_DAYSrequired30How often to prompt for an export, because browser storage can be evicted.

The build, in order

  1. Food data you can actually query

    A search returns real foods with real nutrient data, and it works the second time without a network.

    1. Sign up at api.data.gov for a free key. Then fetch one food of each data type and look at what is actually in it before designing anything.

      terminal
      curl 'https://api.nal.usda.gov/fdc/v1/foods/search?query=oats&api_key=YOUR_KEY'
    2. Store every fetched food. Search the local cache before the network, which is both faster and how you stay under the rate limit.

      Files src/food-cache.ts

    3. A nutrient absent from a record is unknown, not zero. This distinction runs through every total you will compute later, so get it right here.

      Files src/nutrients.ts

    done when · tick each as it passes
    watch out
    • The API allows roughly a thousand requests an hour per address, which a search-as-you-type box will exhaust in an afternoon. Debounce properly, cache every result, and search the local cache first.
    • The data types are not equivalent. Foundation and SR Legacy entries carry full laboratory micronutrient panels; Branded entries carry only what a manufacturer put on the label, which is usually a dozen nutrients. Showing a branded food's empty micronutrient panel as though it were zero is wrong and misleading.
  2. Logging with units that make sense

    You can log a cup of something and the grams are right.

    1. Search, pick a portion, set a quantity, choose a meal. Few taps, large targets, because this happens at a table.

      Files src/log.ts

    2. Grams computed once at entry. The portion label is kept for display only.

      Files src/units.ts

    3. Repeating a previous entry is the most common action after the first week. Make it one tap.

      Files src/log.ts

    done when · tick each as it passes
    watch out
    • Storing quantity and portion rather than grams means a corrected portion definition silently rewrites every historical entry that used it. Compute grams at entry time and store them; keep the portion only for display.
  3. Totals that are honest about what they do not know

    The daily panel shows full micronutrients and is clear about coverage.

    1. For each nutrient, accumulate the value and count how many entries supplied one. Both numbers go to the interface.

      Files src/totals.ts

    2. Macronutrients, vitamins, minerals. A flat list of eighty numbers is not information.

      Files src/nutrients.ts

    3. Three foods, computed by hand, asserted in a test. This is the arithmetic everything else depends on.

      Files src/totals.test.ts

    done when · tick each as it passes
    watch out
    • Treating a missing nutrient as zero produces a day that appears deficient in something because one branded food did not print it on a label. Track how many of the day's foods actually carried each nutrient and show that count next to the figure.
  4. Recipes and repeated meals

    The meal you make every week is logged in one action.

    1. Ingredients in grams, plus a serving count. Grams throughout, exactly as with entries.

      Files src/recipes.ts

    2. Store the computed contribution with the entry. The recipe is a template, not a live reference.

      Files src/log.ts

    3. A recipe used as an ingredient. Resolve depth-first and guard against a recipe containing itself.

      Files src/recipes.ts

    done when · tick each as it passes
    watch out
    • Expanding a recipe by reference at display time means editing the recipe next month changes what you ate last month. Snapshot the nutrient contribution at log time, exactly as with portions and grams.
  5. Neutral trends, enforced by a test

    You can see change over time, and the application cannot start judging without a test failing.

    1. Weekly averages per nutrient, gaps preserved. A day with no entries is missing data, not a zero intake day.

      Files src/charts.ts

    2. Scan interface strings for judgement and diagnostic language: goal, target, over, under, exceeded, cheat, guilty, earned, burn off, good day, bad day, streak. Fail the build, not warn.

      Files scripts/copy-lint.tsscripts/banned-words.json

    3. If you show them, label them as population references and never colour by comparison. Omitting them entirely is a legitimate and safer choice.

      Files src/reference.ts

    done when · tick each as it passes
    watch out
    • Reference intake figures are population averages for planning, not personal targets, and presenting one as a line labelled goal turns a reference into an instruction. If you show them at all, label them as population references and never colour a value red for falling below one.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more wellness.