Skip to main content
ENKO

Typography

Senco uses the system font stack for fast rendering on all platforms — especially budget Android devices where custom web fonts add latency.

Typography in a money app has one job that ordinary product typography does not: a figure has to be unmistakable. A user reading "1,250.00" as "1,25000" is not a legibility inconvenience, it is a failed transfer. Most of the rules below trace back to that.

Font families

RoleFont stackTokenUsage
Sans (primary)system-ui, -apple-system, "Segoe UI", sans-serif--ds-font-sansAll UI text — headings, body, labels, buttons, amounts
Mono (code only)ui-monospace, "SF Mono", "Cascadia Code", monospace--ds-font-monoCode blocks, tokens, developer-facing text
DocumentationArial, Helvetica, sans-serif— (see drift note)Emails, PDFs, external documents, help articles

Why system fonts

  • Zero download cost — no web font requests, instant text render
  • Native feel — text matches the OS, builds familiarity and trust
  • Performance — critical for users on 3G / low-end devices
  • Localization-ready — system fonts handle non-Latin scripts automatically
  • Pipeline-compatible — the Figma MCP server does not return custom typefaces. A system stack is the one choice that survives design-to-code without manual repair.

The performance argument is not theoretical. A webfont is a render-blocking request on the critical path; on a metered 3G connection it delays the moment a user can read their balance. Senco's audience is defined by that connection.

Why Arial for documentation

  • Universal availability — installed on every OS, renders in all email clients
  • Consistent rendering — PDFs, print, and email look identical across devices
  • Professional tone — clean, neutral, appropriate for financial documents
  • Email-safe — no fallback surprises in transactional emails (receipts, confirmations)

system-ui is not available in email clients, and a receipt that renders in Times New Roman on one client and Helvetica on another undermines the one document a user is most likely to forward to their bank.

The two stacks never mix

In-product text is always --ds-font-sans. Exported artifacts — receipt PDF, confirmation email — are always the documentation stack. A receipt shown in the app uses the product stack; the same receipt emailed uses Arial. The switch happens at the export boundary, not per component.

System UI — Primary
System UI
Token: --ds-font-sansStack: system-ui, -apple-system, "Segoe UI", sans-serifRole: All UI — headings, body, labels, buttons
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
0123456789
!@#$%^&*()_+-=[]{}|;':",./<>?
$ € £ ¥ ₦ ₱ ₹ ₩ ₫ ₵ ₸ ₺
Regular400
The quick brown fox jumps over the lazy dog
Medium500
The quick brown fox jumps over the lazy dog
Semibold600
The quick brown fox jumps over the lazy dog
Bold700
The quick brown fox jumps over the lazy dog
Arial — Email & Documentation
Arial
Token: --ds-font-docStack: Arial, Helvetica, sans-serifRole: Emails, PDFs, help articles, external documents
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
0123456789
!@#$%^&*()_+-=[]{}|;':",./<>?
$ € £ ¥ ₦ ₱ ₹ ₩ ₫ ₵ ₸ ₺
Regular400
The quick brown fox jumps over the lazy dog
Bold700
The quick brown fox jumps over the lazy dog
Do / Don't
Do
Ask for their info
We'll email them a secure form to fill in their payment details.
Their name
e.g. Yuki Tanaka

Clear heading hierarchy (H3, body, label) with distinct sizes and weights.

Don't
Ask for their info
We'll email them a secure form to fill in their payment details.
Their name
e.g. Yuki Tanaka

All text the same size with no hierarchy. Flat and hard to scan.

Type scale

Font: system-ui (--ds-font-sans) · Ratio: 1.200 (Minor Third) · Base: 16px · Grid: 4px line-height

Implemented size tokens

These are the values defined in ds.css. Every type role resolves to one of them.

TokenrempxRole
--ds-text-2xs0.625rem10pxDense metadata, badge micro-text
--ds-text-xs0.75rem12pxCaptions, helper text, timestamps
--ds-text-sm0.875rem14pxLabels, buttons, secondary body
--ds-text-base1rem16pxDefault reading text
--ds-text-lg1.125rem18pxLead text, H4
--ds-text-xl1.375rem22pxH3, card titles
--ds-text-2xl1.75rem28pxH2, section headings
--ds-text-3xl2.25rem36pxH1 (fixed)
--ds-text-display2.5rem40pxAccount balance, hero numbers
--ds-text-page-titleclamp(1.75rem, 1.25rem + 1.5vw, 2.063rem)28→33pxFluid page title

Letter-spacing rule

Larger text needs tighter tracking; smaller text needs looser tracking for legibility.

Size rangeLetter-spacingWhy
28px+ (Display, H1, H2)-0.02em to -0.01emSystem-ui splays at large sizes; tighten for cohesion
16–23px (H3, H4, Body)-0.01em to 0Neutral zone; system-ui optimized for this range
10–14px (Body Sm, Caption, 2XS)+0.005em to +0.02emCounter-forms close up at small sizes; open for readability

Only one tracking value is tokenized: --ds-tracking-tight (-0.02em), applied to Display and H1. The rest are applied per role.

Line-height rule

Size rangeLine-height ratioWhy
28px+ (headings)1.2x–1.3xLarge headings need tight leading; too much air disconnects
16–18px (body)1.5xWCAG 1.4.12 minimum for sustained reading
10–14px (small)1.5xSmall text needs generous leading, especially on low-DPI

Headings

LevelSizeWeightLine heightLetter spacingUsage
Display--ds-text-display 40px7001.2 (48px)-0.02emAccount balance, hero numbers
H1--ds-text-page-title 28→33px7001.21-0.02emPage titles
H2--ds-text-2xl 28px7001.29 (36px)-0.015emSection headings
H3--ds-text-xl 22px7001.27 (28px)-0.01emCard titles, subsections
H4--ds-text-lg 18px6001.33 (24px)-0.005emMinor headings, labels

H2 and H3 use weight 700 rather than 600 so they qualify as WCAG "large text" (bold ≥ 18.66px / 14pt), which lowers their contrast requirement to 3:1. H4 at 18px/600 does not qualify — it is semibold, not bold — so H4 must clear the full 4.5:1. This is the single most commonly missed contrast rule in the system, because the heading looks large.

Body

LevelTokenWeightLine heightLetter spacingUsage
body-lg--ds-text-lg 18px4001.56 (28px)0Emphasized body, lead text
body--ds-text-base 16px4001.5 (24px)0Default reading text
body-sm--ds-text-sm 14px4001.5 (21px)0.005emSecondary info, metadata
caption--ds-text-xs 12px5001.5 (18px)0.01emTimestamps, helper text
2xs--ds-text-2xs 10px5001.5 (15px)0.02emBadge micro-text, non-essential only

10px text is capped at non-essential content by rule. Nothing a user must read to complete or verify a transfer may be set at --ds-text-2xs. If a fact matters, it gets 12px minimum.

Weight scale

WeightTokenUsage
400 (Regular)--ds-font-regularBody text, descriptions, secondary content
500 (Medium)--ds-font-mediumSmall-text reinforcement, labels, navigation
600 (SemiBold)--ds-font-semiboldH4, card titles, form labels, buttons
700 (Bold)--ds-font-boldDisplay, H1–H3, account balances, primary emphasis

Four weights, no more. System stacks do not carry a reliable 300 or 800 across platforms, and a weight that renders on macOS and silently snaps to 400 on Android is a design decision that only exists on the designer's machine.

Responsive scaling

Headings scale up on desktop. Body text stays fixed — the reading task is the same size on every device.

LevelMobileDesktop
Display40px57px
H128px33px (via clamp)
H228px33px
H322px28px
H418px22px
Body and belowsamesame

H1 uses clamp() rather than a breakpoint so the page title scales continuously. Every other level steps at the breakpoint.

Do and don't

DoDon't
Set an amount at Display weight 700Set an amount in mono because it "looks financial"
Use H2 at 700 to earn the large-text contrast allowanceUse H4 at 600 and assume 3:1 is enough
Keep body at 16px on every deviceShrink body to 14px on mobile to fit more in
Use --ds-text-2xs for a badge countUse --ds-text-2xs for a fee disclosure
Let a long recipient name wrap to two linesTruncate a recipient name without a title attribute
Heading scale
Display
The quick brown fox jumps over the lazy dog
2.5rem / 700 / 1.2
H1
The quick brown fox jumps over the lazy dog
clamp(1.75rem, 1.25rem + 1.5vw, 2.063rem) / 700 / 1.21
H2
The quick brown fox jumps over the lazy dog
1.75rem / 700 / 1.29
H3
The quick brown fox jumps over the lazy dog
1.438rem / 700 / 1.22
H4
The quick brown fox jumps over the lazy dog
1.188rem / 600 / 1.26
Body scale
body-lg

The quick brown fox jumps over the lazy dog

1.125rem / 400 / 1.56
body

The quick brown fox jumps over the lazy dog

1rem / 400 / 1.5
body-sm

The quick brown fox jumps over the lazy dog

0.875rem / 400 / 1.5
caption

The quick brown fox jumps over the lazy dog

0.75rem / 500 / 1.5
xs

The quick brown fox jumps over the lazy dog

0.688rem / 500 / 1.5

Currency & numbers

Senco handles money — number rendering must be clear and consistent.

  • Use font-variant-numeric: tabular-nums for aligned columns in transaction lists, exchange rates, and amount displays
  • Use the system sans-serif font (not monospace) for all financial data
  • Account balances use Display size (40px) at weight 700

Why tabular, not monospace

Proportional numerals shift column alignment row to row, which makes a transaction list impossible to scan for an outlier. Monospace fixes alignment but reads as code and carries the wrong tone for a balance. tabular-nums gives fixed-width digits inside the system sans — alignment without the terminal aesthetic.

Amount composition

An amount is not one text run. It is composed so that magnitude dominates and denomination stays legible:

PartSizeWeightColor
IntegerDisplay / H2700--ds-heading
Decimal separator + centsOne step smaller700--ds-heading
Currency code--ds-text-sm600--ds-muted
Conversion line--ds-text-sm400--ds-muted

Cents step down one size rather than changing color. Dimming the cents makes the amount read as rounded, which in a fee breakdown is exactly the wrong impression.

This composition applies to amounts that are displayed — balances, receipts, fee rows. An amount being typed into a field is a single run at the field's size, because an <input> renders one font size.

Formatting rules

  • Group separators and decimal marks follow the recipient's locale on the receive side and the sender's on the send side. Both are labelled with the currency code so the two are never ambiguous.
  • Currency code (USD, NGN, PHP) always accompanies an amount. A bare number in a multi-currency product is a defect.
  • A displayed amount has a spoken form. The visible text keeps the compact code and is aria-hidden; a visually hidden sibling carries the currency name, so a screen reader says "1,250.00 US Dollar" rather than spelling out "U S D".
<span aria-hidden="true">1,250.00 USD</span>
<span class="sr-only">1,250.00 US Dollar</span>

This applies only to amounts that are displayed. An amount inside an editable field is read as typed, and its currency is supplied through the field's description — see Amount input.

  • Negative amounts use a leading minus and --ds-error-text, never parentheses. Parentheses are an accounting convention that does not survive screen readers.
  • Exchange rates render at exactly 6 significant figures; amounts render at the currency's minor-unit precision (2 for USD, 0 for JPY). See Tokens.
  • Times are absolute, 24-hour, in the user's local time zone. Never relative ("2 hours ago") — a relative time read later is wrong, and in a screenshot sent to support it cannot be answered.
  • Same calendar day: time only — 10:42. Earlier in the current year: 12 Mar, 10:42. Earlier years: 12 Mar 2025, 10:42. Month names come from the locale through Intl.DateTimeFormat, with hourCycle: "h23" fixed.
  • Status timestamps: a non-terminal status reads {Status} since {time} — Pending since 10:42. A terminal status reads {Status} · {time} — Completed · 12 Mar, 10:42.
Currency & number rendering

Tabular numerals keep columns aligned in transaction lists and amount displays.

Amount (large)$1,250.00
Amount (body)₩345,000 KRW
Rate1 USD = 1,380 KRW
Fee$2.50 transfer fee
Tabular nums250.00 → 345,000

Documentation typography

For emails, PDFs, and external documents, use Arial to ensure consistent rendering across all clients and devices.

LevelSizeWeightUsage
Doc heading24pxBold (700)Email subject, document title
Doc subheading18pxSemibold (600)Section headers
Doc body14pxRegular (400)Email body, document text
Doc caption12pxRegular (400)Footer, legal, disclaimers

Doc body is 14px where product body is 16px. Email and PDF are read at a closer viewing distance on a settled screen, not one-handed on a moving train.

Email example — Arial in context

Transfer confirmed

Your money is on its way to Adaeze O.

You sent$250.00 USD
Fee$2.50
Exchange rate1 USD = 1,380 KRW
They receive₩345,000
View transfer details

This is an automated message from Senco. Do not reply to this email.
© 2026 Senco Inc. All rights reserved.

Accessibility

  • Heading hierarchy must be sequential — no skipping levels (h1 → h2 → h3)
  • Body text line height minimum 1.5 for readability
  • Minimum readable text size: 10px (--ds-text-2xs) — weight 500, positive letter-spacing, non-essential content only
  • Text spacing must be adjustable without loss of content (WCAG 1.4.12)
  • Support assistive technology navigation via semantic heading structure
  • Contrast: 4.5:1 for normal text, 3:1 for large text (≥18.66px bold or ≥24px)
  • Sizes are set in rem, so browser and OS text-size settings scale the interface. No type token uses px.
  • Text must reflow without horizontal scrolling at 320px width and 200% zoom (WCAG 1.4.10)

Known gaps and drift

These are places where this spec and ds.css do not currently agree. They are listed rather than reconciled silently, because an agent generating code from this file needs to know which lines are contracts.

ItemSpec saysds.css hasResolution
Documentation font--ds-font-doc tokenNo such tokenAdd the token, or drop the token name from this spec — the Arial stack is currently applied by the export layer, not by a DS variable
Weight 300Previously documented for display numeralsNo --ds-font-light tokenRemoved from this spec; system stacks do not carry 300 reliably
Smallest sizePreviously 11px (0.688rem)--ds-text-2xs is 10pxThis spec now follows the token
H3 / H4 sizesPreviously 23px / 19px--ds-text-xl 22px, --ds-text-lg 18pxThis spec now follows the tokens
Line-height tokensRatios documented per roleNo --ds-leading-* tokens existRatios are applied per role in CSS; tokenizing them is open work