Design principles
Four principles behind every design decision in Senco.
Principles earn their place by resolving arguments. Each one below is stated with the trade-off it accepts, because a principle that costs nothing decides nothing. When two conflict, the order on this page is the tie-breaker: clarity beats speed, speed beats constraint optimization, and all three beat path simplification.
Clarity first
Every screen answers three questions immediately: Where am I? What can I do? What just happened? Moving money across borders requires trust — show everything, hide nothing.
- Use clear, specific labels — "Send 250 USD" not "Submit"
- One primary action per screen to reduce decision fatigue
- Status feedback is immediate and unambiguous
- Avoid jargon — Senco users may not be native English speakers
- Show exact fees and exchange rate before confirmation
- Break down costs visually (transfer fee + FX markup = total)
- Use recognizable status labels (Pending → Sent → Completed)
What this costs
Clarity costs screen space and it costs conversion. A fee breakdown shown before confirmation loses some users at the last step who would have completed if the total had been a single number. That is the intended outcome: a user who would not have sent had they seen the fee should not be sending.
Trust is built on bad news, not good news
Any product can present a successful transfer well. Trust is decided by how the product behaves when the rate moved, when the transfer is delayed, when verification was rejected. Those screens get the same care as the happy path — specific reasons, an explicit next step, and no euphemism. "Your transfer could not be completed" is not clarity; "The recipient's account number was rejected by the receiving bank" is.
In practice
| Clear | Unclear |
|---|---|
| "Send 250.00 USD → 412,500 NGN" | "Continue" |
| "Arrives by Thursday, 14 Mar" | "Arrives in 2–3 business days" |
| "Fee 3.50 USD + rate margin 1.20 USD" | "Fees may apply" |
| "Rejected — the ID photo was too blurry to read" | "Verification unsuccessful" |
| "Sent — the bank is processing it" | "Done" |
Every line item visible: status, fee, rate, received amount. Show exact costs upfront — no surprises.
Fees may apply. See terms and conditions for details on applicable charges and exchange rates.
Vague language hides costs. User has no idea what they'll pay.
Speed over features
Users send money under pressure — reduce steps, not capabilities.
- Core transfer flow is 3 taps: Amount → Recipient → Confirm
- Pre-fill with last-used values (currency, recipient)
- Show exchange rate comparison inline, not behind a "more info" link
- Skeleton screens during loading — never blank screens
What this costs
Every feature request is measured against the tap count of the core flow. Scheduled transfers, split payments, and multi-recipient batches have all been deferred not because they lack value but because each one adds a decision to a flow that most users complete under time pressure. The system optimizes for the ninetieth transfer, not the first.
Speed means perceived speed
A 400ms response that shows a skeleton feels faster than a 250ms response that shows nothing. The product is measured on when the user can act, not when the request resolves — which is why text renders before images, cached data renders before fresh data, and the primary action is tappable while an entrance animation is still running.
Where speed is deliberately surrendered
The confirmation step. It cannot be skipped, it cannot be triggered by a stray Enter key, and it does not pre-select. One irreversible action in the product, and it is the one place friction is correct.
Saved methods with inline summary — select, review fee, and confirm. No extra steps.
Force the user to navigate to a separate rates page.
Design for constraints
Senco users often deal with slow or unstable connections and lower-end phones. Every screen must work fully with zero images loaded.
Core rule
Images are enhancement, never content. Financial data, transaction status, and actionable UI must render and operate with zero images. This is the baseline, not a fallback.
Touch targets
Touch targets are 48px minimum. Users may have damaged screens or use the app while moving — generous tap areas prevent errors on critical financial actions.
Text-first rendering
Critical content (balances, transaction status, amounts) loads as text first. Images like avatars and banners are lazy-loaded only when scrolled into view. The user can already act on their data before a single image appears.
Progressive image strategy
| Breakpoint | Max image size | Format |
|---|---|---|
| Mobile (≤600px) | 15 KB | WebP |
| Tablet (≤1024px) | 40 KB | WebP |
| Desktop (>1024px) | 80 KB | WebP + JPEG fallback |
Use srcset with at least 3 sizes. Never serve a single large image to all devices.
Save-Data mode
When the user has Data Saver enabled (Save-Data: on header), Senco strips non-essential images — no promotional banners, initials instead of profile photos, text rates instead of chart visualizations. The page remains branded and clean, not broken.
Cache-first loading
A service worker caches static assets and recent data. On repeat visits, the app renders instantly from cache while fresh data syncs in the background. Image cache is budgeted at 10–15 MB maximum — prioritize financial data over decorative assets.
Offline resilience
When offline, Senco shows cached data with a clear timestamp ("Updated 3 min ago"). Avatars become initial circles, banners become text. All financial information remains accessible. Transfers queue locally and sync when connectivity returns.
Stale data must announce itself
Cached data without a timestamp is worse than no data, because a user cannot tell a balance from three days ago from a balance from three seconds ago. Every cached financial value carries its age. An exchange rate older than its validity window is shown struck through with a refresh action, never shown as current.
What this costs
The system gives up webfonts, decorative imagery, hero animation, and chart-heavy dashboards. The interface is plainer than a competitor designed for a flagship phone on office wifi. That is the deliberate trade: the product has to work on the device the user actually has.
It also constrains the design toolchain. The Figma MCP pipeline does not return images or custom fonts — which costs nothing here, because the system does not use either.
Text-first: names, amounts, and status render immediately. Country flags are tiny PNGs that load fast.
Everything loads together — user stares at empty skeletons while images block the render.
Respect Save-Data header — strip banners, use tiny flags, show text rates. 120 KB per session.
Ignoring Data Saver wastes expensive bandwidth on assets the user explicitly opted out of.
Show cached history with clear timestamps. User can still view transactions and status offline.
Full-screen 'No internet' dead-end. User can't see balance, can't see history, can do nothing.
Service worker caches UI assets. Repeat visits render instantly — only fresh data hits the network.
No caching — same 350 KB banner re-downloads every visit. 8+ seconds on 2G.
48px minimum for all interactive elements.
Small targets cause errors on financial actions.
System fonts, SVG, WebP budget, CSS animations — minimal payload.
Web fonts + icon packs + uncompressed images + animation libraries = 755 KB on 3G.
One path, not many
Reduce cognitive load by offering one clear path to each goal.
- Transfer flow is linear (no branching wizards)
- Bank vs. Crypto selection is a comparison, not a choice tree
- Settings are grouped by task ("Notifications", "Security") not by feature
- Navigation is flat: max 2 levels deep
Comparison, not configuration
Bank versus crypto is the clearest example. As a choice tree it asks the user to understand settlement rails before they can send money. As a comparison it shows both options side by side with the two numbers that decide it — total cost and arrival time — and the user picks a row. Same capability, no expertise required.
What this costs
Power users lose shortcuts. There is no way to skip ahead, no saved templates, no advanced mode. The flow is the same length on the hundredth transfer as on the first. Pre-filling recovers most of that cost, and it recovers it without adding a second path to maintain.
Two levels, no more
Navigation depth is capped at two. A setting that needs a third level is in the wrong group, and the fix is to regroup by task rather than to nest. This cap is also what keeps breadcrumbs unnecessary on mobile.
Smart default — surface the most recent contact, let users change if needed.
Too many equal choices. User has to evaluate 5 options before acting.
How to apply these principles
When making a design decision, ask in order:
- Is it clear and transparent? Can the user understand what's happening without instructions? Are all costs and status visible upfront?
- Is it fast? Can we remove a step, pre-fill a field, or show info earlier?
- Does it work on constraints? Will it load on a 3G connection with a low-end phone?
- Is there one path? Are we adding complexity that could be a simpler flow?
If the answer to any question is no, redesign before shipping.
Resolving conflicts
The order is the tie-breaker. Two worked examples:
| Conflict | Resolution |
|---|---|
| Showing the full fee breakdown adds a step (clarity vs. speed) | Clarity wins — the breakdown stays, and speed is recovered by pre-filling the amount |
| A live rate chart helps users decide but costs 60KB (clarity vs. constraints) | Clarity wins on the information, constraints win on the form — the rate comparison ships as text, not as a chart |
| Power users want a saved-recipient shortcut (speed vs. one path) | One path wins — instead of a second flow, the existing flow pre-fills the last recipient |
What these principles are not
They are not a style guide, and they do not decide visual questions. A principle cannot tell you which radius to use — that is what Tokens is for. If a debate about a component can be settled by a token, it is not a principles question.