Holders Business design system

Product UI for business accounts. Quiet surfaces, exact numbers, one accent. Desktop browser first, phone second. Black and white come from holders.io; type is Mona Sans (extended for display) with Geist Mono for figures and addresses; petrol blue is the one accent.

Colour

Neutrals do the work. Black is for primary actions and finished states. Petrol blue marks what is selected, focused or in progress. Status colours appear only where they carry meaning, always with a label or shape.

Neutrals
19.2:1
Ink--ink #0e0f11Text, primary button, done
11.0:1
Ink 2--ink-2 #3a3c41Body copy, secondary text
5.8:1
Muted--muted #62656dLabels, captions, placeholder
3.2:1
Faint--faint #8e9097Disabled, decimals in display only
Line strong--line-strong #d2d1ccInput borders, empty track
Line--line #e6e5e1Dividers, panel borders
Canvas--canvas #f5f5f2App ground, sidebar, context rail
Surface--surface #ffffffWork area, panels, inputs
Accent and states
7.0:1
Accent · petrol--accent #255e7eFocus, links, selection, in progress
6.6:1
Positive--pos #1d6a44Credited, verified, copied
6.2:1
Warning--warn #875700On hold, needs attention
7.3:1
Negative--neg #a3271fErrors, declined, destructive
Aa
Accent soft--accent-softSelection, info banner, focus halo
Aa
Positive soft--pos-soft
Aa
Warning soft--warn-soft
Aa
Negative soft--neg-soft
DoPrimary action in black. Accent for the one thing that is current: the selected nav item, the active step, the focused field.
Don'tAccent-coloured buttons next to black ones, coloured page headers, gradients, glass, or the Solana green and purple outside asset logos.

Type

Mona Sans, one variable family: extended (width 125, weight 700) for the balance and display, width 118 / 650 for headings, normal width for everything people read. Geist Mono for standalone figures, addresses, hashes and references. In tables one face per row: amounts stay in Mona Sans, mono only for the shortened address. Chosen 2026-11-10 (B2 in type.html).

balance 54/1 · 700 · wdth 125 · -0.025em
display 44/1 · 700 · wdth 125Held by you.
h1 34/1.1 · 650 · wdth 118Open a business account
h2 22/1.2 · 650 · wdth 118Deposits
h3 18/1.3 · 650 · wdth 118Company details
body-lg 16/1.55 · 400Start with your work email. You will use it to sign in.
body 14/1.5 · 400Default for tables, forms and panels. Desktop data UI reads at 14.
label 13/1.4 · 500Registration number
caption 12/1.4 · 400Use your company address. We send a confirmation link to it.
figures · Geist Mono 450
address · Geist Mono 400 · 13723ac6e51e95db20105e1d82a88718febff26ca292f67b4ff9b110cd2f32ebf5

Space, radius, depth

A 4px base. Tight inside a group, wide between groups: 6px label to input, 20px between fields, 32 to 40px between field groups, 48px between page sections.

s1 · 4
s2 · 8
s3 · 12
s4 · 16
s5 · 20
s6 · 24
s8 · 32
s10 · 40
s12 · 48
s14 · 56
s20 · 80
r-xs 4 · track, kbd
r-sm 6 · chips, copy
r-md 8 · inputs, banners, nav
r-lg 12 · panels, dialogs
pill · buttons only
Flat · panels sit on a border, never a shadow
--e-pop · menus, popovers
--e-dialog · dialogs, side sheets
Density
controls 36 (forms in app: 40) · table row 48, compact 40 · nav item 34 · sidebar 240 · page max 1120 · panel padding 16/20 · touch targets on phone ≥ 44

Motion

Motion only explains a change of state. 120ms for colour and hover, 180ms ease-out for dialogs and sheets entering. No entrance animations on page content, no shimmer skeletons. The in-progress segment of a settlement track breathes slowly; everything stops under reduced motion.

--ease cubic-bezier(.22,1,.36,1) · --d-fast 120ms · --d-base 180ms · prefers-reduced-motion: all off

Buttons

Pill shape carried from holders.io. One primary per view. Labels name the action: "Add funds with USDT", not "Submit".

primary
hover
focus
loading
disabled
secondary
hover
ghost
danger
icon
lg 40 · auth, final actions
md 36 · default
sm 28 · inside banners, rows
text action

Fields and validation

Label above, help below, error replaces help in place so the layout does not jump. Validate on blur, re-validate on input once an error shows. Errors say what is wrong and how to fix it.

Use your company address.
Enter an email like name@company.com.
From Companies House. Wrong?
$per month
Segmented control · filters, not navigation

Form layout for desktop

Long forms are field groups, not one centred column. Each group: fields on the left in a two-column grid, a context rail on the right that says why we ask and what counts. Short fields share a row; addresses take the full width. On phone the rail folds under the heading.

Company

From Companies House

We filled these from the public register. Change anything that is out of date; we check it against the register again.

Where you operate

Where the team works day to day.

Why we ask

A different operating address needs one proof document later: a utility bill or bank statement from the last 3 months.

Saved 11:06

Steppers

Vertical stepper sits in the left column of a wizard and doubles as navigation back to finished steps. The compact bar is for headers and phone. Numbers only, no "Step 3 of 5" heading above the page title.

  1. Company detailsDone
  2. 2Business profileIn progress
  3. 3Controlling persons
  4. 4Documents
  5. 5Review and sign
2 of 5 · Business profile
5 of 5 · Review and sign

Status and progress

A chip names the state in words and shape. The settlement track is the product's signature: four segments for received, checked, converted, credited. It sits in every deposit row, so a table answers "where is my money" without opening anything.

CheckingConvertedCreditedOn holdReturnedDraftLink sent

progress · done · hold · fail · neutral. Square and triangle marks keep hold and fail readable without colour.

Checking
Converted, crediting
Held at conversion
Credited

Money

Tabular figures, right-aligned in columns, two decimals always. Every sum is drawn by the <hb-amount> component (amount.js, ported from Postcash): separators at 50%, cents at 75%, code after the figure, never wrapped, "—" when missing. Estimates carry ≈ and muted ink until final.

Display · balance
Row · crypto amount
Estimate · pending
≈
You sent
Rate
1 USDT = 1.00 USD
Conversion fee 0.50%
Credited

Addresses and hashes

Full string in Geist Mono, never truncated where someone will send money to it. The first and last four characters are boxed, because that is what people check. In tables, truncate in the middle. Copy confirms in place for 1.4s.

TQ7mWk3nRZp8VfL2xYb9cD4eHs6JtUa1G
723ac6e51e95db20105e1d82a88718febff26ca292f67b4ff9b110cd2f32ebf5TRONSCAN
TXd4…6Utytable formcopied state

Tables

Every table is hb-table: one text face per row, mono only for the shortened address, 44px single-line rows, headers 12/500 muted and never wrapped. Dates and amounts sort (real buttons, ↕ on hover, ▲/▼ when active). Columns menu hides and reorders, the layout is saved per table, the first column stays pinned when the table scrolls inside its box. Middle-drag pans. Row tones are opaque.

DateFromStatusProgressAmountCredited
Today, 14:32TXd4…6UtyChecking
Nov 5, 09:21TXd4…6UtyOn hold · Below minimum
Nov 8, 10:14TXd4…6UtyCredited
Nov 3, 12:10TZp1…k9QaReturned · Wrong network
Loading and empty rows span the visible columns by themselves
DateFromStatusAmount
DateFromStatusAmount

Timeline

For one object's history: a deposit, an application. Done steps are filled ink, the current step is a petrol dot, held is an amber square. Time sits right-aligned in tabular figures; the detail line says what happened in numbers.

  1. Received10:14 on TRON
  2. Checked10:1519 confirmations, risk check passed
  3. ConvertingnowAt 1 USDT = 1.00 USD
  4. Credited≈ after 0.50% fee
  1. ReceivedNov 4, 13:05
  2. Checked13:06
  3. On hold13:06Conversions are paused. It converts by itself when they resume; nothing to do.
  4. Credited

Banners

Full-width at the top of the area they concern. Bold lead sentence states the fact; the rest says what happens next. One action at most. No side stripes.

Empty and error states

An empty state is the first-run screen: say what will appear here and give the one action that fills it. Left-aligned inside the area, dashed outline, no illustration.

No deposits yet

Send USDT on TRON to your deposit address. It shows here within about a minute and is credited as USD.

No deposits match "On hold"

Every deposit is either in progress or credited.

Email card

Transactional email from Holders Business: one subject, one fact, one button. The same card is the mock inbox in prototypes. Amounts inside sentences use <hb-amount inline>.

Dialogs, sheets, menus

A dialog only to confirm something irreversible or to sign. Details open in a side sheet so the list stays in view. On phone both become a bottom sheet. Esc closes; focus returns to the trigger.

Side sheet: 420px, slides from the right edge over the table, --e-dialog, same header and footer anatomy as the dialog.

Remove David Chen?

He will no longer get an identity-check link. You can add him again before you sign.

App shell

Sidebar on the canvas tone; the work area is one white surface inset by 8px with a hairline and 12px radius. Company switcher at the top, Anna's identity at the bottom, the current item gets a white chip with a petrol icon. Below 860px the sidebar becomes a top bar with a menu.

Holders Business
USD balance

Open the account home reference →

Auth layout

Two columns, 7 to 5. Left: brand, the task, one form, left-aligned at a reading measure of 440px. Right, on canvas: what happens next and what to have ready, in the product's own facts. No centred card, no marketing panel, no stock imagery. On phone the context follows the form.

Brand · sign-in link
Context rail: steps with time, documents to prepare, what the account does

Open the sign-up reference →