Offline-first household pantry manager — track stock, build shopping lists, cook from recipes. React 19 + TypeScript, IndexedDB with optional GraphQL/Postgres cloud sync. Designed and built by ETBlue.
翻訳待ち
暮らしTypeScript · JavaScriptGitHub @ETBlueオンライン所有者未確認
Have you tried it? One tap is enough. No sign-in needed.
Loading the full README. The saved excerpt is shown below.
Track what you have. Shop what you need. Cook what you can.
A household pantry manager: track stock levels, build shopping lists, and log ingredient use when cooking. Offline-first, so it works in a supermarket aisle with no signal. Cloud-optional, so a household can share one pantry.
I use this daily, on laptop and phone, to run my own household pantry. Every design decision below came from hitting a real problem while using it.Read the full README ↗Track what you have. Shop what you need. Cook what you can.
A household pantry manager: track stock levels, build shopping lists, and log ingredient use when cooking. Offline-first, so it works in a supermarket aisle with no signal. Cloud-optional, so a household can share one pantry.
▶ Live app · Design guide · Storybook · Design docs
I use this daily, on laptop and phone, to run my own household pantry. Every design decision below came from hitting a real problem while using it.
| Product decisions | Engineering |
|---|---|
| Six real-world design problems, each written up as problem → decision → rationale | One component tree, two data sources — IndexedDB offline, GraphQL in the cloud, switched by a flag |
| Cold-start onboarding that seeds a usable pantry without asking a first-time user to design a taxonomy | OKLCH design tokens where WCAG AA contrast holds by construction, not by spot-checking |
| User-defined categorization instead of an enforced, exhaustive category tree | Four test layers: Vitest units, Storybook smoke tests, Playwright E2E, and an axe WCAG AA scan on every branch |
| Bidirectional entity creation, so a user working from a tag never navigates back just to add an item | ~40 in-repo design docs — brainstorm → design → plan → implement, with the reasoning kept |
Every item tracks packed and unpacked quantities separately — opened packages and untouched ones are different states that matter for consumption order. A segmented progress bar shows both at a glance, color-coded by stock status (in stock / low / out of stock).
Items with variable package sizes (e.g. milk in 1000ml or 2000ml cartons) can be tracked in measurement units instead of package counts. Expiration is configurable per item: set a fixed due date, or let the app calculate it from the last purchase date.
Each item has a configurable refill threshold. The shopping page sorts by stock level by default, so low-stock items rise to the top automatically. The list filters by vendor, so each household member sees only what to pick up at their store.
With cloud sync enabled, anyone in the household can check what's running low on their way to the store — no real-time coordination needed.
Recipes group ingredients with default consumption amounts. When cooking, the app pre-fills quantities from the recipe — follow them or adjust on the fly. Ingredients with a default amount of 0 are optional and start unchecked.
Cooking a recipe deducts every ingredient in one step, with the option to set servings and override individual amounts before confirming.
Real-life household goods have messier state transitions, looser categorization, and more overlapping relationships than a game inventory does. These are the six problems I ran into while using the app, and what I decided to do about each.
A pantry item isn't just "in stock" or "out of stock." It moves through several states, each demanding its own design decision.
Stocking threshold — users carry an implicit sense of "enough" in their heads. Making it explicit (target quantity + refill threshold) lets the shopping page sort low-stock items to the top automatically, and gives them a distinct color in list views.
Variable package sizes — milk might come in 1000ml or 2000ml cartons, so tracking by package count loses precision. A dual unit system (package + measurement) lets users track in whichever unit makes sense for that item.
Packed vs. unpacked — opened goods expire sooner than sealed ones, and users need to see both at once. The two quantities are stored separately, and the segmented progress bar encodes both states in a single visual.
Partial consumption — using 200ml of milk shouldn't require typing arithmetic or pressing minus 200 times. A configurable "amount per consumption" becomes the step size for every ± button across the pantry, cooking, and recipe pages.
Expiration — some items have printed dates (milk), some don't (mushrooms), some never expire (batteries). Two modes cover this: a fixed due date, or a number of days counted from the last purchase. A warning threshold raises a badge as an item nears expiry.
Simple household goods (toilet paper, toothpaste) just decrement. Ingredients don't: several are consumed together in varying combinations, and the same dish uses more or less of an ingredient each time — or skips one entirely.
Recipes group ingredients with a default consumption amount each. Optional or rarely-used ingredients get a default of 0, so the user opts them in per session. While cooking, users follow the defaults or adjust per-session amounts before confirming. Recipes are a starting point, not a contract — that framing is what kept the feature from turning into rigid meal-planning software.
Real-world items can be classified along many axes — preservation method, nutritional category, origin, intended use. A fully exhaustive, mutually-exclusive taxonomy is accurate but impractical for daily pantry use, and nobody wants to design one before buying groceries.
This is the beginning of the README. Read the rest at the original. Read the full README ↗