Five days with the pantry app: weather, cooking, takeaway, and a quieter redesign
I wrote about the pantry app five days ago. Since then I’ve used it every day, noticed all the places it was lying to me, and quietly fixed most of them. The changes are small and mostly unglamorous. Together they make the app feel more like a thing I trust.
This is a follow-up rather than a redesign post. The four screens are the same. The shape is the same. Three files, one Supabase project. What changed is the texture.
I redesigned the whole thing, mostly
The first version was warm but a bit busy. Two typefaces (Syne and Inter), urgency colours doing a lot of shouting, italics for emphasis in too many places. It looked friendly enough at first glance and started to feel noisy after a week.
I rebuilt the type system around a single font, Lexend, which is designed specifically to make reading easier for dyslexic readers. I’m not dyslexic, but the research on Lexend (slightly looser tracking, higher x-height, simpler shapes) generalises to “easier for everyone” — and a kitchen app is exactly the context where you want a font that doesn’t fight back. I removed italics entirely. Where I’d previously used italic for emphasis, I now use weight, because italics are one of the things that degrade for dyslexic readers and most of the time I just wanted “this word is important” rather than “this word is in a different voice.”
I rebuilt the colour system as a small set of CSS custom properties — --ink, --ink-2, --ink-3, --surface, --line, --accent. Every text colour passes 4.5:1 contrast against the background it sits on (I’d been lazy about that in v1). The accent moved from the original orange to a deep teal that holds up better against the warm cream background.
The biggest practical change is that dark mode now works properly. The whole palette inverts on prefers-color-scheme: dark. I didn’t have to write a toggle — the OS handles it, and the app just respects what the OS says. It’s the kind of thing nobody asks for but everybody notices missing.
The pantry screen got the most visible redesign. The old version had a coloured accent bar on each row plus an inline use-by date plus a “Counts as” subtitle plus a search box, and it was always slightly too much. The new version is a table: QTY · ITEM · USE BY, with a small coloured pill next to the date if the item’s about to go off. The accent bar is gone. The “Counts as” line is gone (the mapping data is still there, it just doesn’t need to be visible on the inventory screen). It’s calmer.
I also added search to the recipes screen. Twenty recipes is small enough that you can scroll, but it isn’t small enough that you can always remember the exact name. Typing “chick” finds anything with chicken in the name or in the ingredient list. The search and the “Can cook” filter chip together turn the recipes screen from a list into a question — what can I make right now?
I added the weather
This one I want to talk about specifically because it’s a tiny change that I noticed disproportionately.
The meal planner now has a row of weather forecasts above each day — high, low, and a small icon. It pulls from Open-Meteo, which is a free weather API with no key required. About twenty lines for the fetch and the state, plus a small dispatch table of SVG icons for the WMO weather codes.
The reason I noticed it is that I started planning meals differently. I cook differently when it’s cold. I knew this in the abstract — every cookbook arranges itself by season — but having the forecast next to the day, when I’m picking what to make, surfaced the decision in a way the calendar never did. Stews and bakes go in on the cold days. Salads go in on the warm ones. Roast chicken thighs on Saturday because Saturday looks miserable.
I didn’t plan it that way. I built the weather row because I had the API tab open. The behaviour fell out of it.
This is the kind of feature I would not have written a product brief for. The brief would have said something like “users want to see weather forecasts integrated into meal planning” and I’d have rolled my eyes. But I built it because it was easy, and now I’d miss it.
I closed the loop between recipes and the pantry
The first version of the app could tell you what to cook, but it couldn’t help you actually cook it. You’d plan dinner, make dinner, and then either remember to manually decrement the pantry (which nobody does) or not (which is what nobody does). After a week, the pantry was confidently wrong about what was in it.
The fix was a button. Mark as cooked.
You tap a planned meal. The detail modal shows you what the recipe needs and whether you have it. If you have everything, the button is at the bottom. Tap it, the app asks “All done?” — and if any ingredient has more than one matching pantry item, it asks you which one to use (the close-to-expiring one usually wins). Confirm, and it deducts the quantities, deletes any item that hits zero, and the pantry rebalances.
This is the feature I was missing without realising. The whole point of tracking what’s in the fridge is that the tracking stays true. The two ways to keep it true are (a) decrement every time you take something out, or (b) decrement when something gets used in a meal you already planned. The first is exhausting. The second is invisible.
The implementation is unfussy: when you mark a meal cooked, the app walks the recipe’s non-optional ingredients, finds the linked pantry items, sorts them by expiry date so the soonest-to-expire gets used first, and deducts. If two pantry items map to the same ingredient (I sometimes have two open packets of cheddar), the app asks me to choose. If I don’t choose, it picks the one going off first.
There’s a small piece of magic in that ordering rule that I’m pleased with. The same data that drives the urgency colours on the pantry screen also drives which packet gets consumed first. The user never has to think about “which one should I use up?” — the app already knows, because the dates know.
I gave ingredients more truth
Two smaller changes, both about making the data model match how food actually works.
Optional ingredients. Recipes can now mark ingredients as optional. The parmesan on the carbonara, the cumin in the shakshuka. The “missing” count ignores them. The “ready to cook” tag ignores them. If you have everything else, the recipe is cookable — the parmesan can wait.
This sounds trivial. It changed which recipes the app told me I could cook. Before, the carbonara always said “1 missing” because I rarely have parmesan in the house. With optional ingredients, it correctly says I can cook it. The cookable set roughly doubled.
Pack quantities. Items can now have a pack size — a 250g pack of fresh pasta is qty: 1, unit: pack, pack_amount: 250, pack_unit: g. The display still says “250g Fresh Pasta,” but underneath, the app knows the item is one pack and the pack contains 250 grams. When you cook something that needs 250g of pasta, the pack gets consumed entirely and the item is deleted. When you cook something that needs 100g, the pack gets noted as having 150g left.
This is a small change in the data and a meaningful change in behaviour. Before, the app would refuse to think a 1-pack of pasta could fulfil a 250-gram recipe requirement, because the units didn’t match. Now it can. The pantry is more truthful about what it has, in the same units the recipe asks for.
I added a takeaway poll, because not every night is a cooking night
The honest version of “what should we eat tonight” sometimes isn’t a recipe. Sometimes it’s a question between the people in the kitchen — noodles or pizza or curry. Treating that question as a meal-planner hole, or as a separate app, felt wrong. So I made it a first-class plan unit, sitting in the same slot the meals sit in.
You tap “Add meal” on whichever day, and alongside the recipes there’s a “Get a takeaway” option. Tapping it creates a poll attached to that day. The poll’s options are hard-coded — noodles, pizza, curry, meze — because those are the only four things we ever actually argue about. Anything fancier is a recipe.
Voting is anonymous and friction-free. There’s no auth — each device gets a UUID stored in localStorage the first time the app opens. You can vote for more than one option, because in practice “I’d be happy with curry or pizza” is a real answer. The card on the planner shows the leading option and the vote count, and an accent-coloured VOTE NOW pill until you’ve voted yourself, at which point it goes quiet.
The pill is the only place in the app I’ve allowed all-caps and a strong accent colour. It’s earning that visual loudness because of what it represents: someone else has voted on something you haven’t seen yet, and the app is pulling you toward the decision rather than letting it drift.
What I notice is that the poll doesn’t really need a “winner.” Once two of us have voted for the same thing, the answer is obvious without anyone declaring it. The poll is mostly a coordination object — a thing to point at and say “well, you voted pizza too” — and once that’s true, the decision happens in the kitchen, not the app.
I’d not have built this if I’d been working alone. It’s the change that came from sharing the app with someone else and watching the friction of “what shall we get” play out over text instead.
What I notice about the iteration
Five days of small changes, end to end, takes the app from “interesting prototype” to “thing I use without thinking about.” None of the changes are individually clever. None of them are the sort of thing you’d put on a landing page. But they’re the changes that come from using the thing every day.
The pattern, looking back: every change came from a friction I felt at the actual moment of using the app. The pantry felt noisy → calmer layout. I kept forgetting to deduct → mark as cooked. I was choosing meals without thinking about the weather → put the weather there. The data lied about pack sizes → pack sizes in the model. We kept arguing about takeaway over text → put the argument in the app.
The first article was the easy part. The hard part is making yourself notice the small lies, one at a time.
The shape is still the same. Four screens, three files, one question — what should I cook tonight, given what’s in the fridge?
Tonight it’s Carbonara. It’s going to rain.