AI Summary
The banner that marks AI-authored copy: one sentence of plain language on an animated painting, in one of four palettes. No runtime dependencies.
Live preview
You made 42 purchases in August. Twelve were the same café at the same hour — a habit, not a decision. Groceries fell 31% in the same weeks.
The painting above is live — the colour masses drift and the noise warp breathes on separate cycles, so the field never visibly loops. Only the background moves: the highlighted phrases are static, because a highlight is a reading aid rather than an event.
Each field is one gradient plus a handful of colour masses pushed through a
fractal-noise displacement warp, with a grain pass over the top — so a palette
is a few hex values and three durations, and nothing is re-rendered or fetched
at runtime. The preview shows ember; the ink default is deliberately the
still one.
Installation
pnpm dlx shadcn@latest add @sc1m/ai-summaryThis also merges the token layer and installs
src/lib/cn.ts. For the manual route — prerequisites, components.json, and
app-level setup — see Installation.
Usage
import {
AiSummary,
AiSummaryLabel,
AiSummaryText,
AiSummaryMark,
} from "@/components/ui/ai-summary";Composition
Every part is a separate export. AiSummary owns the painting; the rest are
presentational and must appear in this order.
AiSummary├── AiSummaryLabel└── AiSummaryText └── AiSummaryMarkExample
<AiSummary palette="ember">
<AiSummaryLabel>What mattered</AiSummaryLabel>
<AiSummaryText>
You made 42 purchases in August.{" "}
<AiSummaryMark>Twelve were the same café at the same hour</AiSummaryMark>{" "}
— a habit, not a decision.
</AiSummaryText>
</AiSummary>Palettes
Four ship. ink is the default — the restrained one, brand accent on
near-black with three masses and almost no movement. ember is warm with a
high warp and a slow pan; tide is cool and deep, low warp with high octaves;
moss is green and sand on a lazy 52s pan.
Groceries fell 31% while eating out held flat.
Groceries fell 31% while eating out held flat.
Groceries fell 31% while eating out held flat.
Groceries fell 31% while eating out held flat.
API reference
AiSummaryMark takes no props of its own — the highlight is static, and
box-decoration-break: clone keeps the pill whole when a phrase wraps.
Accessibility
- The painting is
aria-hidden— it carries no information, and the summary reads the same with it removed. - The veil between painting and copy is what makes the text legible, so it is per-palette rather than constant: a hot field needs a flat 72%, a deep one can be graded and let the top of the card keep its colour.
- The highlight on
AiSummaryMarkis static. It is a reading aid, not an event: animating it in would pull the eye to the mark before the reader has reached it, and repeat on every remount. - SMIL ignores
prefers-reduced-motion— no CSS rule can stop<animate>— so the component leaves the painting's timelines out of the DOM entirely rather than trying to suppress them. The server render and first paint are always the still painting; motion is added on hydration only if the reader allows it.
Design tokens
The palettes are deliberately raw hex. They are paint, not semantics — there is no brand meaning to inherit, and pinning them to tokens would give every brand the same picture. Everything structural around them is tokens.
Source
src/components/ui/ai-summary/ai-summary.tsx — no next/* imports, so it drops into any
React 19 + Tailwind v4 app.