KQ-1765 · BEFORE/YOU/SEND
Before You Send
A contact form that searches the help articles as the subject is typed and slides the matches up between the subject and the rest of the form: the panel's measured height opens on springs.glide, each answer rises 8px in cascade order and its relevance bar grows on glide a beat later, and a changing query re-ranks them in place on glide. Choosing one opens it in the panel, and This solved it folds the form down to a thank-you on glide — a ring landing on springs.recoil, a check drawn on springs.flick — instead of a ticket. The subject is a combobox: Down and Up walk the answers, Enter opens one, Escape hides them, and a failed search never blocks Send.
Contact Fieldline support
We reply within a day. Answers show up as you type.
type a subject · fieldline support
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/before-you-send
Props
- resultStyle
- "cards" | "rows" | "wash"
- default "cards"
- How the answers are drawn: a row of cards, hairline rows with a meter and percentage, or rows whose relevance is a wash behind the title.
- density
- "compact" | "cozy" | "roomy"
- default "cozy"
- Control heights, type and gaps.
- accent
- "cobalt" | "jade" | "amber" | "mono"
- default "cobalt"
- Relevance bars, the active answer, Send and the thank-you ring.
- articles
- HelpArticle[]
- default defaultHelpArticles
- The help articles searched: title, summary, steps, topic, keywords, read time and href.
- topics
- ContactTopic[]
- default defaultContactTopics
- The topics offered as a radiogroup above the subject.
- value / defaultValue
- ContactDraft / Partial<ContactDraft>
- default — / { topic: "billing", email: "jun@coldbrook.studio" }
- The draft (topic, subject, details, email), controlled or initial.
- onValueChange
- (draft: ContactDraft) => void
- default —
- Fires from every edit with the whole draft.
- onSearch
- (query, topic) => ArticleMatch[] | Promise<ArticleMatch[]> | void
- default —
- A settled search. Return matches to replace the built-in scoring; a rejection shows a quiet note and never blocks Send.
- onSolved
- (article: HelpArticle, query: string) => void
- default —
- This solved it was chosen: the form closes with a thank-you and nothing is sent.
- onSend
- (draft: ContactDraft) => { reference? } | Promise<{ reference? }> | void
- default —
- The form was sent after validation. A promise holds Send; resolve a reference to show it, reject to keep everything and offer Try again.
- onOpenArticle
- (article: HelpArticle) => void
- default —
- Full article was pressed in a preview.
- company
- string
- default "Fieldline"
- Whose support this is.
- responseTime
- string
- default "within a day"
- How soon a message is answered, in words.
- maxResults
- number
- default 3
- The most answers shown.
- minRelevance
- number
- default 0.25
- Answers below this relevance (0 to 1) are not shown.
- searchDelay
- number
- default 160
- Quiet time after typing before a search runs, ms.
- height
- number
- default 540
- The tallest the surface grows, px; past it the form scrolls inside.
- disabled
- boolean
- default false
- Nothing can be typed, chosen or sent.
- className
- string
- default —
- Classes for the outer surface.
| Prop | Type | Default | Description |
|---|---|---|---|
| resultStyle | "cards" | "rows" | "wash" | "cards" | How the answers are drawn: a row of cards, hairline rows with a meter and percentage, or rows whose relevance is a wash behind the title. |
| density | "compact" | "cozy" | "roomy" | "cozy" | Control heights, type and gaps. |
| accent | "cobalt" | "jade" | "amber" | "mono" | "cobalt" | Relevance bars, the active answer, Send and the thank-you ring. |
| articles | HelpArticle[] | defaultHelpArticles | The help articles searched: title, summary, steps, topic, keywords, read time and href. |
| topics | ContactTopic[] | defaultContactTopics | The topics offered as a radiogroup above the subject. |
| value / defaultValue | ContactDraft / Partial<ContactDraft> | — / { topic: "billing", email: "jun@coldbrook.studio" } | The draft (topic, subject, details, email), controlled or initial. |
| onValueChange | (draft: ContactDraft) => void | — | Fires from every edit with the whole draft. |
| onSearch | (query, topic) => ArticleMatch[] | Promise<ArticleMatch[]> | void | — | A settled search. Return matches to replace the built-in scoring; a rejection shows a quiet note and never blocks Send. |
| onSolved | (article: HelpArticle, query: string) => void | — | This solved it was chosen: the form closes with a thank-you and nothing is sent. |
| onSend | (draft: ContactDraft) => { reference? } | Promise<{ reference? }> | void | — | The form was sent after validation. A promise holds Send; resolve a reference to show it, reject to keep everything and offer Try again. |
| onOpenArticle | (article: HelpArticle) => void | — | Full article was pressed in a preview. |
| company | string | "Fieldline" | Whose support this is. |
| responseTime | string | "within a day" | How soon a message is answered, in words. |
| maxResults | number | 3 | The most answers shown. |
| minRelevance | number | 0.25 | Answers below this relevance (0 to 1) are not shown. |
| searchDelay | number | 160 | Quiet time after typing before a search runs, ms. |
| height | number | 540 | The tallest the surface grows, px; past it the form scrolls inside. |
| disabled | boolean | false | Nothing can be typed, chosen or sent. |
| className | string | — | Classes for the outer surface. |