RecommendationCard
An agent's proposal awaiting an answer: a kicker, the recommendation, a confidence figure counted up beside a ring that fills to match, and two buttons that collapse into one quiet resolved line
Preview
<script lang="ts">
import { RecommendationCard } from "fancy-ui-svelte";
import type { RecommendationState } from "fancy-ui-svelte";
// Nothing is on a timer here: the demo only moves when you move it, so there
// is no rig to hold still for reduced motion.
let indexState = $state<RecommendationState>("open");
let cacheState = $state<RecommendationState>("open");
const settled = $derived(indexState !== "open" || cacheState !== "open");
function reset() {
indexState = "open";
cacheState = "open";
}
</script>
<div class="flex w-full max-w-xl flex-col gap-3 p-6">
<RecommendationCard
badge="Suggestion"
title="Add an index on orders.customer_id"
description="The three slowest queries this week all scanned the whole table."
confidence={0.87}
acceptLabel="Apply migration"
bind:state={indexState}
>
<code
class="bg-foreground/5 text-muted-foreground block rounded-md px-2.5 py-1.5 font-mono text-xs"
>
CREATE INDEX orders_customer_id_idx ON orders (customer_id);
</code>
</RecommendationCard>
<RecommendationCard
badge="Speculative"
title="Cache the pricing lookup for 60 seconds"
description="Prices changed twice last quarter, so staleness is cheap — but the traffic here is modest."
confidence={0.42}
acceptLabel="Apply patch"
dismissLabel="Not now"
bind:state={cacheState}
/>
<div class="mt-1 flex items-center justify-between gap-3">
<p class="text-muted-foreground text-xs">
The ring and the counter agree on one figure; the band shifts colour at 75% and 50%.
</p>
{#if settled}
<button
type="button"
class="text-muted-foreground hover:text-foreground hover:bg-foreground/5 flex-none rounded-md px-2.5 py-1 text-xs font-medium transition-colors"
onclick={reset}
>
Reset both
</button>
{/if}
</div>
</div>Installation
Usage
<script lang="ts">
import { RecommendationCard } from 'fancy-ui-svelte';
</script>
<RecommendationCard />Examples
Basic Usage
<script lang="ts">
import { RecommendationCard } from "$lib/fancy-ui/recommendation-card";
import type { RecommendationState } from "$lib/fancy-ui/recommendation-card";
// Nothing is on a timer here: the demo only moves when you move it, so there
// is no rig to hold still for reduced motion.
let indexState = $state<RecommendationState>("open");
let cacheState = $state<RecommendationState>("open");
const settled = $derived(indexState !== "open" || cacheState !== "open");
function reset() {
indexState = "open";
cacheState = "open";
}
</script>
<div class="flex w-full max-w-xl flex-col gap-3 p-6">
<RecommendationCard
badge="Suggestion"
title="Add an index on orders.customer_id"
description="The three slowest queries this week all scanned the whole table."
confidence={0.87}
acceptLabel="Apply migration"
bind:state={indexState}
>
<code
class="bg-foreground/5 text-muted-foreground block rounded-md px-2.5 py-1.5 font-mono text-xs"
>
CREATE INDEX orders_customer_id_idx ON orders (customer_id);
</code>
</RecommendationCard>
<RecommendationCard
badge="Speculative"
title="Cache the pricing lookup for 60 seconds"
description="Prices changed twice last quarter, so staleness is cheap — but the traffic here is modest."
confidence={0.42}
acceptLabel="Apply patch"
dismissLabel="Not now"
bind:state={cacheState}
/>
<div class="mt-1 flex items-center justify-between gap-3">
<p class="text-muted-foreground text-xs">
The ring and the counter agree on one figure; the band shifts colour at 75% and 50%.
</p>
{#if settled}
<button
type="button"
class="text-muted-foreground hover:text-foreground hover:bg-foreground/5 flex-none rounded-md px-2.5 py-1 text-xs font-medium transition-colors"
onclick={reset}
>
Reset both
</button>
{/if}
</div>
</div>Props
| Prop | Type | Default | Description |
|---|---|---|---|
title * | string | - | What the agent is proposing, e.g. "Add an index on orders.customer_id" |
description | string | - | Secondary muted line under the title — the reasoning, the expected effect |
confidence | number | - | How sure the agent is, from 0 to 1. Omitted, the whole confidence block disappears rather than reading as zero; out-of-range numbers are clamped and the band shifts colour at 0.75 and 0.5 |
acceptLabel | string | "Apply" | Label for the confirm button |
dismissLabel | string | "Dismiss" | Label for the decline button |
onAccept | () => void | - | Called when the recommendation is accepted, after `state` has been written |
onDismiss | () => void | - | Called when the recommendation is dismissed, after `state` has been written |
state | "open" | "accepted" | "dismissed" | "open" | Where the recommendation stands (bindable). Resolving swaps the buttons for a quiet line inside a polite live region; a resolved card refuses to resolve again |
badge | string | - | Small kicker above the title, e.g. "Suggestion" |
Slots
| Slot | Description |
|---|---|
children | The detail region between the header and the footer — a snippet of the change being proposed |
Links
Related components
AgentPlan
Glanceable checklist of an agent's plan: a done/total count and completion bar over one row per step, with a status glyph, an optional detail line, and substeps indented under a rail
AiDataTable
Compact comparison table for a model's structured answer: real table semantics, tabular numeric columns, check-or-dash booleans, and one tintable column — rendered in the order it arrived, with no sorting
ApprovalCard
A human-in-the-loop gate before a side effect: the agent states what it is about to do, and the footer swaps its approve and deny buttons for a one-line verdict once someone decides