Familiarity was a requirement
Finance teams work in these screens every day. Month end does not pause for a redesign.
Versapay moves money for finance teams. Two years of it ran through our hands: the platform’s move off Ruby onto React, the design system that made that move survivable, and the reporting product that shipped on top of it.
A legacy Ruby product had grown into different versions of tables, date pickers and warnings. Engineering was moving to React; design needed to write down one shared language.
Finance teams work in these screens every day. Month end does not pause for a redesign.
Variants map to component props, not just tidy Figma pages. If a component could not be built as drawn, the drawing needed to change.
Canned Reports became the first product built entirely on the new system.
Directions stayed visible and reviewable. The wireframe page became a record of how the shape was found, not a presentation of only the winning idea.
Both reports stay visible. Testers understood there were two products without opening anything, and switching took one click.
Tidier at rest, worse in use. It hid the second report; people asked for a feature that already existed.
Report generation is asynchronous. The interface needed its own copy, exit, and recovery path for the wait.
Report type, then a date range. Two decisions, no configuration screen.
A four-step load animation and a row you can leave. Closing the tab does not cancel the job.
The notification is the product. The CSV stays in the table until it expires.
An empty report is not an error. Show what was searched and offer a wider range.
Customer adoption and payment adoption answer different questions but use the same table, picker and download behaviour. Learning one teaches you the other.
Every axis maps to a prop: size, colour, state, variant. One button has 297 legal combinations. The library contains 35 component sets and 57 standalone symbols.
| Component | Specification | Axes / notes |
|---|---|---|
| Button | 297 variants | Size · colour · state · variant |
| IconButton | 216 variants | Size · font size · colour · state |
| Checkbox | 186 variants | Checked · indeterminate · size |
| TableCellRow | 144 variants | Hover · selected · columns · divider |
| TableHeadRow | 36 variants | Small · checkbox · columns |
| Typography | 25 variants | 13 roles · gutter |
| Tab | 18 variants | Active · orientation · icon |
| Table | 16 variants | Small · checkbox · small screen |
| Date picker | 502 instances | Closed · calendar · range |
| Load animation | 4 steps | Plus progress bar |
| Filter pill | 5 states | Default to applied |
| Empty state | No data match | Plus success banner |
Product typography: Inter. Roles range from 32/bold headings through 20/semibold sections, 16/medium navigation, 14/bold table headings and 13/regular body copy.
Once the language existed, teams could rebuild screens in React without a fresh design debate each time. Canned Reports shipped on the system and became the reference for other teams.
Components specified against React props kept design and code in step.
Structure and vocabulary stayed recognisable through the rebuild.
Report navigation was settled by user testing.
Reporting became the first product delivered on the new system.
The migration stopped being a redesign and became a checklist. A million-user platform could move forward with one language.
Versapay is the client. Interface examples are reconstructions from the production design file, preserved from this portfolio’s original case study. They describe the design work, not current production software.