Design guide
Based on Apple's Human Interface Guidelines
(developer.apple.com/design), adapted for our users: field staff on mid- and
low-end Android phones, often outdoors in strong sunlight, often on weak
signal. The Payments screen is the reference implementation; building blocks
live in src/ui/grouped.js.
Principles
- Content first. The interface steps back: no decorative borders, shadows or boxes around everything. Names, amounts and status lead.
- Clear hierarchy. One large title per screen, grouped sections with small uppercase headers, quiet secondary text. A screen should read top to bottom without hunting.
- Consistency. The same row, sheet, button and icon style everywhere, so learning one screen teaches the rest.
- Respect the platform. Borrow Apple's polish, keep Android's behaviour.
- Our users' words, not designers' words. Say exactly what the person does, in the team's own vocabulary: "Choose dealer", not "Who paid?"; "Policy", not "Scheme". If a field is needed, the label says so ("required") — an unfamiliar or optional-looking label gets skipped.
- Optional second language beside English where it helps: section
headers, the main action button, the screen's one-line purpose, and error
messages. Turn it on with
secondaryLanguageinconfig/brand.json; strings are wrapped withur()/SHOW_SECOND_LANGfromsrc/config. English stays first; the second language is a second line, never a replacement. Theurdutext style insrc/ui/grouped.jsis the second-language style (it uses the system font, sized ~3 pt larger for right-to-left scripts).
Type — Inter
Inter is the open-licence face closest to SF Pro (SF Pro itself may only be
used on Apple platforms). Loaded in App.js, four weights only.
| Style | Size / line | Weight | Use |
|---|---|---|---|
text.largeTitle |
34 / 41 | Bold | Screen title (collapses on scroll) |
text.title |
22 / 28 | Bold | Big numbers, sheet titles |
text.headline |
17 / 22 | SemiBold | Bar titles, emphasis |
text.body |
17 / 22 | Regular | Row labels and values |
text.subhead |
15 / 20 | Regular | Secondary lines |
text.footnote |
13 / 18 | Regular | Section headers (uppercase), footers |
text.caption |
12 / 16 | Regular | Small labels under a value |
Android ignores fontWeight with a custom font, so use font.regular /
medium / semibold / bold (separate faces), never fontWeight.
Colour
| Token | Value | Use |
|---|---|---|
g.bg |
#F2F2F7 |
Screen background behind groups |
g.cell |
#FFFFFF |
Group rows |
g.label |
near-black | Primary text |
g.secondary |
grey 72% | Values, footers (≈5.4:1 — readable outdoors) |
g.tertiary |
grey 45% | Placeholders, chevrons |
g.tint |
#0369A1 (our accent) |
Actions, links, selected state |
g.danger / success / warning |
system-style red / green / orange | Status only |
Apple's own blue (#007AFF) is only ~4:1 on white — too faint in sunlight —
so we keep our darker accent as the tint. Icon tiles may use the iOS system
hues (indigo, green, orange) because the glyph on them is white.
Layout
- Grouped sections (
GroupedSection+GroupedRow): white rounded group (radius 12) on grey, 16 px side margins, header above and footer below. - Rows are at least 52 px tall (Android's minimum is 48). Label left, value or control right, chevron when it opens something. Separators are hairlines that start where the text starts.
- Large title (
LargeTitle+LargeTitleBar+useLargeTitle): big title at the top of the scroll; a slim bar with the title fades in once it scrolls away. - Segmented control (
Segmented2) for 2–4 mutually exclusive choices (payment method), instead of chips. - Sheets: grabber on top, title centred, Done top-right. Choosing from a list happens in a sheet, not an inline dropdown.
- No hidden gestures. Every action has a visible control (a red ✕ to
remove, a button to add). Our users don't discover swipes;
SwipeRowexists but is not used for anything a user must be able to do. - Shortcuts, not choices. When a field is free text (policy), the typing box comes first and looks like a box; common values sit below it as optional one-tap shortcuts, labelled "Or tap a common one".
- One primary button per screen: filled tint, 52 px, radius 12.
Motion and feedback
Short, purposeful, ease-out; everything goes instant with Reduce motion
(src/ui/motion.js). Light haptics on selection, success haptic + sound on a
saved payment.
What we deliberately don't copy
- Back: never rely on the iOS swipe-from-left-edge; Android's back button and gesture must work on every screen.
- Date and time pickers: Android's own, not iOS wheels.
- Liquid Glass / blur: real-time blur is slow on low-end Android and hard to read in sunlight. Translucent bars use a near-opaque fill instead.
- SF Symbols: Apple-only. We use Ionicons, outline when idle, filled when active.