Completion Celebration
A celebratory status card for the moment a multi-step flow completes — a lesson, a recipe, a quiz, an onboarding sequence. Renders a tone-coloured icon disc with a checkmark that strokes itself in, a serif headline, an optional message, and an optional call-to-action. A small confetti burst fires from the center on mount, then settles. The "peak end" of a completion experience.
Lesson complete
Nice work — you finished all 4 steps.
Customize
Tone
success
Options
Installation
npx shadcn@latest add https://craftbits.dev/r/completion-celebration.jsonUsage
import { CompletionCelebration } from "@craft-bits/core";
<CompletionCelebration
title="Lesson complete"
message="Nice work — you finished all 4 steps."
cta={{ label: "Next lesson", onClick: goNext }}
/>Understanding the component
- Tone-driven swatch.
toneselects asuccess/accent/warningpalette that paints the icon disc, the ring around it, the checkmark stroke, and the confetti dots. All four reuse the--cb-*semantic tokens, so a re-theme repaints the whole celebration. - Drawn-in checkmark. The default icon is a single SVG path animated via a
pathLengthtween withSPRINGS.smooth. With reduced motion on, the stroke is fully drawn on the first frame — no animation, no flash. - Card entrance. The root is a
motion.divthat scales from0.9to1and fades in withSPRINGS.smooth. Reduced-motion users get a plain opacity fade withDURATIONS.fast. - Deterministic confetti. Eighteen particles are positioned by hashing
useId(), so the layout is identical on server and client — noMath.random()in render. Each particle flies from the center at a random angle out to 56-112px and fades over 0.22-0.30s. Reduced motion suppresses the burst entirely. - Polite ARIA. The root carries
role="status"andaria-live="polite", with anaria-labelof"Completion: <title>"so screen readers announce the finish without interrupting the user. - Controlled visibility.
visible={false}unmounts the card. Re-mount the parent with a freshkeyto re-trigger the entrance animation — useful in docs demos and in flows that need the celebration to "play again." The customizer above uses the same trick with its Replay button.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
visible | boolean | true | When false, nothing renders. Key-remount the parent to re-trigger the entrance. |
title | ReactNode | "Complete!" | Headline. Strings render in the serif display style. |
message | ReactNode | — | Optional supporting line beneath the title. |
cta | { label; onClick?; href? } | — | Optional call-to-action. Renders as a button (or anchor when href is set). |
tone | "success" | "accent" | "warning" | "success" | Palette for the disc, ring, checkmark, and confetti. |
confetti | boolean | true | Whether a confetti burst fires on mount. Auto-disabled under reduced motion. |
icon | ReactNode | default checkmark | Override the centred icon. Should fit a 32×32 box. |
className | string | — | Merged onto the root via cn(). |
Accessibility
- Root uses
role="status"andaria-live="polite"so the completion is announced without interrupting other speech. - The
aria-labeldefaults to"Completion: <title>"when the title is a string. - Confetti is decorative — its container is
aria-hidden. - Reduced motion: the card uses a short fade, the checkmark renders fully drawn from the first frame, and the confetti burst is suppressed.
- Tone colours all clear WCAG AA contrast against
--cb-bg-elevated.
Credits
- Extracted from:
terminal-dreams(src/components/cookbook/CompletionCelebration.tsx). The library version is a re-architecture: the source was a fixed-position fullscreen overlay with a 3D card flip and recipe-specific copy. The craft-bits version is a non-modal status card with generalised props (title,message,cta), tone-aware theming via--cb-*tokens, deterministic confetti fromuseId()instead ofMath.random(), motion viaSPRINGS.smooth/EASINGS.out, and a stroke-drawn checkmark in place of the recipe emoji.