# Ludo PDA touch guide (Zebra TC27 class)

> How to build staff and vendor tools for handheld terminals in Ludo style: gloved-use target sizes in dp and mm, one-hand layout zones, offline and queue states, honest dark-surface guidance, and density rules.

Source of truth: `apps/ui/tokens/ludo.tokens.json` v0.1.0. Served from https://ui.ludo.computer/guides/pda-5in-touch.md. Generated 2026-07-26. Self-contained: every value is inline, no images required.

Device correction first: this guide's URL says 5 in, but the reference device class is the Zebra TC27, which has a 6.0 in display. All numbers below use the real spec (Zebra TC22/TC27 specification sheet, zebra.com): 6.0 in FHD+ 1080 x 2160, 403 ppi, 450 nits, multi-mode capacitive rated for bare or light-gloved fingertips, Gorilla Glass, IP68 and IP65, 1.5 m drop to concrete (MIL-STD-810H), -10 to 50 C, 3800 mAh standard or 5200 mAh extended battery.

The operator is standing in a queue, one hand on the device, the other scanning or taking payment, often in direct sun. Honest current state: the shipped touch affordance in the design system is the `data-density="touch"` variant in `registry/ludo/theme.css`; the handheld frame in the registry is a decorative mockup. This guide is the spec new PDA work must meet.

## Use these tokens

```text
Panel maths            403 ppi; 1 mm = 15.9 px; Android density bucket 440 (2.75x); 1 dp = 2.75 px
Touch target           60 dp minimum (165 px, ~10.4 mm) for gloved use; 8 dp+ spacing
                       Material's 48 dp (~8.3 mm) is a bare-finger size; gloves add 30 to 50%
Action row height      64 dp for full-width primary actions

Outdoor surface        #FAFAFA background, #09090B text (19.06:1)
Primary action         #5A3FD0 fill, #FFFFFF label (6.87:1); #7351EF fill (5.06:1) acceptable indoors
Queued / pending       #B45309 text on #FFFBEB (4.84:1)
Synced / success       #047857 text on #ECFDF5 (5.21:1)
Failed / error         #B91C1C text on #FEF2F2 (5.91:1); destructive buttons #DC2626 fill, white label (4.83:1)
Night mode text        #FAFAFA on #09090B; status text #34D399 / #FBBF24 / #F87171; purple #A589FF (7.22:1)
Figures                Geist Mono or tabular lining figures for amounts and counts
Typeface               Geist, weights 400 / 500 / 600 (cap 600)
Motion                 feedback under 100 ms; transitions 100 to 200 ms (instant / fast / base tokens); nothing decorative
Density                wrap the app subtree in data-density="touch"
```

## Layout

- Single column. One action per row. No side-by-side button pairs except a full-width confirm above a smaller cancel.
- One-hand zones on the tall 6.0 in screen: primary actions live in the bottom 40% of the screen where the thumb reaches. Nothing critical in the top corners. Keep destructive actions away from the scan-trigger side of the device so a squeezed grip cannot fire them.
- Screen padding 16 dp; 8 dp minimum between adjacent targets.
- Glanceable hierarchy: the one number that matters (amount due, queue count, balance) is the largest thing on screen. The operator reads it in under a second while walking.
- Every screen shows connection state. Reserve a persistent status line: online / offline, queued count, last-sync time.
- Vendor-facing screens may use the amber vendor reskin (`data-foundation="vendor"` in theme.css); admin and default stay Ludo purple. Foundation and density are independent: vendor + touch is the normal PDA combination.

## Type

Android sizes in sp so OS font scaling works. At default scale on this panel, 16 sp is about 2.8 mm em height, right at the comfort line for 350 mm viewing (cap height >= distance / 150), and sunlight plus walking eat the margin. Size up, not down.

| Role | Size | Weight | Notes |
|---|---|---|---|
| Primary figure (amount, count) | 32 sp | 600 | Geist Mono or tabular figures |
| Screen title | 20 sp | 500 | One line, truncate never; wrap if needed |
| Glance data (row amounts, statuses) | 18 to 20 sp | 500 | Readable mid-stride |
| Body | 16 sp | 400 | Minimum for running text |
| Secondary / meta | 14 sp | 500 | Hard floor. Nothing smaller, ever |
| Button labels | 18 sp | 600 | Sentence case, verb-first |

- Below 16 sp outdoors, use weight 500 or 600. Light small text disappears on a 450 nit LCD in sun.
- Respect the OS font-scale setting to 200%. Rows grow; layouts reflow rather than truncate.

## Colour

- The TC27 panel is 450 nits, mid-pack, not sunlight-proof (industry guides class 500 nits as invisible in direct sun). Push contrast to 7:1+ and default to light surfaces with near-black text outdoors: #09090B on #FAFAFA gives 19.06:1.
- Dark surfaces and battery, the honest version: the TC27 display is an LCD. The backlight runs regardless of pixel colour, so a dark theme saves no meaningful battery on this device. That saving is OLED-only. Position dark mode purely as a night-legibility feature for evening operations. The real battery levers are lower indoor brightness, short screen timeouts and minimal animation.
- Night mode palette: #09090B background, #FAFAFA text (19.90:1), secondary #A1A1AA (7.76:1), purple #A589FF not #825FFF (7.22:1 versus 4.77:1), status text #34D399 / #FBBF24 / #F87171.
- Status colours always pair with a word or icon ("Queued", "Synced", "Failed"). A third of male operators have some colour vision deficiency; amber versus green alone is not a signal.
- Purple is chrome and accent, not information. Transaction and payment semantics stay owned by the consuming app (the ludo-computer theme layer), not by this guide.

## Interaction

- Targets 60 dp minimum (about 10.4 mm physical) with 8 dp spacing. Basis: fingertip contact width is 8 to 10 mm (MIT Touch Lab anthropometry); the TC27 digitiser is rated for light gloves, and gloves add 30 to 50% to contact width. Material's 48 dp bare-finger minimum is roughly 20% under the gloved requirement on this panel.
- Feedback under 100 ms on scan and confirm actions: visual state change immediately, result when it arrives. Never block the UI on the network.
- Offline is a normal state, not an error. Venue radio coverage drops mid-shift. The shipped copy register for this state: "transactions queue on the device and post when the network returns." Match it.
- Every transactional screen shows: queued-transaction count, last-sync timestamp, and a per-record pending or synced status. No blocking spinner on any network call; the operator keeps trading.
- Confirmation over undo for money actions: a full-width confirm step before charging or settling, because gloved mis-taps happen and there is no hover to warn.
- Transitions 100 to 200 ms using the instant / fast / base duration tokens with cubic-bezier(0.4, 0, 0.2, 1). No decorative animation; it costs battery and attention.

## Do / Don't

- Do size targets at 60 dp (10 mm) for gloved hands with 8 dp gaps. Don't ship Material's 48 dp default; it is a bare-finger size.
- Do put primary actions in the bottom 40% of the screen. Don't use top navigation bars or top-corner actions on a device held one-handed at arm's length.
- Do default to light surfaces with #09090B text outdoors. Don't ship the web dark theme expecting battery savings on this LCD.
- Do show queued count, last-sync time and per-record status on every transactional screen, with the register "transactions queue on the device and post when the network returns". Don't block on a spinner when the radio drops.
- Do respond to every tap within 100 ms. Don't make the operator wonder whether the scan registered.
- Do use 16 sp body, 18 to 20 sp glance figures, 14 sp floor. Don't go smaller to fit more rows.
- Do pair every status colour with a word or icon. Don't signal queued versus synced by colour alone.
- Do keep copy operational and short: "Charge AUD 12.50", "3 queued". Don't use em dashes, "guest", or vague labels like "Continue".

## Checklist before shipping

- [ ] All targets at least 60 dp with 8 dp spacing, verified on the physical device with gloves
- [ ] Primary actions in the bottom 40%; destructive actions clear of the scan-trigger side
- [ ] Body 16 sp minimum, glance data 18 to 20 sp, nothing under 14 sp
- [ ] Layout survives OS font scale at 200% without truncation
- [ ] Outdoor mode: light surfaces, 7:1+ contrast, no text lighter than #3F3F46
- [ ] Night mode uses #A589FF purple and foregroundDark status shades; dark mode not sold as a battery feature
- [ ] Offline state on every transactional screen: queued count, last-sync timestamp, per-record status
- [ ] Copy uses the register "transactions queue on the device and post when the network returns"
- [ ] Every tap answered within 100 ms; no blocking spinners on network calls
- [ ] data-density="touch" applied; no em dashes; "visitor" never "guest"; weight cap 600 respected
