# 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.

- Package: `fancy-ui-svelte` (Svelte 5 + Tailwind CSS v4)
- Category: AI Agents
- Tags: ai, agents, recommendation, confidence
- Dependencies: number-ticker
- Docs: https://fancy-ui.rama.app/docs/components/recommendation-card
- Source: https://github.com/RamaHerbin/fancy-ui/tree/main/src/lib/fancy-ui/recommendation-card

## Import

```svelte
<script lang="ts">
  import { RecommendationCard } from 'fancy-ui-svelte';
</script>
```

Setup (once per project): `npm install fancy-ui-svelte`, then add `@import "fancy-ui-svelte/tailwind.css";` after `@import "tailwindcss";` in your main CSS file. Full guide: https://fancy-ui.rama.app/llms.txt

React: `import { RecommendationCard } from 'fancy-ui-react';` (same prop names; `class` becomes `className`, snippets become `ReactNode`).

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `title` (required) | `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" |
| `sound` | `boolean` | `false` | Plays select on accept and close on dismiss, once the user has enabled sound |

## Snippets

| Snippet | Description |
| --- | --- |
| `children` | The detail region between the header and the footer — a snippet of the change being proposed |

## Examples

### Basic Usage

```svelte
<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
		sound
		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
		sound
		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>
```
