Try it first
You’re Asha. Dinner is in 25 minutes.
Two people haven’t answered. Say you’re coming, nudge someone, then switch to them and watch the nudge arrive. The household is fictional; the interface is the real one.
The real interface on made-up data. It runs in your browser and resets on reload.

Overview
Home Hub is a private app I designed and built for my own household of four. It does two things: it gets everyone to the table at mealtime, and it keeps one shared grocery list.
Getting the features to work was never the hard part. The hard part was getting four people who already had a perfectly good fallback, texting each other, to keep using something new after the first week. So every decision here was judged by one question: does this make the app the path of least resistance, or one more thing to abandon?
I was also one of the four users. I wasn’t studying usage from a distance. I was watching it happen at the dinner table.
The problem
Four people, two iPhones and two Android phones. “Is everyone eating tonight?” was shouted up the stairs. The grocery list lived in four different chat threads, and nobody knew which one was current.
A home-built tool has no captive audience. Nobody signed up, nobody’s onboarding is tracked, and going back to the old way costs nothing. The risk was never “will this feature work”. It was “will this still be open on everyone’s phone in a month”.
Before
A meal call that depends on who is within earshot. A list split across chats. Duplicate purchases, forgotten ones, and no idea what was spent.
After
Every phone rings at mealtime and each person answers with one tap. One list everyone can see, tick off and add to from the shop.
Constraints became design input
The decision: a web app for the iPhones, a native app for the Android phones, and the iPhone’s own Clock app as the alarm.
Putting a native app on an iPhone needs a paid Apple developer membership, which we declined. And Safari cannot schedule a local alarm at any price. Rather than fight that, I designed around it: iPhones get an installable web app whose notification is a heads-up, and the family sets mealtimes as recurring alarms in Clock, which does override silent mode. Android gets a native app that can ring properly.
The other constraints were human. Some of the four are older readers. The app is used one-handed, mid-errand, often on bad supermarket signal. That set the type size, the tap targets and the rule that every tap updates the screen first and saves afterwards.
| At mealtime | Android app | iPhone web app |
|---|---|---|
| Rings at the exact time | Yes, scheduled on the phone | Usually within seconds, sometimes minutes late |
| Full-screen alarm that loops | Yes | No. A single banner |
| Works on silent | Yes | No. Apple forbids it |
| Works with no network | Yes | No. It needs a connection |
| The workaround | Not needed | A recurring alarm in the Clock app does the ringing |
Principles: “Enamelware”
The decision: one calm visual language where each colour means exactly one thing. Deep spinach ink, porcelain surfaces and a single egg-yolk yellow: the look of a kitchen tin. It favours legibility and big tap targets over density.
Escalate by shape, not just colour
Urgency adds an edge, a fill or a weight. A list still reads correctly in greyscale, before any word is read.
Flat by default
Only things that really float above the page cast a shadow: the dock, a toast, a lifted sheet.
Say it once
If a fill already separates a label, it isn’t also uppercased. If a bar marks a row, the row isn’t also tinted.
44-pixel tap targets
Even where the visible control is smaller. It’s used with one thumb, in a shop aisle.
Meals: answering without lying
The decision: travel sets a soft default, but a household outing is a hard lock.
When someone marks themselves as travelling, their meals default to “not eating” and their phone stays quiet. But they can still tap a real answer for any meal, because plans change and a person should never be locked out of telling the truth about themselves.
“Everyone’s out” is different. It locks every meal card until someone switches it off, because it means nobody is home to answer anything. The asymmetry is deliberate: one is a guess about a person, the other is a fact about the house.
Try it
Mark yourself away, then try to answer anyway
Set a return date, go back to Meals and notice you can still override the default. Then switch on “Everyone’s out” and see the difference.
The real interface on made-up data. It runs in your browser and resets on reload.

Nudges: ask a person, not “everyone”
The decision: design for follow-through, not just delivery.
The first version sent a notification at mealtime, and that was enough for week one. Then I noticed two things at the table. Some notifications were missed. More telling: people would answer “on my way” and then not turn up. The notification had done its job, the app had done its job, and dinner was still late.
I treated that as a trust problem, not a volume problem. Instead of a louder alert, anyone can nudge a specific person, and after mealtime the app lists who is “still not at the table”. Tapping a name opens a sheet for that one person: nudge them, or mark their answer for them. That replaced a dropdown and four small buttons, and was chosen from four mockups.
Try it
Nudge Kabir, then become Kabir
One visitor can’t be four people at once, so use “View as” under the phone. Nudge someone as Asha, switch to them, and their nudge arrives.
The real interface on made-up data. It runs in your browser and resets on reload.

Grocery: honest numbers
The decision: keep the daily action frictionless, even at the cost of richer data.
Ticking an item off is a two-second gesture, and it stays that way. Price and shop are optional and asked for afterwards, never before. The moment a core habit needs a form, people quietly stop.
Spend categories are a closed list, so the totals don’t split across typos. Free text lives in an optional note instead. And an item someone has been nudged about appears in one place only, the “needs a hand” card, rather than twice on the same screen.
Try it
Do a small shop
Add something, put it in the cart, mark it bought, then log what the trip cost.
The real interface on made-up data. It runs in your browser and resets on reload.

History: don’t show noise
The decision: hide the charts until they can say something true.
Spending trends stay hidden until there are about 40 purchases or 4 shopping runs to draw from. A half-empty chart reads as broken or misleading, and either one erodes trust in everything else the app says. Showing nothing until it could show something honest mattered more than shipping the feature sooner.
Once the charts appear, they double as the filters. Tap a category bar or a month tile and the whole page narrows to it, so there is no separate filter panel to learn.
Try it
Six months of made-up shopping
Tap a bar in “Spend by category”, then switch to Year and tap a month.
The real interface on made-up data. It runs in your browser and resets on reload.

Small interactions, real care
The decision: swipe to delete with Undo, instead of an “Are you sure?” dialog.
A confirm dialog taxes every deletion to protect against the rare mistake. Undo taxes only the mistake. Here, Undo works by waiting: the note isn’t actually removed until the toast has gone. If the app closes first, the note survives. When something has to fail, that is the safe way for it to fail.
Try it
Delete a note, then change your mind
Swipe a private note left. On a computer, drag it with the mouse.
The real interface on made-up data. It runs in your browser and resets on reload.

Process
The decision: mockups first, and every change logged with its why.
Any change to what a screen looks like starts as a set of mockups, built from the app’s real stylesheet at phone width with realistic data. One option is picked before anything is built. Wording gets the same treatment: when a label has several states, such as counts, plurals and empty, each one is drawn.
Every change is then written down with the reasoning behind it, especially where the obvious approach was rejected. Decisions get reversed in the open. A shared private notepad was replaced by individual private notes within a day of shipping, and the log says why.
Write the why, not a restatement of the diff. The diff is already in git; the reasoning is not.
From the project’s own working rules
A system, not screens
The decision: one design system, mirrored by hand into the Android app.
Two front ends share one database, so every colour, threshold and closed list has to match in three places, and nothing enforces it. The Android app fell behind because nobody had to say out loud what each change meant for it. Now every change to the web app states its Android consequence: ported, not applicable, or deferred with a reason. Parity was lost to fifteen deferrals, then won back.




Outcomes, and what I’d do next
Home Hub is in daily use by all four of us. The early build stayed deliberately narrow, meals and groceries and nothing else, until that one loop was clearly sticking. Earning daily use first meant each later feature was met with trust instead of fatigue.
The most important decision wasn’t a feature. It was noticing, by living with the tool, that the real failure wasn’t people ignoring a notification. It was a commitment quietly slipping between “I said yes” and “I showed up”. Designing for that gap is what turned Home Hub from something my family tolerated into something they rely on.
An honest gap
The Android app matches the web app screen for screen, but it has never rung on a real phone. Until it has, I treat it as unproven, and I prefer designs that fail visibly.
Next
Get the Android alarm ringing on a real device. Then admin and member roles, which are designed and waiting.
