Components / AI interface
ApprovalCard
Human-in-the-loop questions the agent asks before acting.
Install
npx shadcn@latest add https://irisui.dev/r/approval-card.jsonimport ApprovalCard from "@/components/iris/approval-card/approval-card";
Preview
ApprovalCard
A human-in-the-loop card that asks one question at a time before an agent acts, with single-choice, multi-select and free-text answers.
When to use
- Collect the decisions an agent needs before it does something with side effects, such as which story to draft or who signs it off.
- Walk through a short set of questions (2–5) in sequence. Single-choice answers advance on their own and multi-select answers wait for Continue.
- Not for confirming one destructive action — use
Dialog, orHoldToConfirmfor an inline guard. Not for proposing a single best option with a confidence level — useRecommendationCard.
Usage
import ApprovalCard from "@/components/iris/approval-card/approval-card";
h(ApprovalCard, {
questions: [
{
q: "Which story should I draft first?",
type: "radio",
options: ["Greek bond yields", "ECB rate signals", "Ferry strike"],
},
{ q: "Which formats should I compose?", type: "check", options: ["Quick brief", "Full story", "Digest block"] },
],
onSubmitted: (answers) => composeDrafts(answers),
});
Props
| Prop | Type | Default | Description |
|---|---|---|---|
questions |
{ q: string; type: "radio" | "check"; options: string[] }[] |
3 newsroom questions (story, formats, sign-off) | Questions shown in order. radio is single choice and advances 480ms after a pick. check is multi-select. |
labels |
Partial<{ skip, continue, send, customPlaceholder, sentMessage }> |
Skip, Continue, Send, Something else…, Answers sent |
Copy for the footer buttons, the free-text row and the sent confirmation. |
onSubmitted |
(answers: Record<number, number[]>) => void |
— | Called on Send with selected option indexes keyed by question index. |
onAnswerChange |
(questionIndex: number, answer: number[]) => void |
— | Called whenever a selection changes. |
resettable |
boolean |
true |
Shows "Start over" after the answers are sent. |
variant |
string |
— | Accepted for gallery parity. It has no effect. |
Notes
- Every question has a free-text "Something else…" row. Typing in it clears a radio selection, and Enter submits once there is an answer.
onSubmittedreceives option indexes only. Free-text answers are kept in internal state and are not passed to the callback.- The dismiss (×) button collapses the card to an "Open approval" button. Skip on the last question does the same.
- The question region is
aria-live="polite". Options are buttons witharia-pressed, and inactive questions arearia-hiddenand removed from the tab order. - The stack slides and the step counter rolls like an odometer. Under
prefers-reduced-motionthe slide is skipped. - Reads
--surface,--ink,--ink-2,--ink-3,--line-strong,--hover,--green/--green-tintand theButton(ghost Skip, accent Continue/Send).