KQ-1774 · TAX/PROFILE

Tax Profile

A billing-details form with a live invoice header beside it: every field a labelled native input in reading order with its error inline, the preview re-typesetting as you type and lighting the line of the field you are in. A well-formed tax ID runs a checking pulse along the field's lower edge while it is looked up; a registered number fills the legal name, which lifts out of the note under the field and glides into the invoice header on springs.glide, landing with a springs.flick settle and a wash of the accent while the Verified badge pops on springs.snap. Not-found numbers and an unanswering registry keep the entry with a note, a failed check digit is an inline error, and on a narrow container the preview folds away behind a toggle.

KQ-1774 · TAX/PROFILE

Billing details

Printed on every invoice from Waylight Labs Ltd

VAT number, country and digits
9 digits after GB. Checked as you type.
Printed under the address when set.

No changes

try gb 204 718 369 · or 990 317 427 for a registry that does not answer

Install

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

$ pnpm dlx shadcn@latest add @kinetiq/tax-profile

Props

fieldStyle
"boxed" | "line" | "filled"
default "boxed"
How the inputs are drawn: bordered boxes, an underline only, or soft filled wells.
previewStyle
"classic" | "modern" | "compact"
default "classic"
The invoice header preview: seller left and title right, a large title with two blocks, or one tight strip.
accent
string
default "var(--accent-bright)"
Any CSS colour: focus edges, the checking pulse, the landing wash and the invoice rule.
company
string
default ""
The legal company name the form starts with.
address
TaxAddress
default defaultTaxAddress
The billing address the form starts with: { line1, line2, city, postalCode, country }.
taxId
TaxIdValue
default { country: "GB", number: "" }
The tax ID the form starts with; spaces and a repeated country prefix in the number are ignored.
email
string
default "billing@fernworks.studio"
The billing email the form starts with.
reference
string
default ""
The purchase order reference the form starts with.
values
TaxProfileValues
default —
Controlled form values ({ company, taxId, email, address, reference }); when set, the starting-value props are ignored.
onValuesChange
(values: TaxProfileValues) => void
default —
Fires from every edit and from a registry fill, with all the values.
countries
TaxCountry[]
default defaultTaxCountries
Countries for the tax ID prefix and the address: { code, name }.
seller
TaxSeller
default defaultTaxSeller
Who issues the invoices, shown in the preview: { name, lines, taxId, email }.
invoiceNumber
string
default "INV-2026-0418"
The invoice number in the preview.
now
number
default defaultTaxNow (17 Oct 2026)
The current time in ms, the preview's issue date (read in UTC). Never read from the clock.
lookup
(taxId: string) => Promise<TaxLookupResult>
default a seeded registry of four companies
Looks a full tax ID up, e.g. "GB204718369"; a rejected promise reads as an unanswering registry.
checkDelay
number
default 900
How long the default lookup takes, in ms.
onValidate
(result: TaxLookupResult) => void
default —
Fires with every check outcome: valid (with the registered name), not-found, unreachable or invalid.
onSave
(values: TaxProfileValues) => void | Promise<void>
default —
Save was pressed with valid values; return a promise to show Saving… and its outcome. With errors, focus goes to the first invalid field instead.
onCancel
() => void
default —
Cancel was pressed; an uncontrolled form also returns to its last saved values.
status
"ready" | "loading" | "error"
default "ready"
Whether the saved details have arrived.
onRetry
() => void
default —
Try again was pressed after the details failed to load.
height
number
default 540
Height of the surface in px; its body scrolls inside.
disabled
boolean
default false
Nothing can be edited or saved.
className
string
default —
Classes for the outer surface.