KQ-1674 · MARGIN/HELP

Margin Help

A form with a help rail: focusing a field glides its help card down the rail until it sits level with that field, on a spring whose landing is springiness — from springs.glide, which settles without overshoot, to springs.recoil, which lands with two bounces — while the card's content cross-fades in one cell, its measured height glides and its notch slides along the edge to keep pointing at the field. A field without help leaves the rail empty, an error opens as a band in the card on springs.snap, the Help switch glides the rail's width away, and below a 40rem container the help becomes an expandable line under each field. Every field's help is its description for assistive technology, the help link sits in the tab order right after its field, and Escape on the link returns to the field.

KQ-1674 · MARGIN/HELP

Wholesale account

Coldbrook Roasters · step 2 of 3

The legal name on your registration, which may differ from the name over the door.

e.g. Halden Street Roasters Ltd

Find your registered name

Nine digits, printed under your business name on the registration letter. Spaces are fine.

e.g. 482 117 093

Where to find it

Delivery slots and invoices go here. A shared inbox means nobody misses a drop.

e.g. orders@haldenstreet.co

Sets your price tier. You can change it after the first month without a new application.

e.g. Most cafés start at 5–20 kg.

See the price tiers
Optional
Optional

Anything the driver needs: gate codes, step-free access, hours to avoid.

e.g. Rear door on Mill Lane, buzzer 2. Before 9am please.

2 of 4 required answers done

help · business name · 2 of 4 required

Install

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

$ pnpm dlx shadcn@latest add @kinetiq/margin-help

Props

fields
MarginHelpField[]
default defaultMarginHelpFields
The form: { id, label, kind?, required?, placeholder?, options?, validate?, help?: { text, example?, link? } }.
value / defaultValue
Record<string, string>
default — / defaultMarginHelpValues
Answers by field id, controlled or initial.
onChange
(values, fieldId) => void
default —
Fires from the keystroke or choice that changed an answer.
onSubmit
(values) => void | Promise
default —
Send was pressed with every answer valid; a promise holds the button, a rejection keeps every answer.
onFieldFocus
(fieldId) => void
default —
The field the rail follows changed.
onHelpLink
(fieldId, link, event) => void
default —
A help link was followed; call event.preventDefault() to handle it yourself.
showHelp / defaultShowHelp
boolean
default — / true
Whether the rail shows, controlled or initial.
onShowHelpChange
(show) => void
default —
Fires from the Help switch.
railWidth
number
default 240
The help rail's width in px.
cardStyle
"card" | "note" | "margin"
default "card"
A raised card with a notch, a tinted note with an accent edge, or a bare margin note.
springiness
number
default 0.35
How the card lands, 0 (glide, no overshoot) to 1 (recoil, two bounces).
accent
string
default "var(--accent-bright)"
Links, the focused label, the note's edge and Send — any CSS colour; buttons take its hue at a fixed lightness so white text reads in both themes.
heading
string
default "Wholesale account"
The form's heading and accessible name.
subheading
string
default "Coldbrook Roasters · step 2 of 3"
The line under the heading.
submitLabel
string
default "Send application"
The submit button's label.
status
"ready" | "loading" | "error"
default "ready"
Loading shows skeleton fields; error shows Try again.
onRetry
() => void
default —
Try again was pressed after the form failed to load.
height
number
default 540
The form's height in px; the fields and the rail scroll together inside it.
disabled
boolean
default false
Nothing can be typed or sent.
className
string
default —
Classes for the form.