# Approval Card

A human-in-the-loop decision surface for approvals, single-choice questions, custom responses, and multi-step review flows.

> For the complete documentation index, see [llms.txt](/llms.txt). Markdown variants are available by appending `.md` to any URL or sending an `Accept: text/markdown` header. An agent skill is available at [/.well-known/agent-skills/site-skill.md](/.well-known/agent-skills/site-skill.md).





<ComponentPreview name="approval-card">
  <ApprovalCardDemo />
</ComponentPreview>

## Installation [#installation]

```bash
npx shadcn@latest add https://motif-ui.vercel.app/r/approval-card.json
```

Pulls in the platform [`button`](https://ui.shadcn.com/docs/components/button),
[`input`](https://ui.shadcn.com/docs/components/input) and
[`radio-group`](https://ui.shadcn.com/docs/components/radio-group) — the same bases Prompt
Input and Skill Detail build on, not copies of their own — plus `tooltip` for the
step-bar previews.

The card reads a step walked past in `warning`, a token the base theme does not have. The
registry item carries it, so `shadcn add` adds it to your CSS — `:root`, `.dark` and
`@theme`, the same three places `styles/globals.css` keeps it here.

## Usage [#usage]

```tsx
import { ApprovalCard } from "@/components/agents/approval-card";

export function Review() {
  return (
    <ApprovalCard
      questions={[
        {
          id: "scope",
          title: "How focused should the first release be?",
          options: [
            { value: "focused", label: "A focused starter set" },
            { value: "broad", label: "A broader collection" },
          ],
        },
      ]}
      onAnswersChange={(answers) => console.log(answers)}
      onSubmit={(answers) => console.log("submitted", answers)}
    />
  );
}
```

## Status [#status]

The card has two modes. Without `questions` it is a plain approval surface — `Approve`,
`Request changes` and `Reject` are only rendered when their handlers are passed. With
`questions` it walks through the list one step at a time. There is one answer shape:
a single choice plus free text. Picking an option clears the text field, typing in the
field clears the pick, and the answer is stored in an `answers` map keyed by question id.
Step position lives in the footer as one bar per question, which is also the only step
control. Width carries position: the question you are on is twice the resting width.
Colour carries state: the current bar is at full strength, an answered one sits at 55%,
one not reached yet at 20%, and one walked past without an answer turns the `warning`
token's amber. Clicking a bar jumps to that
question (hovering lengthens it, nudges the rest of the row along, and previews the
question in a tooltip), and every bar names itself to assistive tech
("Question 2 of 3: Who signs off… (answered)"). Navigation is free — `Skip` and `Continue`
both move on without an answer — and only `Submit` on the last question enforces that
every question is answered.

The root is a `bg-card` container with a border, so it reads as raised on the page or
inside a `bg-muted` panel. The free-text field is the platform input, so its border and
focus ring are the same as everywhere else in the app; the card only pins the field's
`h-10 rounded-xl` geometry.

| Status                           | Meaning                                         |
| -------------------------------- | ----------------------------------------------- |
| `pending`                        | Waiting for the user, controls enabled          |
| `submitting`                     | Controls locked while the request is in flight  |
| `answered` / `approved`          | Done, no controls: `result` or the status text  |
| `rejected` / `changes-requested` | Closed without submitting: `result` or the text |

## Component source [#component-source]

<ComponentSource name="approval-card" src="registry/new-york/agents/approval-card/index.tsx" title="approval-card.tsx" />
