Suggestion Card
An agent offers one person a next step, in a card only they see, with buttons that name the action.
- Status
- Since
0.44.0- Accessibility pattern
- named region; ticks are labelled checkboxes
Last updated
Assistant · 3 decisions from this chat
Expires tomorrow
Install
Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Suggestion Card before installation. Retain the digest printed by this command.
pnpm exec vegastack-design verify suggestion-cardInstall with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.
pnpm dlx shadcn@4.21.0 add @vegastack/suggestion-cardRun the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.
The same command installs the registry items it composes: @vegastack/checkbox, @vegastack/truncated-text.
Usage
import { SuggestionCard } from "@/components/ui/suggestion-card";
<SuggestionCard
privateLabel="Only you can see this"
avatar={<PersonAvatar person={agent} size="sm" />}
title="Assistant · 3 decisions from this chat"
items={items}
onItemCheckedChange={toggle}
actions={
<>
<Button size="sm" variant="secondary" onClick={accept}>
Create 2 tasks
</Button>
<Button size="sm" variant="ghost" onClick={dismiss}>
Dismiss
</Button>
</>
}
footnote="Expires tomorrow"
/>;The card has no action or wording of its own: the host passes the buttons and every line. For an
agent that must wait for a decision before it acts, use
ApprovalCard. A suggestion is an offer, and ignoring it is fine.
Assistant · 3 decisions from this chat
Expires tomorrow
Examples
Accepting
state="busy" holds the items still while the host works. The spinner goes on the host's main
button, and the host turns off its other buttons, so Dismiss can't race the accept.
Assistant · 2 decisions from this chat
Outcomes
accepted replaces the card with outcome, a plain line, with actions after it for Undo. Use it
too for something an agent did by itself. expired folds to the title, outcome and actions.
failed keeps the whole card, so Retry has its items. A dismissed card is simply not rendered.
Asha created 2 tasks from Assistant's suggestion
Assistant created 3 tasks from this chat
Assistant · 2 decisions from this chat
This suggestion expired.
Assistant · 2 decisions from this chat
Couldn't create the tasks.
In a list
variant="row" is the compact form for a list: the title and buttons side by side, stacked in a
container under 32rem. privateLabel, items and the footnote don't show.
Assistant · Create 2 tasks from "4000 K downlight decision"
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
title* | React.ReactNode | — | What is offered, and the card's name: "Assistant · 3 decisions from this chat". |
actions | React.ReactNode | — | The buttons, which name the action: a secondary "Create 2 tasks", then ghost "Review" and "Dismiss". Never primary, and never "Yes" or "No thanks". |
avatar | React.ReactNode | — | The agent's avatar, before the title. |
footnote | React.ReactNode | — | The quiet line at the foot: "Expires tomorrow". |
items | readonly SuggestionCardItem[] | [] | What it would act on. |
menu | React.ReactNode | — | The ⋯ menu, at the card's top end. |
onItemCheckedChange | ((id: string, checked: boolean) => void) | — | An item was ticked or unticked. |
outcome | React.ReactNode | — | What became of it: "Asha created 2 tasks from Assistant's suggestion". |
privateLabel | React.ReactNode | — | Who it is for, above the title: "Only you can see this". |
state | SuggestionCardState | "open" | busy — accepting: the items hold still. The spinner goes on the host's main button, and the
host holds or drops the others, so Dismiss can't race the accept. accepted — the card gives
way to outcome, a plain line, with actions after it ("Undo"); it also fits something an
agent did by itself. expired — the title, outcome and actions ("Ask again"). failed —
the whole card with outcome over actions ("Retry"). After accepted, ref is what's left
to take focus: the button that had it is gone. |
variant | "card" | "row" | "card" | card — in a conversation, full width. row — compact, for a list: the title and buttons side
by side, stacked in a container under 32rem; privateLabel, items and the footnote don't show. |
Data attributes and CSS variables on SuggestionCard
| Attribute | Values |
|---|---|
data-slot | "suggestion-card" | "suggestion-card-actions" | "suggestion-card-avatar" | "suggestion-card-footnote" | "suggestion-card-header" | "suggestion-card-heading" | "suggestion-card-item" | "suggestion-card-items" | "suggestion-card-menu" | "suggestion-card-outcome" | "suggestion-card-private" | "suggestion-card-title" |
data-state | mirrors a prop or state value |
data-variant | mirrors a prop or state value |
| Prop | Type | Default | Description |
|---|---|---|---|
id* | string | — | A stable key, and what onItemCheckedChange reports. |
label* | React.ReactNode | — | What it is: "Order 3000 K samples". Cut with an ellipsis on one line. |
checked | boolean | — | Ticked. Leave it out for an item that can't be unticked: no checkbox. |
meta | React.ReactNode | — | A muted note kept whole at the row's end: "Ravi, Wed". |
Accessibility
- An open card is a region named by its title. A row is a group, and an accepted line is neither, so a long list or history doesn't flood the landmarks.
- Each item's label names its checkbox. A long label is cut on screen, not for a screen reader, and shows in full on hover.
- A card that appears announces nothing. Announce a failure or an acceptance through the
conversation's own
useAnnouncerregion, and afteracceptedmove focus to the card withref, since the button that had it is gone.
| Key | Action |
|---|---|
| Tab | Move through ticks and buttons |
| Space | Tick or untick an item |
| Contract | States tested |
|---|---|
| Behaviour | open, busy, accepted, expired, failed, ticks, no-ticks |
| Accessibility | named-region, group-row, no-landmark-when-accepted, labelled-checkboxes, aria-busy, browser-accessibility-test |
| Visual | card, busy, outcomes, row |
Do / Don't
Approval Card
An agent asks its owner to approve a risky action — who asks, what, on which records, why, until when — with one Approve / Decline for the whole request.
Agent Edit Banner
The strip under a document's header after an agent changed it — who, how many changes, when — with View changes, Approve, Undo and a dismiss.