Learning Path Card

A high-impact "destination" tile that links to a multi-chapter learning module. Magazine-style hierarchy: a mono-caps eyebrow, a serif title, an optional italic subtitle, a short description, a metadata row (chapters · duration), and a thin progress bar that hugs the bottom edge.

Customize
Progress
35%
Chapters
3
Accent

Installation

npx shadcn@latest add https://craftbits.dev/r/learning-path-card.json

Usage

import { LearningPathCard } from "@craft-bits/core";
 
<LearningPathCard
  href="/paths/attention"
  eyebrow="Learning path"
  title="Attention, from the ground up"
  subtitle="A walk through the transformer's beating heart."
  description="Three chapters that build attention from a dot product to a multi-head block."
  chapters={3}
  duration="~45 min"
  progress={0.35}
  accent
/>

Understanding the component

  1. Anchor at the root. The card renders as a single <a> so the entire surface is one click target — no nested-link traps, no JS for navigation. forwardRef<HTMLAnchorElement> lets routers (Next.js Link, Remix Link) hand off the underlying anchor.
  2. Editorial hierarchy. Eyebrow (small mono caps) → serif title → italic subtitle → body description → metadata row. The serif title uses text-wrap: balance so two-line headlines break evenly; the description uses text-wrap: pretty to avoid orphans.
  3. Accent rail signals the featured path. accent={true} paints a 3px --cb-accent left border and tints the eyebrow with the accent color. Use it once per row to mark a recommended or in-progress path.
  4. Hover lift is two-property only. transition is scoped to box-shadow,transform,border-color (never transition-all). On hover the card lifts -translate-y-0.5, its shadow deepens via a two-layer recipe, and the title color shifts toward the accent. The arrow in the metadata row nudges right via group-hover:translate-x-0.5.
  5. Progress hugs the bottom edge. When progress is provided, a 4px track sits flush against the bottom of the card (outside the inner padding) with the fill colored by the accent state. The fill width animates via a CSS transition: width 300ms ease-out — no motion library, SSR-safe.
  6. Shadows never use pure black. Both shadow layers are rgb(15, 15, 15) (a neutral near-black) at low alpha, which sits naturally over any theme.

Props

PropTypeDefaultDescription
titlestringrequiredCard title — serif, large.
subtitlestringItalic subtitle line below the title.
descriptionReactNodeShort editorial blurb.
progressnumberCompletion ratio in [0, 1]. Renders the thin bottom progress bar when provided.
chaptersnumberChapter count — rendered in the metadata row.
durationstringDuration label, e.g. '~45 min'.
hrefstringrequiredDestination URL.
eyebrowstringSmall uppercase label above the title.
accentbooleanfalseWhen true, adds a 3px accent left rail and accent-tinted eyebrow.
classNamestringMerged onto the rendered <a> via cn().

Accessibility

  • The entire card is a single anchor — the title, subtitle, description, and metadata all read as part of the link's accessible name.
  • When progress is provided, the bottom track exposes role="progressbar" with aria-valuenow/aria-valuemin/aria-valuemax and a synthesized aria-label ("35% complete").
  • Decorative separators (the · between chapters and duration) are marked aria-hidden="true".
  • The focus ring is :focus-visible only — a 2px accent ring with a 2px offset against the page background, meeting WCAG AA contrast in both light and dark themes.
  • The hover lift transitions only box-shadow, transform, and border-color — never transition-all. Animations complete in under 300ms (Doherty).

Credits

  • Extracted from: terminal-dreams (src/components/principles/LearningPathCard.tsx).