Online Course Template

£10

A complete one-page site for selling a single online course, built around a draggable pace planner: set your hours per week and watch the curriculum re-pack itself into real weeks with a live finish date.

ScrollTriggerDraggable Lenis intermediate
5 more details
draggablescroll-revealstaggercount-upkeyboard-navigation
Online Course Template - website template preview

This template reads better at your own screen size than in the frame below:

About this template

A production-ready one-pager for selling a single online course from one credible instructor. The fictional course is cyanotype printing, and the art direction is the subject itself: a bright cobalt ground (a cyanotype is a saturated blue print), paper-white panels for the dense content, white botanical silhouettes as authored SVG, and a cursive mono face writing the margin notes a printer pencils on a test strip. There are no photographs anywhere - every image is designed CSS or hand-authored SVG, so nothing needs sourcing or licensing.

Read the full template overview

The signature is the pace planner. A draggable hours-per-week slider re-packs the eight curriculum modules into real weeks as you drag: the week-by-week plan restacks live, each week's load bar resizes, and the projected finish date rolls to its new value. It is real arithmetic, not theatre - the planner reads each module's stated hours out of the curriculum section's own markup, so when a buyer edits their module list the plan recomputes itself. The slider snaps to whole hours, works fully from the keyboard, and on touch only claims horizontal gestures, so a vertical swipe on the handle still scrolls the page.

Around it the page makes the case a course page has to make: a promise-led hero with a framed print, an hour-stated curriculum, the instructor's story with a designed monogram portrait, count-up results, one honest price with a guarantee, and a FAQ written the way a confident teacher actually answers. Swap the course, the instructor and the module hours, point the enrol button at your checkout, and deploy it as static files - there is no build step.

What's included

12 items
  • Complete deployable one-page site: nav, promise-led hero, curriculum, pace planner, instructor, results, enrolment card, FAQ, footer
  • Signature pace planner: a draggable hours-per-week slider that re-packs the curriculum into weeks, resizes each week's load bar, and rolls the finish date - all computed from the module hours stated in the markup
  • The planner is honest arithmetic: edit a module's data-hours in the curriculum and the whole plan recomputes; delete the curriculum section and the planner degrades to its static default
  • Full slider accessibility: role=slider with aria-valuenow/min/max and a spoken valuetext, arrow keys, Home, End and PageUp/Down, visible focus rings that swap colour by surface
  • Touch-safe dragging: coordinates read null-safe from the raw event, nothing commits on a touch press, and only a proven-horizontal gesture scrubs - a vertical swipe on the handle scrolls the page
  • No photography to source: the hero print, student prints and instructor portrait are authored SVG and designed CSS panels with hand-written margin annotations
  • Count-up stats and scroll reveals that ship with final values in the markup, so no-JS and reduced-motion readers see finished numbers
  • Gabarito display, Albert Sans body, and Victor Mono's cursive italic doing the test-strip annotations - a pairing used nowhere else
  • Six-colour token block with RGB triples and a measured contrast table in the stylesheet header - a reskin is a six-value edit
  • Degrades completely: with JavaScript off the default six-hour plan renders as a readable static list and every section reads in full
  • Reduced motion keeps the planner fully working - plan, bars and date update instantly instead of animating, via both the CSS media query and the script's own motion branch
  • Lenis smooth scrolling wired in and fully optional

Perfect for

5 use cases
  • Course creators selling one cohort or self-paced course from their own site
  • Workshops, masterclasses and craft schools with a stated curriculum
  • Coaches and trainers whose programme is measured in hours per week
  • Membership or bootcamp landing pages that need an honest time-commitment story
  • Learning Draggable: a real, arithmetic-driven example of a slider that drives page content

How it works

5 sections

Structure

One script initialises everything with a single setup and teardown path, and one motion branch keyed only on reduced motion so exactly one branch runs on every device. Every querySelector is guarded, so any section - the stats, the instructor, the whole planner - can be deleted and the rest keeps working. Reveal start states are double-gated behind a has-js class and the motion media query, so a blocked CDN or a reduced-motion preference both leave a complete static page.

The pace planner

The planner reads each module's data-hours out of the curriculum section's markup, then packs them greedily into weeks of the chosen size, letting a module span a week boundary the way real study does. Dragging the slider recomputes the pack at every whole-hour stop: the week rows restack with a stagger, each load bar grows to its share of a ten-hour week, and the week count and finish date tween to their new values through GSAP proxies. The finish date is real calendar arithmetic - next Monday plus the plan, landing on the final Friday.

The drag itself

Draggable runs with allowNativeTouchScrolling, and the handler reads coordinates from the raw event, null-safe, never from the cached pointer. A touch press commits nothing; the gesture only starts planning once it has proved more horizontal than vertical, so a finger swiping up the page scrolls the page. A mouse click anywhere on the track seeks immediately, and the handle snaps onto its stop on release.

Accessibility and reduced motion

The handle is a real slider: role=slider, aria-valuemin/max/now, and an aria-valuetext that speaks the whole answer - hours, weeks and finish date - on every change. Arrow keys, PageUp/Down, Home and End all work, and focus rings swap colour per surface because marigold vanishes on marigold. Under reduced motion the planner stays fully interactive but everything updates instantly, and Lenis is never created.

Without JavaScript

The default six-hour plan is authored into the HTML as a plain list, character for character what the script would render, and the readout column states the same answer in words. Stats ship with their final figures in the markup and the FAQ is native details elements, so the page reads complete with scripting off.

Plugins ScrollTrigger, Draggable
Difficulty Intermediate
Smooth scroll Lenis integration
Includes HTML + JS + CSS source, documentation, AI setup prompt, lifetime updates

Questions about this template

What does the pace planner actually calculate?

You set hours per week on a slider and it re-packs your curriculum into real weeks, resizes each week's load bar and rolls a finish date, all computed from the module hours stated in the markup. Edit a module's data-hours and the whole plan recomputes; it is arithmetic, not a canned animation.

Does it handle enrolment and payment?

No. The enrolment card is designed and complete but unwired. Point it at Teachable, Podia, Gumroad, Kajabi or a Stripe payment link. The template's job is the honest time-commitment story that gets someone to the enrolment button.

Do I need photography or an instructor portrait?

No. The hero print, student prints and instructor portrait are authored SVG and designed CSS panels with hand-written margin annotations, so there is nothing to source before launch. You can add a real portrait later without disturbing the layout.

Will the slider fight page scrolling on a phone?

No. Coordinates are read null-safe from the raw event, nothing commits on a touch press, and only a proven-horizontal gesture scrubs, so a vertical swipe on the handle scrolls the page. It is also a full role=slider with arrow, Home, End and Page key support.

What if I delete the curriculum section?

The planner degrades to its static default rather than breaking. With JavaScript off you get the default six-hour plan as a readable static list, and count-ups ship with their final values in the markup, so no-JS and reduced-motion readers see finished numbers rather than zeros.

Paid template

Purchase to unlock the code.

Buying Online Course Template opens the HTML, CSS and JavaScript source, the full documentation, an AI setup prompt for your editor, and every update we ship to it. Standard license: unlimited personal and commercial projects.

£10 Standard license, unlimited projects

Browse free effects

Documentation

Quick Start

1. Files. Keep the structure as shipped:

Code snippet omitted: it ships with the download.

2. Swap the script tag for production. At the bottom of index.html:

Code snippet omitted: it ships with the download.

3. Make it your course.

  • Replace the copy: course name, instructor, promises, FAQ, price.
  • Edit the modules in #curriculum. Each module is one <li class="module" data-module data-hours="4" data-title="...">. The planner reads data-hours and data-title: change them and the whole plan recomputes on load. Add or remove modules freely.
  • Point the two enrol buttons (.btn--marigold in the nav and the enrol card) at your checkout or a mailto:.
  • Update the static plan in #pace if you change the modules (see "The static fallback plan" below), or leave it; with JavaScript on it is replaced at load anyway.

4. Optional: remove Lenis. Delete the lenis.min.js script tag. Anchors fall back to native smooth scrolling and everything else is untouched.

The pace planner

The planner element carries its configuration as data attributes:

Code snippet omitted: it ships with the download.

Attribute Default Description
data-min 2 Lowest hours-per-week stop
data-max 10 Highest stop; also the width a "full" load bar represents
data-start 6 The value the page loads with

The slider snaps to whole hours. The plan packs modules greedily into weeks of the chosen size, letting a module span a week boundary (marked "cont." in the plan). The finish date is next Monday plus the plan, landing on the final Friday, formatted for en-GB; change the locale in formatDate() in script.js if you want another.

If you delete the whole #curriculum section, the planner finds no module data and leaves the static plan in place with the slider inert, and the page still reads correctly.

The static fallback plan

The week list inside [data-plan] in index.html is what no-JS visitors (and search engines) read. It is authored to match the default 6 h/week pack of the shipped modules. If you change modules or hours, update it to match: load the page with JavaScript on, and copy what the script renders (right-click, inspect, copy outer HTML of the rows).

If you change the stop labels

The 2h … 10h labels under the track are static markup (.pace-stops). If you change data-min/data-max, update them to match.

Accessibility

  • The slider handle is a real role="slider" with aria-valuemin/max/now and an aria-valuetext that speaks the full answer ("6 hours a week: 6 weeks, finishing 4 Sep 2026") on every change.
  • Keyboard: arrow keys move one hour, PageUp/PageDown two, Home/End to the ends. Focus rings are visible on every interactive element and swap colour per surface.
  • Touch: a vertical swipe on the handle or track scrolls the page; only a proven-horizontal drag changes the plan. A tap on the track seeks.
  • Reduced motion: honoured by both the CSS media query and the script's matchMedia branch. The planner stays fully interactive; plan, bars, count-ups and date simply update instantly. Lenis is never created.
  • No JavaScript: the full page renders, including the default plan as a plain list and final stat figures.
  • Delete any section and the script skips it; every lookup is guarded.

Palette

Six colours, all defined once in :root in assets/style.css, each with an --*-rgb triple that every translucent value derives from. A reskin is editing six hexes and six triples; there are no stray rgba() literals to hunt.

Code snippet omitted: it ships with the download.

The stylesheet header records the measured contrast ratio of every pair as used. Two rules to keep when reskinning: the accent is banned as text on paper surfaces and as small text on the ground (it is a fill, a bar, a focus ring, or bold display type), and the dim/faint alpha values are pinned at the ratios listed; thin them and body copy drops below AA.

Type is Gabarito (display), Albert Sans (body) and Victor Mono italic (the hand-written annotations). Swap them in the Google Fonts link and the three --font-* tokens.

Dependencies

  • GSAP 3 core
  • ScrollTrigger (reveals, count-ups)
  • Draggable (the pace slider; without it the track still seeks on click and the keyboard still works)
  • Lenis (optional smooth scrolling)

All loaded from CDN in index.html; no bundler, no build step.

Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.

Your cart

Your cart is empty

The Vault £99

Every effect, template and UI element: 165 today, plus everything we release next.