Weak Patterns Callout

A "needs reinforcement" callout — a warning-tinted panel that surfaces a list of weak or struggling patterns the reader should revisit. Each pattern is rendered as a chip with an optional score readout and optional link. Pair it with a generic "items below mastery threshold" filter and let the component decide whether to render itself (empty arrays short-circuit to nothing).

Preview

Needs reinforcement

Patterns trending below your mastery threshold this week.

Customize
Callout
0
3

Installation

npx shadcn@latest add https://craftbits.dev/r/weak-patterns-callout.json

Usage

import { WeakPatternsCallout } from "@craft-bits/core";
 
<WeakPatternsCallout
  patterns={[
    { label: "Two-pointer", score: 0.34, href: "/topics/two-pointer" },
    { label: "Sliding window", score: 0.41 },
  ]}
  cta={<a href="/review">Review now</a>}
/>

Drop the score for a flat list of names, or pass an empty array to short-circuit:

<WeakPatternsCallout patterns={weakItems} />

Switch the surface palette via the tone prop:

<WeakPatternsCallout tone="info" patterns={items} />
<WeakPatternsCallout tone="error" patterns={items} />

Understanding the component

  1. Empty arrays render nothing. The component checks patterns.length === 0 before any DOM is emitted. Pass the raw weak-items array and skip the conditional at the call site.
  2. Tone drives the palette. warning (default), info, and error each derive their background, border, and chip tint from the cb semantic tokens — no inline colours.
  3. Pattern shape is flat. Each chip is { label, id?, score?, href? }. id is only needed when labels may collide. When href is present, the chip renders as a real anchor and picks up focus-visible affordances.
  4. Scores are formatted, not raw. Pass a 0–1 value to pattern.score and the component renders it as 0–100%. Override formatScore to swap the unit (e.g. raw count, "low") or return null to suppress.
  5. Chip stagger respects reduced motion. Each chip enters with a small y shift staggered by STAGGER (40ms). Under prefers-reduced-motion: reduce the entry collapses to a static fade.

Variants

Default — warning tone

<WeakPatternsCallout
  patterns={[
    { label: "Two-pointer", score: 0.34 },
    { label: "Sliding window", score: 0.41 },
  ]}
/>

Info tone with CTA

<WeakPatternsCallout
  tone="info"
  title="Drift detected"
  description="Three patterns are trending down."
  patterns={[
    { label: "Binary search", score: 0.62, href: "#bs" },
    { label: "DFS", score: 0.71, href: "#dfs" },
  ]}
  cta={<a href="#review">Review</a>}
/>

Error tone, no scores

<WeakPatternsCallout
  tone="error"
  title="Failing fast"
  patterns={[{ label: "Heap math" }, { label: "Trie traversal" }]}
/>

Props

PropTypeDefaultDescription
patternsreadonly { label: string; id?: string; score?: number; href?: string }[]requiredItems to surface. Empty array renders nothing.
titleReactNode"Needs reinforcement"Headline copy.
descriptionReactNodeOptional sub-line below the title.
tone'warning' | 'info' | 'error''warning'Surface palette.
ctaReactNodeTrailing action node on the header row.
iconComponentType<{ className?: string }> | nullbuilt-in alert glyphLeading icon. Pass null to suppress.
formatScore(score: number) => ReactNode0–1 → 0–100%Score formatter. Return null to suppress.
classNamestringMerged onto the root via cn().
...restHTMLAttributes<HTMLDivElement>Any other root attribute.

Accessibility

  • The root is announced as a polite live region (role="region" + aria-live="polite") so chip-list updates are surfaced to assistive tech without interrupting the user.
  • Linked chips render as real anchors with focus-visible ring affordances drawn from the active tone token.
  • The leading icon is aria-hidden="true" — the title carries the semantic weight.
  • Chip stagger and entry motion are suppressed under prefers-reduced-motion: reduce; only opacity fades remain.
  • All three tone colours read from the cb semantic token system (--cb-warning, --cb-info, --cb-error) and clear WCAG AA contrast on the default surface tokens.

Credits

  • Extracted from: algoflashcards (src/platform/ui/WeakPatternsCallout.tsx). The original was a thin wrapper over the project's Alert / Badge shadcn components with a hard-coded "Needs Reinforcement" string, bg-warning/N inline tokens, and a patterns: string[] API. craft-bits drops the shadcn Alert shell, replaces every inline colour with cb-* semantic classes, generalises the pattern shape to { label, id?, score?, href? }, adds tone variants, a configurable title / description / CTA / icon, score formatting, and a staggered chip entry that respects prefers-reduced-motion.