Will the Riverside office keep late opening on Fridays after the move?
KQ-1738 · QUESTION/QUEUE
Question Queue
The Q&A of a live town hall: open questions sorted by votes and an Answered section, in one feed. A vote fills its pill and rolls the count digit by digit on springs.snap; a beat later the question lifts and climbs past the ones it overtakes as a layout move on springs.glide while their ranks roll down, settles on springs.flick and pops a ▲ chip on springs.recoil. Answering opens an editor under the question on glide, publishing moves the card into Answered as a shared-layout move with a recoil stamp and the answer opening beneath; Up and Down walk the questions, U upvotes and A answers.
Coldbrook town hall
Thursday all-hands · Q&A · 230 votes
Up and Down move between questions. U upvotes. A answers.
9 open · 2 answered · 230 votes
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/question-queue
Props
- cardStyle
- "soft" | "outline" | "flat"
- default "soft"
- Card treatment: tinted cards, bordered cards, or ruled rows with no cards.
- density
- "compact" | "cozy" | "roomy"
- default "cozy"
- Type size, padding and the size of the vote pill.
- accent
- string
- default "var(--accent-bright)"
- Any CSS colour: your votes, the top question's rule, the climb's shadow and its ▲ chip.
- questions / defaultQuestions
- QueueQuestion[]
- default — / defaultQueueQuestions
- The questions, open and answered, controlled or initial: { id, text, author: { name, team? } | null (anonymous), votes, voted?, at, answer?: { text, by, team?, at } }.
- onQuestionsChange
- (questions: QueueQuestion[]) => void
- default —
- Fires from the vote, ask or answer that changed the list, with the whole new list.
- onAsk
- (text: string, options: { anonymous: boolean }) => void
- default —
- A question was asked from the composer; it is already in the list.
- onUpvote
- (question: QueueQuestion, voted: boolean) => void
- default —
- The viewer upvoted (voted true) or withdrew a vote.
- onAnswer
- (question: QueueQuestion, answer: string) => void
- default —
- The host answered a question; empty text means it was answered live.
- now
- number
- default defaultQueueNow
- The moment relative times count from, ms. Never read from the clock.
- title
- string
- default "Coldbrook town hall"
- The session's name in the header.
- subtitle
- string
- default "Thursday all-hands · Q&A"
- One line under the title.
- host
- { name: string; team?: string }
- default defaultQueueHost
- Who answers are signed by.
- asker
- { name: string; team?: string }
- default defaultQueueAsker
- Who new questions are asked as, unless asked anonymously.
- canAnswer
- boolean
- default true
- Host view: an Answer action on each open question (and the A key).
- allowAnonymous
- boolean
- default true
- Offer the Anonymous switch in the composer.
- maxLength
- number
- default 240
- Longest question, in characters; a counter appears near the limit.
- status
- "ready" | "loading" | "error"
- default "ready"
- Whether the questions have arrived: loading shows skeleton cards, error a Try again.
- onRetry
- () => void
- default —
- Try again was pressed after the questions failed to load.
- height
- number
- default 540
- Height of the surface in px; its body scrolls inside.
- label
- string
- default the title
- The feed's accessible name.
- disabled
- boolean
- default false
- Read only: nothing can be asked, voted or answered.
- className
- string
- default —
- Classes for the outer surface.
| Prop | Type | Default | Description |
|---|---|---|---|
| cardStyle | "soft" | "outline" | "flat" | "soft" | Card treatment: tinted cards, bordered cards, or ruled rows with no cards. |
| density | "compact" | "cozy" | "roomy" | "cozy" | Type size, padding and the size of the vote pill. |
| accent | string | "var(--accent-bright)" | Any CSS colour: your votes, the top question's rule, the climb's shadow and its ▲ chip. |
| questions / defaultQuestions | QueueQuestion[] | — / defaultQueueQuestions | The questions, open and answered, controlled or initial: { id, text, author: { name, team? } | null (anonymous), votes, voted?, at, answer?: { text, by, team?, at } }. |
| onQuestionsChange | (questions: QueueQuestion[]) => void | — | Fires from the vote, ask or answer that changed the list, with the whole new list. |
| onAsk | (text: string, options: { anonymous: boolean }) => void | — | A question was asked from the composer; it is already in the list. |
| onUpvote | (question: QueueQuestion, voted: boolean) => void | — | The viewer upvoted (voted true) or withdrew a vote. |
| onAnswer | (question: QueueQuestion, answer: string) => void | — | The host answered a question; empty text means it was answered live. |
| now | number | defaultQueueNow | The moment relative times count from, ms. Never read from the clock. |
| title | string | "Coldbrook town hall" | The session's name in the header. |
| subtitle | string | "Thursday all-hands · Q&A" | One line under the title. |
| host | { name: string; team?: string } | defaultQueueHost | Who answers are signed by. |
| asker | { name: string; team?: string } | defaultQueueAsker | Who new questions are asked as, unless asked anonymously. |
| canAnswer | boolean | true | Host view: an Answer action on each open question (and the A key). |
| allowAnonymous | boolean | true | Offer the Anonymous switch in the composer. |
| maxLength | number | 240 | Longest question, in characters; a counter appears near the limit. |
| status | "ready" | "loading" | "error" | "ready" | Whether the questions have arrived: loading shows skeleton cards, error a Try again. |
| onRetry | () => void | — | Try again was pressed after the questions failed to load. |
| height | number | 540 | Height of the surface in px; its body scrolls inside. |
| label | string | the title | The feed's accessible name. |
| disabled | boolean | false | Read only: nothing can be asked, voted or answered. |
| className | string | — | Classes for the outer surface. |