Completion Screen Template
A reusable wrap-up screen for any lesson, quiz, or multi-step flow. Animated title plus subtitle, a responsive grid of stat tiles, an optional bespoke slot, and a single Continue CTA — every section can be toggled off via prop, so the same primitive serves a celebration screen, a quiet "saving..." screen, and a full lesson summary.
Completion screen
Problem complete
Three phases stitched into one final answer.
- Score92%23 of 25 points
- Time4m 12sMedian: 5m 30s
- Streak5Days in a row
Customize
Content
Stats
92
4
Installation
npx shadcn@latest add https://craftbits.dev/r/completion-screen-template.jsonUsage
import { CompletionScreenTemplate } from "@craft-bits/edu";
<CompletionScreenTemplate
title="Problem complete"
subtitle="Three phases stitched into one final answer."
stats={[
{ id: "score", label: "Score", value: "92%", description: "23 of 25 points" },
{ id: "time", label: "Time", value: "4m 12s" },
]}
cta={{ label: "Next problem" }}
onContinue={goNext}
/>Drop the stat grid by omitting the stats prop. Hide the CTA by omitting cta. Push bespoke content (a code bridge, a metaphor gallery, a celebration burst) into the children slot — it renders between the stats and the CTA.
Understanding the component
- Header. Title renders as an h2 with the comfortable sans-serif at the top of the scale; the optional subtitle sits below in cb-fg-muted. Both fade-and-rise in on mount with SPRINGS.smooth.
- Stat grid. Each stat is a tile inside a semantic ul / li list. Tiles use the standard rounded-cb-md corner with a border-cb-border-muted outline and a bg-cb-bg-elevated surface. Tiles enter with a tight 40ms stagger driven by STAGGER from @craft-bits/core/motion.
- Bespoke slot. Anything passed as children is rendered between the stats and the CTA. Use it for project-specific content. The slot is a raw ReactNode — its motion and chrome are the consumer's responsibility.
- CTA. A single Continue button. When onContinue is undefined the button is disabled so the parent can render the screen before the destination is wired. Tap scale held at 0.97; hover lifts by 1px.
- Reduced motion. useReducedMotion() short-circuits every entry animation to its final pose, and the tap / hover transforms drop to identity.
Variants
No stats, just a celebration
<CompletionScreenTemplate
title="You're done!"
subtitle="Nothing left to grade."
cta={{ label: "Back to home" }}
onContinue={goHome}
/>With bespoke content between stats and CTA
<CompletionScreenTemplate
title="Lesson complete"
stats={summaryStats}
cta={{ label: "Continue" }}
onContinue={advance}
>
<CodeTrace code={completedCode} lang="typescript" />
</CompletionScreenTemplate>Disabled CTA (parent not yet wired)
<CompletionScreenTemplate
title="Saving your progress..."
cta={{ label: "Next problem", ariaLabel: "Loading next problem" }}
/>Props
CompletionScreenTemplate
| Prop | Type | Default | Description |
|---|---|---|---|
title | ReactNode | — | Headline at the top of the screen. |
subtitle | ReactNode | — | Optional sub-headline in muted color. |
stats | CompletionScreenStat[] | — | Tiles displayed in a responsive 1 / 2 / 3-column grid. |
cta | CompletionScreenCTA | — | Continue CTA. Omit to hide the button row. |
onContinue | () => void | — | Click handler. When omitted the CTA is disabled. |
regionAriaLabel | string | 'Completion screen' | aria-label for the outer region landmark. |
children | ReactNode | — | Optional bespoke slot between the stats and the CTA. |
className | string | — | Merged onto the root div via cn(). |
...rest | HTMLAttributes<HTMLDivElement> | — | Any other div attribute. |
CompletionScreenStat
| Field | Type | Description |
|---|---|---|
id | string | Stable identifier for React list rendering. |
label | ReactNode | Short label above the value. |
value | ReactNode | Primary value (formatted string or number). |
description | ReactNode | Optional sub-line beneath the value. |
CompletionScreenCTA
| Field | Type | Description |
|---|---|---|
label | ReactNode | Button label. |
ariaLabel | string | Optional aria-label override. |
Accessibility
- Outer div carries
role="region"and anaria-label(defaultCompletion screen) so screen readers expose the wrap-up as a self-contained landmark. - An sr-only
role="status"live region announces the completion immediately on mount. - Stats are rendered as a semantic ul / li list with an
aria-label="Completion stats"so the grid is announced as a coherent group. - The Continue button accepts an explicit ariaLabel when the visible label is a ReactNode rather than a string — falling back to the label only when it's a plain string.
- Tap and hover transforms respect
prefers-reduced-motion. The disabled button drops to 40% opacity andcursor-not-allowed. - Color contrast: subtitle uses
--cb-fg-mutedand the stat sub-line uses--cb-fg-mutedas well; the stat label uses--cb-fg-subtle.
Credits
- Extracted from:
algoflashcards(src/lessons/primitives/chrome/CompletionScreenTemplate.tsx). The original was an eight-section completion screen tightly bound to a single lesson — phase breakdown, concept plaques, CodeTrace artifact, hero layoutId morph, metaphor tab gallery, and a continue CTA. craft-bits strips every curriculum-specific concern (trackHex,phaseRows,conceptPlaques,metaphors,heroLayoutId,srAnnouncement) and keeps the underlying intent: celebrate the finish with an animated title plus subtitle, surface a small set of stats, optionally embed bespoke content, then hand off to the next step via a single Continue CTA.