remold/ui

Course outline

Modules and lessons joined by lines that stay attached while modules open, lessons get ticked off and finished work drops away.

ConnectorsMotionIcon

Course

Product Design Foundations

5 of 14 lessons
Up next: Making sense of notes
Talking to users12 min
Jobs to be done10 min
Check your understanding5 min
Layout and grids11 min
Type that reads well9 min
Colour with purpose13 min
Motion basics7 min

Every course platform, creator site and onboarding flow has an outline like this. Here the lines from each module to its lessons are redrawn on every frame of the animation, so they bend and stretch with the rows instead of jumping when something opens, closes or drops away.

Things to try

  • Open and close modules. The lessons slide out of the way and the lines grow and retract with them.
  • Tick a lesson off. The circle becomes a tick, the module's count and the progress bar update, and "Up next" moves on. Finish every lesson in a module and its number fills in.
  • Show only what's remaining. Finished lessons leave, their lines drawing back out, and the rest close up. A finished module disappears entirely.
  • Use the keyboard. Up and down move through the outline; right opens a module, left closes it or goes up to it.

Install

npx shadcn@latest add https://your-docs-site/r/course-outline.json

Dependencies

The install command adds these for you. If you copy the code by hand, install them yourself.

npm install @base-ui/react @hugeicons/core-free-icons @hugeicons/react @remold-ui/react motion

How it works

Each module's number carries data-rm-anchor with the module's id, and each lesson's tick carries data-rm-link with its module's id. Connectors draws a line from every anchor to everything that links to it. Motion owns the rows' layout animation, while useConnectorMotion keeps those lines attached during the transition.

const connectorRef = useRef<HTMLDivElement>(null);
const connectorMotion = useConnectorMotion(connectorRef);

<Remold.Connectors ref={connectorRef} radius={10}>
  <motion.div layout {...connectorMotion}>
    {rows.map((row) =>
      row.module ? (
        <motion.button layout {...connectorMotion} key={row.id}>
          <Module number={<span data-rm-anchor={row.id}>{row.n}</span>} />
        </motion.button>
      ) : (
        <motion.div layout {...connectorMotion} key={row.id}>
          <Lesson tick={<button data-rm-anchor={row.id} data-rm-link={row.parent} />} />
        </motion.div>
      ),
    )}
  </motion.div>
</Remold.Connectors>
The course and its lessons are made up.

On this page