Course Progress Tracker — Free LMS Sidebar UI Snippet

Course Progress Tracker · Dashboards · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Averaged completion
Header ring computed from every module's data-progress.
Shared ring math
One --pct custom property drives every mini ring's dashoffset.
Resume card
Highlighted "continue where you left off" next-lesson link.
Scroll-to-current
Resume click scrolls and briefly flashes the active module.
Status modifiers
.is-done and .is-current classes swap icon and colors via CSS.
Animated fill-in
Ring and bar animate to their values on load via requestAnimationFrame.
Self-contained rows
Add a module by copying a row and setting two attributes.
Dark LMS styling
Indigo-to-violet gradient accents on a dark panel background.

About this UI Snippet

Course Progress Tracker — Overall Ring, Module List & Resume Card

Screenshot of the Course Progress Tracker snippet rendered live

The course progress tracker is the panel every online-learning product shows a learner as soon as they log in: how far through the course am I, and what's next? This snippet builds that panel with a header completion ring, a slim overall progress bar, a "continue where you left off" resume card, and a scrollable module list where each row carries its own mini progress ring and a done/current/upcoming status icon.

One data source, two visualizations

Rather than hard-coding the header percentage, the JS reads every module's data-progress attribute, averages them, and drives both the big SVG ring and the slim top bar off that single computed number. This mirrors how a real LMS would compute completion — from the actual state of each module — instead of a value someone forgot to update by hand.

SVG rings for the completion visuals

Both the header ring and each row's mini ring are plain <circle> elements with stroke-dasharray set to their circumference and stroke-dashoffset animated to reveal the filled arc. The mini rings use a CSS custom property, --pct, so the same stroke-dashoffset formula works for every row without repeating the circumference math per element — set --pct on the row's HTML and the ring fills itself.

The resume card as a focal point

The "continue where you left off" card sits between the top progress summary and the module list, styled with a gradient border-glow treatment so it reads as the primary next action rather than another list row. Clicking it scrolls the matching in-progress module into view and briefly highlights it — a pattern useful for deep-linking a learner straight back into an in-progress module.

Status-driven module rows

Each .cpt-mod carries a modifier class — .is-done, .is-current, or neither for upcoming — that swaps the icon glyph and colors via CSS alone, no per-row JS branching needed. Add a module by copying a row, setting its data-progress and the modifier class, and the ring and header math pick it up automatically since the average is computed from live DOM state.

Customizing it

Swap in real module counts, add a locked-state icon for modules gated behind prerequisites, or read progress from an API and set data-progress before this script runs. Pair it with a lesson sidebar nav for full-course navigation, a progress wizard for a linear multi-step flow, or circular progress for the ring pattern on its own.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to work out the ring math or the averaging logic by hand. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how a single --pct CSS custom property lets one stroke-dashoffset formula drive every module's mini progress ring, or why the header ring's percentage is computed by averaging each module's data-progress attribute rather than being hard-coded. The same assistant can help optimize it — ask whether the reduce over data-progress values should instead weight modules by lesson count, or whether the ring's animation should use CSS @property for a smoother transition. It's also useful for extending the tracker: ask it to add a locked-module state that disables navigation until prerequisites complete, wire data-progress from a real API response, or add a confetti burst when the course hits 100%. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "course progress tracker" dashboard panel in plain HTML, CSS, and JavaScript — no framework, no library.

Requirements:
- A header section with an SVG ring showing the overall course completion percentage as a filled arc (using stroke-dasharray/stroke-dashoffset on a circle), plus a slim horizontal progress bar below it showing the same percentage.
- Compute that overall percentage in JavaScript, not by hard-coding it: read a data-progress attribute (0-100) off every module list item in the DOM, average them, and use that single computed value to set both the ring's stroke-dashoffset and the bar's width.
- A highlighted "continue where you left off" card, visually distinct (e.g. a gradient border or background glow) from the rest of the list, showing the next lesson's title and remaining time, that on click scrolls the current in-progress module into view in the list below and briefly flashes its background.
- A scrollable list of course modules below the resume card, each row showing: a status icon that differs for completed (checkmark), current (filled dot), and upcoming (empty circle) states via a CSS modifier class rather than per-row JavaScript branching, a title and lesson-count subtitle, and its own small SVG progress ring sized independently from the header ring.
- Make every mini ring share one CSS formula for stroke-dashoffset driven by a single CSS custom property (e.g. --pct) set inline per row, so adding a new module only requires setting that one property and a status class, not writing new CSS.
- Animate both the header ring and the top progress bar filling in on page load using requestAnimationFrame so the fill transition is visible rather than snapping instantly to its final state.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Paste the HTML, CSS, and JSThe panel renders with sample module data already filled in.
  2. 2
    Check the header ringIt animates in showing the averaged completion percentage.
  3. 3
    Review the module listDone, current, and upcoming rows each carry their own mini ring.
  4. 4
    Click "Continue where you left off"The current module row scrolls into view and briefly highlights.
  5. 5
    Edit data-progress valuesChange a module's data-progress attribute in the HTML panel.
  6. 6
    Reload to see the recomputeThe header ring and bar update to match the new average.

Real-world uses

Common Use Cases

Online course dashboards
Show learners overall and per-module progress at a glance.
Corporate training LMS
Track compliance-course completion across required modules.
Cohort-based programs
Pair with a lesson sidebar nav for in-lesson navigation.
Certification prep apps
Surface which modules remain before a quiz result breakdown.
Onboarding curricula
Adapt the same ring pattern as onboarding checklist widget.
Skill-tracking apps
Combine with circular progress rings for other metrics.

Got questions?

Frequently Asked Questions

The script collects every element with class .cpt-mod, reads its data-progress attribute, sums them, and divides by the module count. That single averaged number then drives both the header SVG ring's stroke-dashoffset and the slim top progress bar's width, so there's one source of truth instead of two numbers that could drift apart.

Each row's mini ring circle has a CSS custom property --pct set inline (e.g. style="--pct:50"), and the CSS computes stroke-dashoffset as the circumference minus circumference times --pct over 100. Because the formula lives once in the CSS and only --pct changes per row, adding a new module row just means copying markup and setting one property.

Add a .is-locked modifier alongside the existing .is-done/.is-current pattern, dim the row with reduced opacity and pointer-events: none in CSS, and swap the icon glyph to a lock symbol. Check the previous module's data-progress before allowing navigation into a locked one if you wire this to real routing.

Yes — replace the href="#" and the preventDefault-based scroll behavior with a real lesson URL once you have routing. The current demo intercepts the click to keep the example self-contained inside the sandbox, but in a real app you'd just navigate normally.

Model modules as an array of objects with title, subtitle, progress, and status fields, map them to rows, and compute the averaged percentage with a reduce in a memoized value or computed property. Drive the ring's stroke-dashoffset from that computed percentage instead of a DOM query, and the CSS ports over unchanged.