Skip to content

Progress Path

Show progress through ordered sections of items, with mastery and section milestones, such as a course.

import { ProgressPath, ProgressPathLesson, ProgressPathSection } from '@youj/react'

Installation & usage

npm i @youj/react

For an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.

Use a progress path for anything learned in order: a language course, an onboarding program, a training plan. Lessons are nodes on a trail that swings across the column, grouped into sections that each take a chart color and close on a trophy milestone. Each node wears a segmented ring, one segment per level of mastery. Lessons before the current one are done, the current one is an ink disc glowing in its section's color with a Start callout beside it, and the rest are locked. The trail is solid behind you and dotted ahead. For steps of a form, use a Stepper; for a pipeline's stages, a Stage Rail.

import { ProgressPath, type ProgressPathSection } from '@youj/react'

const sections: ProgressPathSection[] = [
  {
    id: 'basics',
    eyebrow: 'Section 1',
    title: 'Basics',
    lessons: [
      { id: 'greetings', title: 'Greetings', detail: '6 min', mastery: 4 },
      { id: 'numbers', title: 'Numbers', detail: '7 min', mastery: 1 },
      { id: 'basics-check', title: 'Basics checkpoint', milestone: true },
    ],
  },
]

<ProgressPath sections={sections} current="numbers" onSelect={(lesson) => open(lesson.id)} />

current is the lesson up next: those before it are done and those after it locked; null means the course is finished. Leave it out and the first lesson not fully mastered is up next. mastery counts the levels a lesson has mastered, out of levels (4 by default), and a done lesson at full mastery wears a crown. detail adds a mono line beside a lesson's title, and milestone draws a lesson as a trophy that closes its section. Sections take the chart colors in turn, or their own tone.

The trail swings wider as the column grows, and lesson titles sit beside their nodes once it's at least 32rem wide; narrower, the nodes carry the path alone. onSelect opens a lesson that's done or current. A locked one shakes its head and plays the error cue.

Reference

Propssections, current, levels… (7)

Props

ProgressPath props
PropType
sectionsRequiredProgressPathSection[]–
currentstring | null

The lesson up next: those before it are done and those after it locked. null when the course is finished. Default: the first lesson not fully mastered.

–
levelsnumber

Levels of mastery per lesson, one ring segment each.

default 4

4
onSelect(lesson: ProgressPathLesson, section: ProgressPathSection) => void

Called when a lesson that isn't locked is opened.

–
labelstring

Accessible name. Default: the labels' "Progress path".

–
localestring

Default: the YoujProvider locale.

–
soundboolean

Plays the success cue when the current lesson moves on.

default true

true
Other props pass through: extends ComponentProps<'div'>.
StylingParts and data attributes

Styling

Parts: progress-path, progress-path-trail, progress-path-walked (with data-walked), progress-path-section, progress-path-head, progress-path-eyebrow, progress-path-title, progress-path-value, progress-path-node (with data-state and data-milestone), progress-path-glow, progress-path-ring, progress-path-level (with data-state), progress-path-disc, progress-path-label (with data-side) and progress-path-callout.

MotionFirst view, current moves on, current lesson… · reduced motion

Motion

Motion tokens
MomentTokens
First viewthe walked trail draws itself, segment by segment
duration.slowease.emphasized

340ms · cubic-bezier(0.6, 0, 0.1, 1)

340mscubic-bezier(0.6, 0, 0.1, 1)
Current moves onthe next segment draws and the Start callout glides after it
duration.slowease.emphasized

340ms · cubic-bezier(0.6, 0, 0.1, 1)

340mscubic-bezier(0.6, 0, 0.1, 1)
Current lessona slow ping from its glow while the path is on screen
duration.deliberateease.exit

560ms · cubic-bezier(0.4, 0, 1, 1)

560mscubic-bezier(0.4, 0, 1, 1)
Locked lesson pressedthe node shakes its head
duration.baseease.standard

220ms · cubic-bezier(0.2, 0, 0, 1)

220mscubic-bezier(0.2, 0, 0, 1)

Reduced motion

The trail is drawn in place, the callout jumps, and there's no ping or shake.

Reduced motion

The trail is drawn in place, the callout jumps to the next lesson, and the current lesson glows without pinging.

AccessibilityKeyboard, ARIA

Accessibility

  • The path is a group named by label (default: the labels' "Progress path") and described by how to move through it; each section is named by its title.
  • Each lesson is a button named by its title, its place in the course, its status and its mastery ("Numbers, lesson 2 of 3, up next, mastery 1 of 4"). The current one has aria-current="step", and locked ones are aria-disabled.
  • The lessons are one tab stop, starting on the current lesson. Up and down walk the path, left and right do too (mirrored in RTL), and Home and End jump to its ends.
  • A section's progress is spoken with its count ("1 of 3 lessons").
  • Its words come from the labels (progressPath, progressPathHint, lessonItem, milestoneItem, lessonDone, lessonCurrent, lessonLocked, lessonStart, sectionProgress), translated by the language packs.