XPayBack·2023

We had ten ways to draw a button. None of them agreed.

Building the foundations and core components for XPayBack's user app — typography, colour, spacing, iconography, and a token pyramid that future versions could build on without inheriting the v1 inconsistencies.

RoleProduct Designer — Foundations
CompanyXPayBack
TypeDesign System · Mobile
StatusShipped to v2; codification in progress
Building a Design System for User App — XPayBack

The stakes

XPayBack was already live. Real users were transacting. The app had been built by successive designers and engineers over a couple of years — each shipping fast, none with a system to anchor on — and that had compounded into measurable inconsistency: the same component drawn six different ways across pages, font sizes that didn't ladder, colour values picked from screenshots, no token structure at all.

Two things made this urgent. A v2 redesign was in flight, and without a system the v2 components would diverge from v1 the same way v1 components had diverged from each other — the debt would just compound a generation. And every new feature was re-asking the same answered questions: what colour for this badge, what radius for this card, what font weight for a section title. The cost wasn't a single bad screen. It was the time tax on everything downstream.

The problem

XPayBack's user app has accumulated visual inconsistency across typography, colour, iconography, and components because there is no shared token foundation — every new screen re-derives decisions that should already be settled, and the v2 redesign is about to inherit the same debt unless we put a system under it first.

What we found

We divided the team into sub-groups and ran an end-to-end audit of foundations and components — typography, colour, iconography, images & illustrations, elevation/spacing, corner radius, buttons, app bar, cards, lists, input fields, selectors. The pattern was the same everywhere: the same purpose, multiple executions.

Typography

Different line heights for the same font. Odd sizes (11px) sitting between scale steps. Sentence case and normal case mixed within the same component.

Colour

Different colours for the same component (text, offer tags, etc). Several components failed contrast — the colour values had been picked visually, not measured against accessibility standards.

Iconography

Filled, stroke, single-tone, dual-tone icons all coexisting. Icon sizes ranged from 12px to 32px in no particular system. Same icon, different colours in different places.

Buttons

Sentence case, capital case, and normal case all in use. Text buttons had inconsistent line height. Colours drifted across primary, secondary, and destructive variants.

Cards & lists

Elevation and corner radius weren't defined — components inside cards had different styles and placements. Lists had inconsistent spacing, varied icon sizes, and contrast issues in some states.

Input fields

Box-style and line-style fields used interchangeably. The filled state used both 14px and 16px text without a clear rule for when to use which.

Options considered

Option A — Patch the existing components

Rejected

Cheaper in the short run, but doesn't fix the absence of a token foundation. Every patched component still derives its colour, type, and spacing from one-off decisions — so the same drift starts again the next time someone ships a feature. The debt compounds.

Option B — Adopt a third-party system (Material / Carbon)

Rejected

Mature, accessible, free — but doesn't match XPayBack's brand language, and a replatform onto a third-party component library was a months-long engineering commitment we couldn't justify. The product needed a system that looked like XPayBack, not like Material.

Option C — Homegrown foundations + token pyramid + componentisation

Chosen

Audit-driven. Token pyramid for the foundations so the same decisions get reused everywhere. Component layer rebuilt on top of those tokens. Cheaper to maintain than third-party, cheaper to evolve than patches.

Tradeoffs

No component tokens in v1

We shipped the token pyramid with three layers — Values → Global → Alias — and deliberately stopped there. Component tokens (color-text-inputfield-filled, etc) are the most powerful layer but also the highest-maintenance one, and this was the team's first time owning a token system. We accepted the cost: components inherit from Alias tokens directly for now, which means a few duplicated decisions and slightly noisier component overrides. Component tokens get added as the system matures and the team is comfortable with the layers below.

Material Icons + brand customisations, not a bespoke icon set

Designing every icon from scratch would have taken weeks we didn't have. We adopted Material Icons as the base, locked the system to 16×16 with 2px safe area, 4px rounded corners, 1.5px stroke, and worked with the brand team for the icons that genuinely needed to look like XPayBack. The cost: a few icons feel generic next to the brand. Worth it to ship the foundations on time.

Use-case naming over component-coupled naming

We considered naming tokens after components — color-background-buttonprimary, color-text-inputfield-filled — and rejected it. Component-coupled names get stale fast: rename the component and every token that references it has to change too. We chose use-case names instead — color-brand-default, color-text-primary, color-icon-primary — so the same token survives a component rename. Tradeoff: developers occasionally have to look up which token a particular component uses, instead of inferring it from the name.

What we built

01

End-to-end audit framework

Split the team into sub-groups, one per foundation or component family. Each sub-group catalogued every instance of their assigned element across the live app, recorded what was wrong, and produced a single document the whole team could review. The audit is what made the next 27 slides of the deck possible — without it, every later decision would have been argued on opinion.

Audit framework — foundations and components catalogued
02

Token pyramid — Values, Global, Alias

A four-layer model — Values → Global → Alias → (Component) — with the component layer intentionally deferred. Raw hex values feed Global tokens (neutral-900), Global tokens feed Alias tokens (color-text-primary, color-icon-primary, color-background-primary-inverse), and components read directly from Alias. Naming is use-case-driven, not component-driven, so a button rename doesn't break the colour story underneath.

Token pyramid — Values, Global, Alias, Component layers
03

Type ramp on a 4px grid

Gilroy as the default typeface; Poppins for headings, Inter and Syne for decorative work. Six type roles — heading, body, button, label, decor-1, decor-2 — each with its own scale. Line height = 1.5× font size (golden ratio), snapped to the 4px grid so vertical rhythm stays consistent.

Type ramp — heading, body, button scales with line height
04

Colour system grouped by purpose

A global colour palette as the superset, then subsets categorised by usage — Brand, Text, Background, Border, Icons, Utility, Supportive. Contrast measured against WCAG, not eyeballed. The colour story is what fixed the largest single category of audit findings.

Colour system — global palette grouped by usage buckets
05

Spacing and corner-radius scales

Spacing: 2 / 4 / 8 / 12 / 16 / 20 / 24 / 32 / 40 — all multiples of 4 so layouts compose predictably. Corner radius: 0 / 4 / 8 / 12 / 16 / 20 / 24 / 32. Both narrow enough to be memorable; wide enough to cover every component in the audit.

Spacing and corner-radius scales on a 4px grid
06

Componentisation + SPEC files

Buttons, selectors, input fields, chips, snackbar, navigation, app bar, cards, lists, and bottom sheets rebuilt on top of the foundations. Every component shipped with a SPEC file documenting anatomy, usage rules, margin and padding, alignment, and do's and don'ts — so a developer or a new designer can implement the component correctly without asking the original designer.

Redefined components and SPEC files — app screens rebuilt on the new system

Design targets

1shared token foundation across foundations and components, where there were none
10+components rebuilt on the new system with SPEC files for each
v2redesign anchored to the system rather than re-deriving decisions

The point of the system isn't a number on a dashboard — it's the cost of every screen that comes after it. The success signal we're looking for is boring: a designer ships a new screen and never has to ask "what shade of grey, what radius, what font weight" — they read it off the system. We stress-tested the system by asking the team to recreate existing screens using only the new components; that's when we caught the gaps and refined the spec files.

What's next

Codify every component in code

Design QA is done for a subset; the rest is in flight. The goal is parity between the Figma library and the codebase so a token change in design propagates without a manual translation step.

iOS in parallel

Foundations are platform-agnostic; components aren't. The iOS componentisation runs in parallel to the Android codification so we don't ship a half-systemised cross-platform app.

Component tokens, eventually

The deliberately-deferred fourth layer of the pyramid. Once the team is comfortable with Alias tokens, component tokens get layered on for the components that need them — input fields and buttons first, because they have the most state variants.