KQ-1730 · QUESTION/TRAIL

Question Trail

A conversational intake, one question at a time: each question is typed first, then its bubble rises a step from its tail corner on springs.snap, and the option you pick travels into place as your answer chip on springs.glide. Pressing an earlier chip rewinds — the later bubbles fold back up into it bottom-first in a cascade, heights closing on glide while their words lift away on the exit ease — and answering again replays the trail forward with every kept answer until a question has none, so a changed branch only asks what is new. Options are a roving group of buttons (arrows move, Enter sends, Escape keeps the old answer) and text questions are labelled fields that say what is wrong.

KQ-1730 · QUESTION/TRAIL

Book a call-out

Basinworks · answers kept as you go

2 of 6
  1. Basinworks asks: Hello. What's the trouble?

  2. Because you said A leak

    Basinworks asks: Where is the water coming from?

  3. Basinworks asks: How soon do you need someone?

Tap an answer to change it; later ones are kept.

2 answered · leak branch · waiting on how soon

Install

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

$ pnpm dlx shadcn@latest add @kinetiq/question-trail

Props

questions
QuestionTrailQuestion[]
default defaultQuestionTrailQuestions
{ id, prompt, short?, kind?, options?, placeholder?, optional?, next?, validate? } — next is an id, a map from answer to id (a branch), or null to end.
value / defaultValue
Record<string, string>
default — / {}
Answers by question id, controlled or initial; "" is a skipped optional question. The trail opens at the first unanswered one.
onValueChange
(answers) => void
default —
Fires from the press, Enter or Skip that changed an answer, with every answer kept.
onAnswer
(questionId, value, answers) => void
default —
Fires once per answer given.
onComplete
(answers, path) => void
default —
Fires when the last question on the path is answered, with the path's ids — again after a rewind that completes it.
onRewind
(questionId) => void
default —
Fires when an earlier answer is opened again.
onSave
(saved: { answers, at }) => void
default —
Fires from Save for later with the answers and the question waiting.
storageKey
string
default —
Writes a saved trail to this device's storage under this key and picks it up after mount.
bubbleStyle
"soft" | "outline" | "plain"
default "soft"
Filled bubbles with a tail corner, hairline bubbles, or a plain transcript on a left rule.
density
"compact" | "regular" | "roomy"
default "regular"
Gaps, padding and type size.
accent
string
default "var(--accent-bright)"
Answer chips, the composer's focus, progress and the save mark — any CSS colour.
typingDelay
number
default 420
How long the host types before a new question arrives, in ms.
host
string
default "Basinworks"
Who is asking; named to assistive tech before each question.
title
string
default "Book a call-out"
The intake's heading.
closing
string | (answers) => string
default a thank-you that repeats the phone number
The host's last line once every question on the path is answered.
status
"ready" | "loading" | "error"
default "ready"
Loading shows placeholder bubbles; error explains and offers Try again.
onRetry
() => void
default —
Try again was pressed after the questions failed to load.
height
number
default 520
Height of the surface in px; the trail scrolls inside it.
disabled
boolean
default false
Read only: nothing can be answered or changed.
className
string
default —
Classes for the surface.