KQ-1717 · NUMBER/MATCH

Number Match

A new-device approval drawn as the two screens it happens on: the browser signing in, showing a two-digit number, and the trusted phone asking whether it's you with three numbers to choose from. The right choice joins the screens — a line grows from the number and from the phone's frame level with the chosen key, each half on glide, and they meet in the middle where a dot lands on recoil — while both screens turn to success and a check stamps on recoil; a wrong one reaches in danger, stops short and draws back on a tween as both screens explain the block. The choices are buttons with roving focus (arrows, Home, End; Enter or Space chooses), outcomes are announced, a lifetime bar drains only while it can be seen, and the screens sit side by side, overlap or mirror, stacking in a narrow container.

KQ-1717 · NUMBER/MATCH
fieldline.app/sign-in

Check your phone

Tap 47 on your phone to finish signing in.

14:32
FieldlineSign-in request

Are you trying to sign in?

  • Desktop browser on a laptop
  • Near Porto, Portugal
  • 14:32 · rae@fieldline.dev

Tap the number shown on your other screen.

waiting · tap 47 · Fieldline sign-in

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/number-match

Props

request
{ number, choices?, device, place, time, account }
default defaultNumberMatchRequest
The sign-in being approved: the number shown, the phone's choices (seeded when omitted), and what, where and when.
status / defaultStatus
"waiting" | "approved" | "blocked" | "denied" | "expired"
default — / "waiting"
Where the request stands, controlled or initial.
onStatusChange
(status) => void
default —
Fires from the choice, the denial or the expiry that changed it.
onApprove
(request) => void
default —
The right number was chosen.
onDeny
(request, reason: "mismatch" | "denied") => void
default —
The request was stopped: a wrong number, or "No, it's not me".
onExpire
(request) => void
default —
The request ran out of time before anyone chose.
onRetry
() => void
default —
Try again or Send a new request on the signing-in screen; pass a new request to start over.
expiresIn
number
default 60000
How long the request lives, in ms.
timeZone
string
default "UTC"
The time zone the request's time is shown in.
appName
string
default "Fieldline"
The product's name, on both screens.
signInUrl
string
default "fieldline.app/sign-in"
The address in the signing-in window's bar.
secureHref
string
default "#security"
Where Change your password goes after a block or a denial.
layout
"split" | "overlap" | "mirror"
default "split"
The two screens side by side, the phone over the window's edge, or the phone first; they stack in narrow containers.
numberSize
number
default 56
The number's size on the signing-in screen, in px; the tile's padding scales with it.
accent
string
default "var(--accent-bright)"
Any CSS colour for the number, the waiting highlights, the lifetime bar and the app marks.
disabled
boolean
default false
The choices and the denial are unavailable.
className
string
default —
Classes for the outer wrapper.