KQ-1668 · MEND/LIST
Mend List
A long application form that gathers every problem a submit finds into a mend list dropped in above the form and pinned to the top of its scrolling body: the list opens on springs.glide while its items fall in from 8px above on springs.snap in a cascade, each naming the field and the fix. Choosing an item scrolls the body to the field on glide, re-aimed every frame so the field lands just under the pinned list, then the field pulses once with an accent ring and takes focus; from then on checking is live, and the moment a field passes its item strikes itself through on springs.flick while the count rolls down on snap. The list is a labelled nav whose heading takes focus when it drops, Next walks the unmended fields from the keyboard, and a refused submit keeps every answer.
Studio residency application
Fernworks · Spring 2027 intake
5 to mend · next: email
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/mend-list
Props
- sections
- MendSection[]
- default defaultMendSections
- The form as sections of fields: { id, title, description?, fields }, each field { id, label, kind, required?, minLength?, pattern?, patternFix?, requiredFix?, options?, placeholder?, hint?, autoComplete?, wide?, validate? }.
- values / defaultValues
- Record<string, string | boolean>
- default — / defaultMendValues
- The answers keyed by field id, controlled or initial.
- onValuesChange
- (values) => void
- default —
- Fires from the edit that changed the answers, with all of them.
- onFieldChange
- (id, value, values) => void
- default —
- Fires from each edit with the field, its new value and every answer.
- onSubmit
- (values) => void | { fieldErrors? } | Promise<void | { fieldErrors? }>
- default —
- A submit that passed every check. A promise shows progress on the button; a rejection keeps every answer and says so; resolving with fieldErrors puts the server's words into the mend list.
- onInvalid
- (ids: string[]) => void
- default —
- A submit found problems; the ids still to mend, in form order.
- onSaveDraft
- (values) => void | Promise<void>
- default —
- Save draft was pressed; a promise shows it saving, then a check.
- onMend
- (id: string) => void
- default —
- An item in the mend list was chosen.
- listStyle
- "panel" | "chips" | "numbered"
- default "panel"
- How the mend list is drawn: a bordered panel of two-line items, a run of chips, or a numbered checklist with a progress bar.
- density
- "compact" | "cozy" | "roomy"
- default "cozy"
- Field height (32, 36, 40px) and the room between fields and sections.
- accent
- string
- default "var(--accent-bright)"
- The list's checks, the pulse ring, the strike line and the button's progress, any CSS colour.
- checkOnMount
- boolean
- default false
- Run the submit check on mount so the mend list shows from the first frame.
- title
- string
- default "Studio residency application"
- The form's heading and accessible name.
- subtitle
- string
- default "Fernworks · Spring 2027 intake"
- A quiet line under the heading.
- submitLabel
- string
- default "Submit application"
- The submit button's label.
- height
- number
- default 520
- Panel height in px; the form scrolls inside it and the mend list pins to its top.
- status
- "ready" | "loading"
- default "ready"
- Loading draws placeholder fields and holds the buttons.
- disabled
- boolean
- default false
- Nothing can be edited, saved or sent.
- className
- string
- default —
- Classes for the outer container.
| Prop | Type | Default | Description |
|---|---|---|---|
| sections | MendSection[] | defaultMendSections | The form as sections of fields: { id, title, description?, fields }, each field { id, label, kind, required?, minLength?, pattern?, patternFix?, requiredFix?, options?, placeholder?, hint?, autoComplete?, wide?, validate? }. |
| values / defaultValues | Record<string, string | boolean> | — / defaultMendValues | The answers keyed by field id, controlled or initial. |
| onValuesChange | (values) => void | — | Fires from the edit that changed the answers, with all of them. |
| onFieldChange | (id, value, values) => void | — | Fires from each edit with the field, its new value and every answer. |
| onSubmit | (values) => void | { fieldErrors? } | Promise<void | { fieldErrors? }> | — | A submit that passed every check. A promise shows progress on the button; a rejection keeps every answer and says so; resolving with fieldErrors puts the server's words into the mend list. |
| onInvalid | (ids: string[]) => void | — | A submit found problems; the ids still to mend, in form order. |
| onSaveDraft | (values) => void | Promise<void> | — | Save draft was pressed; a promise shows it saving, then a check. |
| onMend | (id: string) => void | — | An item in the mend list was chosen. |
| listStyle | "panel" | "chips" | "numbered" | "panel" | How the mend list is drawn: a bordered panel of two-line items, a run of chips, or a numbered checklist with a progress bar. |
| density | "compact" | "cozy" | "roomy" | "cozy" | Field height (32, 36, 40px) and the room between fields and sections. |
| accent | string | "var(--accent-bright)" | The list's checks, the pulse ring, the strike line and the button's progress, any CSS colour. |
| checkOnMount | boolean | false | Run the submit check on mount so the mend list shows from the first frame. |
| title | string | "Studio residency application" | The form's heading and accessible name. |
| subtitle | string | "Fernworks · Spring 2027 intake" | A quiet line under the heading. |
| submitLabel | string | "Submit application" | The submit button's label. |
| height | number | 520 | Panel height in px; the form scrolls inside it and the mend list pins to its top. |
| status | "ready" | "loading" | "ready" | Loading draws placeholder fields and holds the buttons. |
| disabled | boolean | false | Nothing can be edited, saved or sent. |
| className | string | — | Classes for the outer container. |