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

Usage

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

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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

PropTypeDefaultDescription
titleReactNodeHeadline at the top of the screen.
subtitleReactNodeOptional sub-headline in muted color.
statsCompletionScreenStat[]Tiles displayed in a responsive 1 / 2 / 3-column grid.
ctaCompletionScreenCTAContinue CTA. Omit to hide the button row.
onContinue() => voidClick handler. When omitted the CTA is disabled.
regionAriaLabelstring'Completion screen'aria-label for the outer region landmark.
childrenReactNodeOptional bespoke slot between the stats and the CTA.
classNamestringMerged onto the root div via cn().
...restHTMLAttributes<HTMLDivElement>Any other div attribute.

CompletionScreenStat

FieldTypeDescription
idstringStable identifier for React list rendering.
labelReactNodeShort label above the value.
valueReactNodePrimary value (formatted string or number).
descriptionReactNodeOptional sub-line beneath the value.

CompletionScreenCTA

FieldTypeDescription
labelReactNodeButton label.
ariaLabelstringOptional aria-label override.

Accessibility

  • Outer div carries role="region" and an aria-label (default Completion 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 and cursor-not-allowed.
  • Color contrast: subtitle uses --cb-fg-muted and the stat sub-line uses --cb-fg-muted as 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.