KQ-1670 · THREAD/FIELDS

Thread Fields

A single-page form where a follow-up question appears directly beneath the answer that asked for it. Choosing that answer opens the follow-ups to their measured height on springs.glide while a thin thread draws from the chosen option down the gutter and hooks into each new question's label, its reveal riding the same value as the height so it reaches each label as the label arrives; changing the answer pulls the thread back up on the exit ease as those questions fold away, keeping their answers, and the new answer's thread drops from its own option. Choice questions are radiogroups whose arrows move and choose, checkbox groups move by arrow and toggle with Space, and nested follow-ups indent with threads of their own.

KQ-1670 · THREAD/FIELDS

Book a studio

Coldbrook Studios · Fernworks Row

2 of 6 answered
What are you booking?

Up to 16 in the print room.

Do you need equipment?
Will anyone need step-free access?optional

workshop · 2 follow-ups · 2 answered

Install

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

$ pnpm dlx shadcn@latest add @kinetiq/thread-fields

Props

questions
ThreadQuestion[]
default defaultThreadQuestions
Every question, top-level and follow-up: { id, label, kind: "choice" | "multi" | "text" | "textarea" | "number" | "email", options?, required?, placeholder?, hint? }.
conditions
ThreadCondition[]
default defaultThreadConditions
{ when, is, show }: the answer (or any of the answers) of `when` that reveals the questions in `show`. A question named in any `show` is a follow-up.
values / defaultValues
Record<string, string | string[]>
default — / {}
The answers, hidden follow-ups' included (they are kept), controlled or initial.
onValuesChange
(values) => void
default —
Fires from the edit that changed the answers, with all of them.
onChange
(id, value, visible) => void
default —
Fires from each edit with the question, its value and only the answers now visible.
onReveal
(ids: string[], by: string) => void
default —
Follow-ups appeared, with the question whose answer revealed them.
onSubmit
(answers) => void | Promise<void>
default —
Submit with every visible required question answered; receives only visible answers. A promise shows progress; a rejection keeps everything and offers Try again.
threadStyle
"elbow" | "curve" | "stitch"
default "elbow"
How the thread is drawn: square turns, a single swoop to each follow-up, or dashed tacking stitches.
indent
number
default 24
How far follow-ups sit in, in px: the gutter the thread runs in. Capped at 16 below 34rem.
accent
string
default "var(--accent-bright)"
The chosen option, the thread and its beads, any CSS colour.
title
string
default "Book a studio"
The form's heading and accessible name.
subtitle
string
default "Coldbrook Studios · Fernworks Row"
A quiet line under the heading.
submitLabel
string
default "Request booking"
The submit button's label.
height
number
default 520
Panel height in px; the form scrolls inside it.
status
"ready" | "loading"
default "ready"
Loading draws placeholder questions.
disabled
boolean
default false
Nothing can be answered or sent.
className
string
default —
Classes for the outer container.