Sizing
Sizing defines fixed dimensions, container widths, aspect ratios, and touch targets for Senco components.
Where spacing governs the gaps between things, sizing governs the things themselves. The two rules that matter most in this product are that a control must be hittable by a thumb on a moving bus, and that a fixed height must never be what clips a translated label.
Component dimensions
Fixed heights for core Senco components.
| Component | Token | Height | Notes |
|---|---|---|---|
| App bar | --ds-size-appbar | 48px | Fixed, contains brand + action icons |
| List item | --ds-size-list-item | 60px | Avatar (36px) + 12px vertical padding |
| Bottom nav | --ds-size-bottomnav | 52px | Icon (20px) + label + padding |
| Avatar | --ds-size-avatar | 36px | Recipient initials, circular |
| Input | --ds-input-height | 48px | Default control height |
| Input (dense) | --ds-input-height-sm | 36px | Filter bars, inline edit — never a primary field |
| Touch target | --ds-size-touch | 48px | Minimum tappable area |
Icon sizes
| Token | Size | Pairs with |
|---|---|---|
--ds-size-icon-sm | 16px | --ds-text-sm and below — inline icons |
--ds-size-icon | 20px | --ds-text-base — navigation and action icons |
--ds-size-icon-lg | 24px | Standalone icons, empty-state marks, app bar |
Icon size pairs with text size, not with available space. A 24px icon next to 14px text reads as the icon shouting; a 16px icon next to 18px text disappears. When an icon stands alone, size it to its touch target instead — a 20px icon centered in a 48px hit area.
Icon set
Icons come from lucide-react (1.x). No other icon source is used; country flags are images, not icons.
| Rule | Value |
|---|---|
| Size | --ds-size-icon-sm / --ds-size-icon / --ds-size-icon-lg, passed as size |
| Stroke | Library default (2), never overridden per instance |
| Colour | currentColor, unless a spec names an icon token |
| Beside a text label | aria-hidden="true" |
| Standalone | The control carries the accessible name; the icon stays aria-hidden |
Canonical mapping. A spec that names a meaning uses this icon, and a new meaning is added here before it is used anywhere.
| Meaning | lucide export |
|---|---|
| Pending | Clock |
| Processing, loading spinner | LoaderCircle |
| Sent | ArrowUpRight |
| Completed, selected, success | Check |
| Failed, caution, crypto risk | TriangleAlert |
| Error note | CircleX |
| Cancelled, close, remove | X |
| Info | Info |
| Tip | Lightbulb |
| Regulatory disclosure | ShieldCheck |
| Biometric disclosure | Fingerprint |
| External link | ExternalLink |
| Expand, dropdown | ChevronDown |
| Forward, drill in | ChevronRight |
| Search | Search |
| Add | Plus |
| Retry | RotateCw |
| Show / hide password | Eye / EyeOff |
| Upload | Upload |
| Download | Download |
| Share | Share |
| Date | Calendar |
| Camera capture | Camera |
| QR scan | ScanLine |
| Manual entry | Keyboard |
Check means both "completed" and "selected". In both cases it answers "is this done", and a second check shape would make users compare two near-identical glyphs.
Height is a minimum, not a fixed value
Every height in the table above is a min-height. Senco ships in multiple languages, and German or Tagalog labels routinely run 30–40% longer than English. A hard height on a button turns that into clipped text; a min-height turns it into a two-line button, which is ugly and legible instead of tidy and broken.
The only true fixed heights are the app bar and bottom nav, which are chrome and contain no wrapping content.
Why 60px list items
A transaction row carries a 36px avatar, two lines of text, and an amount. At 56px the two text lines touch the row edge; at 64px a mobile screen shows five transactions instead of six. 60px is 36 + 12 + 12 — the avatar plus space-3 above and below — so the number is derived from the scale rather than chosen.
48px radio cards + 48px CTA. Every target is comfortable to tap.
Cramped 28px controls. Users struggle to select the right option or hit the button accurately.
Container sizing
Containers constrain content width.
| Token | Max width | Usage |
|---|---|---|
--ds-container-xs | 320px | Mobile forms, modals |
--ds-container-sm | 480px | Narrow content, transfer flow |
--ds-container-md | 640px | Default content width |
--ds-container-lg | 960px | Dashboard, wide layouts |
--ds-container-xl | 1200px | Full-width desktop |
--ds-container-max | 1400px | Absolute page ceiling |
Why the transfer flow is capped at 480px
The transfer flow is a single-column sequence of decisions. Widened to 640px it starts to look like a form that could be filled in any order, and users begin scanning rather than stepping. 480px is roughly one phone width — it keeps the desktop experience the same shape as the mobile one, which matters because most users do the same transfer on both.
Measure
Body copy targets 45–75 characters per line. At 16px in the system stack that lands between roughly 480px and 720px, which is why container-md (640px) is the default for content and container-lg is reserved for layouts that split into columns rather than run long text.
Aspect ratios
Consistent ratios for images, cards, and media containers.
| Ratio | Usage |
|---|---|
| 16:9 | Hero banners, promotional cards |
| 3:2 | Transaction cards, detail images |
| 4:3 | Thumbnails, compact media |
| 1:1 | Avatars, profile images |
| 3:4 | ID document uploads |
3:4 exists for identity documents specifically. A passport page and a national ID card are both closer to portrait than to any of the landscape ratios, and a capture frame that does not match the document shape produces uploads that get rejected by verification.
Touch targets
- Minimum size: 48 × 48px (
--ds-size-touch) - Minimum gap between targets: 8px (
space-2) - The visual element (icon, button) can be smaller than 48px — the tappable area must not be
- Applies to all devices receiving touch input
Standards
| Criterion | Requirement | Senco |
|---|---|---|
| WCAG 2.2 SC 2.5.8 Target Size (Minimum), AA | 24 × 24px | Exceeded |
| WCAG 2.1 SC 2.5.5 Target Size, AAA | 44 × 44px | Exceeded |
| Senco standard | — | 48 × 48px |
48px is above every requirement on purpose. The AA minimum of 24px is a floor for any interface; a transfer confirmation tapped with a thumb while walking is not the case that floor was written for. The cost of an accidental tap in this product is a misdirected payment.
Expanding a target without moving the visual
A 20px icon button keeps its 20px icon and gains its hit area through padding, not through a larger glyph:
visual 20px icon
padding 14px each side
target 48 × 48px
Where layout cannot afford the space — a dense table row action — the target is expanded with a pseudo-element overlay instead of by growing the box.
Do and don't
| Do | Don't |
|---|---|
| Give every icon button a 48px hit area | Ship a 24px icon with a 24px target |
Use min-height on controls | Use height and let a long label clip |
| Keep 8px between adjacent targets | Butt two destructive actions against each other |
| Size icons to their text | Size icons to fill the space available |
| Cap the transfer flow at 480px | Let the flow stretch to the viewport on desktop |
Known gaps
| Item | Status |
|---|---|
| Dense control height (36px) | Below the 48px touch standard — permitted only for pointer-primary surfaces, not documented per component |
--ds-size-touch enforcement | Convention; no lint rule or test verifies it |
| Aspect ratio tokens | Ratios are documented but not defined as --ds- variables |