Brand book
08 · UI foundations

Rows on hairlines

The app’s structure comes from DESIGN-V2: rows on hairlines, 64px rows, no cards or shadows, one shared Row and one shared Sheet. Hearth changes the colours and the title face, not the structure. Every example below is live: tap it.

Spacing

A 4px base

space-1 · 4px
space-2 · 8px
space-3 · 12px
space-4 · 16px
space-5 · 20px
space-6 · 24px
space-8 · 32px
space-12 · 48px
space-16 · 64px
Page gutter
20px on phones, rows run full bleed.
Inside a row
4px between the two lines, 12px top and bottom, 64px minimum height.
Between controls
8px minimum clearance around any secondary target.
Sections
A Sunken section head, no extra gap. The hairline does the separating.
Radii

Mostly square, softly rounded where you touch

none · 0px
Rows, section heads, full-bleed bars
sm · 6px
Checkboxes, small tags
md · 12px
Buttons, inputs, cookie banner
lg · 20px
Sheet top corners, phone frames in marketing
pill · 999px
“Your shop” marker only
icon · 22.37%
App icon (iOS squircle approximation)
Rows

One template, every screen

Row anatomyExample data
Item namemetadata · provenance$0.00unit or change
Top left: name, Inter 17/600Top right: figure, Inter 17/600 tabular Bottom left: meta, 13 Ink 3Bottom right: unit price or change, 13
Rows in useExample data
Tonight
Spaghetti bologneseThu · 40 min · 4 serves · You have 7 of 9 ingredients$3.40a serve
Fruit and vegetables · 2
Broccoli2 heads · Coles, checked 30 Sep$2.98$1.10 less
Greek yoghurt1kg · Woolworths, checked 30 Sep$7.4040c more
Pantry · meat and fish

Estimated from the category. Chicken keeps about 2 days in the fridge.

Chicken thighsCheck before using: bought 13 Aug
Beef mince, 500gUse by Saturday
Four fixed zones
Name, figure, meta, unit or change. Zones never swap between rows. Let the figure shrink (“$3.40”, with “a serve” below) so names get the full width.
Height
min-height: 64px, never a fixed height, so text spacing can grow without clipping.
Tap target
The whole row, edge to edge. One control per row.
Surfaces
Rows are Raised (#FFFFFF) on Paper with a 1px Hairline. Section heads are Sunken with an 11px caps label. No cards, boxes, tinted bands or shadows.
Accessibility
One screen-reader stop per row: “Broccoli, 2 heads, $2.98, Coles, checked 30 September”.
Buttons

One primary action per screen

Plan this week

Primary. Action fill, white Inter 17/600, 52px tall, 12px radius. One per screen.

Plan this week

Pressed. Action pressed #8A3310, in 120ms. No scale, no glow.

Plan this week

Focus. 2px Action outline, 2px offset.

Choose shops

Quiet. Raised, 1px Field edge, Ink text. For secondary actions.

See how it works

Text link. Action, underlined. Tertiary actions.

Continue

Unavailable. Sunken with Ink 3, plus a line saying why: “Choose at least one shop”. Never a pale orange that looks broken.

Do

Plan this week

Don’t

Two orange buttons on one screen, an outline button that looks like a second call to action, or Title Case.

Inputs

Fields you can actually see

Field
Raised fill, 1px Field edge (#8E8377, 3.71:1), 12px radius, 52px tall, Inter 16 so iOS doesn’t zoom.
Label
Always visible above the field, Inter 15/600. Placeholders are hints, never labels.
Required
Mark the optional ones “(optional)”. No lone asterisks.
Focus
2px Action outline.
Error
2px Price rise edge, an icon and a sentence that names the cause and the fix. Explain before the button is pressed when possible.
Checkbox
24px, 1.5px Field edge, 6px radius; ticked fills Action with a white tick.
Sheets

One shell, four ways out

Shopping list
Tap the row to open its sheet
Fruit and vegetables
Example data
Shape
Raised, 20px top corners, a drag handle, Cocoa backdrop at 45%. No shadow.
Detents
Opens at about half height, drags to full.
Dismiss
X button, Escape, tapping the backdrop, and swipe down. All four, always.
Never stacked
A sheet never opens another sheet. Replace the content instead.
Content
The depth: per-store prices with unit pricing, change since last buy, last bought, freshness with its reason.
Motion
220ms, Hearth ease-out. Scroll locks behind.
Price and coverage patterns

Every total shows what it covers and when it was checked

9:41
Example data
Shopping list
41 items from this week’s 7 dinners
What this list costs
Coles Your shopfor 38 of your 41 items$151.40
Woolworthsfor 37 of your 41 items$154.75
ALDI Your shopfor 31 of your 41 items$119.85

Each total covers different items. Tap a shop to see what it leaves out. Prices checked Wed 30 Sep; in-store prices may differ.

Fruit and vegetables · 3
No price yet · 2
SumacWe couldn’t match this at any of your shops
Masa harinaWe couldn’t match this at any of your shops
Coverage: the four statesExample data
Full coveragefor 41 of your 41 items$151.40
Partial coveragefor 37 of your 41 items$154.75
Below 60%Prices for 6 of 41 items · not enough to compare
No coverageNo price yet · we’ll add it when the weekly prices land
Totals
Figure plus “for 38 of your 41 items”, in the same row.
Provenance
“Coles, checked 30 Sep” on every price.
The note
Under the totals, always: totals cover different items, prices were checked on a date, in-store prices may differ.
Unmatched
Collected in a visible “No price yet” section with their names. Never dropped.
Comparison
“42c less at Coles. Coles $2.98, Woolworths $3.40.”
Per serve
“$3.40 a serve” for plans, from typical prices, said once in the header.
Never
“Cheapest”, a winner badge, a crown, or ranking a shop below 60%.
In store

The trolley state

Bottom bar and syncExample data
12 of 41 in the trolleyTicked so far $48.70
Synced 2:14 pm
Saved on this phone · 3 changes waiting to sync

One hand, a trolley in the other. The counter and running total sit at the bottom. Ticks are saved on the phone first and never rolled back. Never say “offline” and never block with a modal.