83Curriculum
The course's table of contents: mono-indexed section heads over the SAME .ns-lesson rows the player rail uses, so a lesson looks identical before and during the course. Sections are native <details> — collapse, keyboard operation and in-page find all come from the platform, and the section holding the current lesson simply ships with open. Four looks over one markup: hairline rows, --timeline, --compact, and rows carrying stills.
Use it for
- The course detail page, below the description
- Anywhere the full course structure is promised
- --compact in a narrow rail or a long syllabus
- --timeline where the ORDER is the point — a path or a certification track
Not for
- Inside the player — that is the player's own rail
- Editing — Curriculum builder (CMS)
- A two-item list. A curriculum with one section is a list
Collapsible
The default. The toolbar carries the totals and one control that expands or collapses everything; each head carries its own lesson count, runtime and completed fraction. Live — open the sections, or use the toolbar.
markup
<div class="ns-curriculum" style="max-inline-size:30rem;inline-size:100%">
<div class="ns-curriculum__bar">
<span class="ns-curriculum__totals">5 sections · 24 lessons · 6h 20m</span>
<button type="button" class="ns-btn ns-btn--quiet ns-btn--xs" data-curriculum-toggle aria-expanded="false">
<span data-curriculum-label>Expand all</span>
</button>
</div>
<details class="ns-curriculum__section" open>
<summary class="ns-curriculum__head">
<span class="ns-curriculum__index">01</span>
<h3 class="ns-curriculum__title">Getting oriented</h3>
<span class="ns-curriculum__meta">
<span class="ns-curriculum__done">2/3 done</span>
<span>32m</span>
<i class="ph ph-caret-down ns-curriculum__toggle" aria-hidden="true"></i>
</span>
</summary>
<a class="ns-lesson" href="#" data-state="done">
<span class="ns-lesson__index" aria-hidden="true"><i class="ph ph-check-circle"></i></span>
<span class="ns-lesson__body">
<span class="ns-lesson__title">What is an org?<span class="ns-visually-hidden"> (completed)</span></span>
<span class="ns-tooltip">What is an org?</span>
<span class="ns-lesson__sub"><span class="ns-ltype ns-ltype--video"><i class="ph ph-video" aria-hidden="true"></i>Video</span></span>
</span>
<span class="ns-lesson__time">08:12</span>
</a>
<a class="ns-lesson" href="#" data-state="done">
<span class="ns-lesson__index" aria-hidden="true"><i class="ph ph-check-circle"></i></span>
<span class="ns-lesson__body">
<span class="ns-lesson__title">Objects & fields<span class="ns-visually-hidden"> (completed)</span></span>
<span class="ns-lesson__sub"><span class="ns-ltype ns-ltype--article"><i class="ph ph-article" aria-hidden="true"></i>Article</span></span>
</span>
<span class="ns-lesson__time">12:40</span>
</a>
<a class="ns-lesson" href="#" aria-current="true">
<span class="ns-lesson__index" aria-hidden="true">03</span>
<span class="ns-lesson__body">
<span class="ns-lesson__title">Navigation that sticks</span>
<span class="ns-lesson__sub"><span class="ns-ltype ns-ltype--video"><i class="ph ph-video" aria-hidden="true"></i>Video</span><span class="ns-lesson__badge">Now playing</span></span>
</span>
<span class="ns-lesson__time">11:08</span>
</a>
</details>
<details class="ns-curriculum__section">
<summary class="ns-curriculum__head">
<span class="ns-curriculum__index">02</span>
<h3 class="ns-curriculum__title">Your first automation</h3>
<span class="ns-curriculum__meta"><span>4 lessons</span><span>1h 02m</span><i class="ph ph-caret-down ns-curriculum__toggle" aria-hidden="true"></i></span>
</summary>
<a class="ns-lesson" href="#">
<span class="ns-lesson__index" aria-hidden="true">04</span>
<span class="ns-lesson__body">
<span class="ns-lesson__title">Flow builder basics</span>
<span class="ns-lesson__sub"><span class="ns-ltype ns-ltype--video"><i class="ph ph-video" aria-hidden="true"></i>Video</span><span class="ns-lesson__badge">Preview</span></span>
</span>
<span class="ns-lesson__time">20:00</span>
</a>
<a class="ns-lesson" href="#">
<span class="ns-lesson__index" aria-hidden="true">05</span>
<span class="ns-lesson__body">
<span class="ns-lesson__title">Build it: an approval flow</span>
<span class="ns-lesson__sub"><span class="ns-ltype ns-ltype--lab"><i class="ph ph-flask" aria-hidden="true"></i>Hands-on lab</span></span>
</span>
<span class="ns-lesson__time">25:00</span>
</a>
<a class="ns-lesson" href="#" data-state="locked">
<span class="ns-lesson__index" aria-hidden="true">06</span>
<span class="ns-lesson__body">
<span class="ns-lesson__title">Section quiz<span class="ns-visually-hidden"> (locked — members only)</span></span>
<span class="ns-lesson__sub"><span class="ns-ltype ns-ltype--quiz"><i class="ph ph-question" aria-hidden="true"></i>Quiz</span><span>8 questions</span></span>
</span>
<span class="ns-lesson__time"><i class="ph ph-lock" aria-hidden="true"></i></span>
</a>
</details>
</div>Lesson types
Every row says what KIND of thing it is before you open it: a 14-minute video and a 14-minute lab are not interchangeable. Icon plus word — the icon carries it at a glance, the word carries it for anyone the icon fails, and the two never appear apart.
markup
<div style="display:flex;flex-wrap:wrap;gap:var(--space-5)">
<span class="ns-ltype ns-ltype--video"><i class="ph ph-video" aria-hidden="true"></i>Video</span>
<span class="ns-ltype ns-ltype--article"><i class="ph ph-article" aria-hidden="true"></i>Article</span>
<span class="ns-ltype ns-ltype--quiz"><i class="ph ph-question" aria-hidden="true"></i>Quiz</span>
<span class="ns-ltype ns-ltype--lab"><i class="ph ph-flask" aria-hidden="true"></i>Lab</span>
<span class="ns-ltype ns-ltype--live"><i class="ph ph-video-camera" aria-hidden="true"></i>Live</span>
<span class="ns-ltype"><i class="ph ph-arrow-down" aria-hidden="true"></i>Download</span>
</div>Timeline
The connector runs behind the index column and stops at the last section, so it reads as a path with an end rather than a rule that ran off the card. The section dot carries the state — done, current, or ahead. For a certification track or a roadmap, where the ORDER is the point.
Foundations
The data model18:00
03
Deployment
Change sets vs SFDX
markup
<div class="ns-curriculum ns-curriculum--timeline" style="max-inline-size:30rem;inline-size:100%">
<details class="ns-curriculum__section" data-state="done" open>
<summary class="ns-curriculum__head">
<span class="ns-curriculum__index"><i class="ph ph-check-circle" aria-hidden="true"></i></span>
<h3 class="ns-curriculum__title">Foundations</h3>
<span class="ns-curriculum__meta"><span class="ns-curriculum__done">Complete</span><i class="ph ph-caret-down ns-curriculum__toggle" aria-hidden="true"></i></span>
</summary>
<a class="ns-lesson" href="#" data-state="done"><span class="ns-lesson__index" aria-hidden="true"><i class="ph ph-check-circle"></i></span><span class="ns-lesson__title">The data model</span><span class="ns-lesson__time">18:00</span></a>
</details>
<details class="ns-curriculum__section" data-state="current" open>
<summary class="ns-curriculum__head">
<span class="ns-curriculum__index">02</span>
<h3 class="ns-curriculum__title">Apex</h3>
<span class="ns-curriculum__meta"><span>1/4 done</span><i class="ph ph-caret-down ns-curriculum__toggle" aria-hidden="true"></i></span>
</summary>
<a class="ns-lesson" href="#" aria-current="true"><span class="ns-lesson__index" aria-hidden="true">02</span><span class="ns-lesson__title">Classes and triggers</span><span class="ns-lesson__time">22:15</span></a>
<a class="ns-lesson" href="#"><span class="ns-lesson__index" aria-hidden="true">03</span><span class="ns-lesson__title">Bulkification</span><span class="ns-lesson__time">19:30</span></a>
</details>
<details class="ns-curriculum__section">
<summary class="ns-curriculum__head">
<span class="ns-curriculum__index">03</span>
<h3 class="ns-curriculum__title">Deployment</h3>
<span class="ns-curriculum__meta"><span>3 lessons</span><i class="ph ph-caret-down ns-curriculum__toggle" aria-hidden="true"></i></span>
</summary>
<a class="ns-lesson" href="#" data-state="locked"><span class="ns-lesson__index" aria-hidden="true">04</span><span class="ns-lesson__title">Change sets vs SFDX</span><span class="ns-lesson__time"><i class="ph ph-lock" aria-hidden="true"></i></span></a>
</details>
</div>Compact, with stills
--compact drops a size and tightens the rows — for a long syllabus or a narrow rail. A still where there is one, the lesson-type glyph where there is not: never an empty box.
markup
<div class="ns-curriculum ns-curriculum--compact" style="max-inline-size:26rem;inline-size:100%">
<details class="ns-curriculum__section" open>
<summary class="ns-curriculum__head">
<span class="ns-curriculum__index">01</span>
<h3 class="ns-curriculum__title">Start here</h3>
<span class="ns-curriculum__meta"><span>2 free</span><i class="ph ph-caret-down ns-curriculum__toggle" aria-hidden="true"></i></span>
</summary>
<a class="ns-lesson ns-lesson--compact" href="#">
<span class="ns-lesson__thumb ns-ph ns-ph--sm" aria-hidden="true"></span>
<span class="ns-lesson__body"><span class="ns-lesson__title">Welcome & setup</span><span class="ns-lesson__sub"><span class="ns-ltype ns-ltype--video"><i class="ph ph-video" aria-hidden="true"></i>Video</span><span class="ns-lesson__badge">Preview</span></span></span>
<span class="ns-lesson__time">04:20</span>
</a>
<a class="ns-lesson ns-lesson--compact" href="#">
<span class="ns-lesson__thumb" aria-hidden="true"><i class="ph ph-article"></i></span>
<span class="ns-lesson__body"><span class="ns-lesson__title">The data model tour</span><span class="ns-lesson__sub"><span class="ns-ltype ns-ltype--article"><i class="ph ph-article" aria-hidden="true"></i>Article</span><span>6 min read</span></span></span>
<span class="ns-lesson__time">06:00</span>
</a>
<a class="ns-lesson ns-lesson--compact" href="#" data-state="locked">
<span class="ns-lesson__thumb" aria-hidden="true"><i class="ph ph-lock"></i></span>
<span class="ns-lesson__body"><span class="ns-lesson__title">Security model<span class="ns-visually-hidden"> (locked)</span></span><span class="ns-lesson__sub"><span class="ns-ltype ns-ltype--video"><i class="ph ph-video" aria-hidden="true"></i>Video</span></span></span>
<span class="ns-lesson__time"><i class="ph ph-lock" aria-hidden="true"></i></span>
</a>
</details>
</div>Flat — a course
THE SHAPE A COURSE USES. Courses here do not have sections: a course is one arc of eight to fourteen lessons, and wrapping that in a single collapsible “Section 1” is a disclosure control that discloses everything. Sections belong to training, where a module really does contain several posts. Note the kind: an icon, not the word — hover or tab to it and the word comes back.
markup
<div class="ns-curriculum ns-curriculum--flat" style="max-inline-size:32rem;inline-size:100%">
<div class="ns-curriculum__bar">
<span class="ns-curriculum__totals">12 lessons · 3h 40m</span>
<span class="ns-curriculum__totals ns-curriculum__done">3 done</span>
</div>
<a class="ns-lesson" href="#0" data-state="done">
<span class="ns-lesson__index" aria-hidden="true"><i class="ph ph-check-circle"></i></span>
<span class="ns-lesson__body">
<span class="ns-lesson__title">What is an org?</span>
</span>
<span class="ns-ltype ns-ltype--icon ns-ltype--video ns-tooltip-host" tabindex="0">
<i class="ph ph-video" aria-hidden="true"></i><span class="ns-tooltip">Video</span>
</span>
<span class="ns-lesson__time">08:12</span>
</a>
<a class="ns-lesson" href="#0" data-state="done">
<span class="ns-lesson__index" aria-hidden="true"><i class="ph ph-check-circle"></i></span>
<span class="ns-lesson__body"><span class="ns-lesson__title">Objects & fields</span></span>
<span class="ns-ltype ns-ltype--icon ns-ltype--article ns-tooltip-host" tabindex="0">
<i class="ph ph-article" aria-hidden="true"></i><span class="ns-tooltip">Article</span>
</span>
<span class="ns-lesson__time">12:40</span>
</a>
<a class="ns-lesson" href="#0" aria-current="true">
<span class="ns-lesson__index">03</span>
<span class="ns-lesson__body"><span class="ns-lesson__title">Navigation that sticks</span></span>
<span class="ns-ltype ns-ltype--icon ns-ltype--video ns-tooltip-host" tabindex="0">
<i class="ph ph-video" aria-hidden="true"></i><span class="ns-tooltip">Video</span>
</span>
<span class="ns-lesson__time">11:08</span>
</a>
<a class="ns-lesson" href="#0">
<span class="ns-lesson__index">04</span>
<span class="ns-lesson__body"><span class="ns-lesson__title">Your first automation</span></span>
<span class="ns-ltype ns-ltype--icon ns-ltype--lab ns-tooltip-host" tabindex="0">
<i class="ph ph-flask" aria-hidden="true"></i><span class="ns-tooltip">Lab</span>
</span>
<span class="ns-lesson__time">18:22</span>
</a>
<a class="ns-lesson" href="#0">
<span class="ns-lesson__index">05</span>
<span class="ns-lesson__body"><span class="ns-lesson__title">Check what you know</span></span>
<span class="ns-ltype ns-ltype--icon ns-ltype--quiz ns-tooltip-host" tabindex="0">
<i class="ph ph-exam" aria-hidden="true"></i><span class="ns-tooltip">Quiz</span>
</span>
<span class="ns-lesson__time">6 q</span>
</a>
</div>Free and members-only
A curriculum a visitor cannot open yet still has to sell the course, so locked rows are DIMMED, never hidden — the titles are the argument. Free rows keep full contrast and the one green chip on the screen, because in a locked list they are the reason to sign up.
markup
<div class="ns-curriculum ns-curriculum--flat" style="max-inline-size:32rem;inline-size:100%">
<a class="ns-lesson" href="#0" data-access="free">
<span class="ns-lesson__index">01</span>
<span class="ns-lesson__body">
<span class="ns-lesson__title">What is an org?</span>
<span class="ns-lesson__sub"><span class="ns-laccess ns-laccess--free"><i class="ph ph-lock-simple-open" aria-hidden="true"></i>Free</span></span>
</span>
<span class="ns-ltype ns-ltype--icon ns-ltype--video ns-tooltip-host" tabindex="0">
<i class="ph ph-video" aria-hidden="true"></i><span class="ns-tooltip">Video</span>
</span>
<span class="ns-lesson__time">08:12</span>
</a>
<a class="ns-lesson" href="#0" data-access="free">
<span class="ns-lesson__index">02</span>
<span class="ns-lesson__body">
<span class="ns-lesson__title">Objects & fields</span>
<span class="ns-lesson__sub"><span class="ns-laccess ns-laccess--free"><i class="ph ph-lock-simple-open" aria-hidden="true"></i>Free preview</span></span>
</span>
<span class="ns-ltype ns-ltype--icon ns-ltype--article ns-tooltip-host" tabindex="0">
<i class="ph ph-article" aria-hidden="true"></i><span class="ns-tooltip">Article</span>
</span>
<span class="ns-lesson__time">12:40</span>
</a>
<a class="ns-lesson" href="#0" data-access="members">
<span class="ns-lesson__index">03</span>
<span class="ns-lesson__body">
<span class="ns-lesson__title">Navigation that sticks</span>
<span class="ns-lesson__sub"><span class="ns-laccess ns-laccess--members"><i class="ph ph-lock-simple" aria-hidden="true"></i>Members</span></span>
</span>
<span class="ns-ltype ns-ltype--icon ns-ltype--video ns-tooltip-host" tabindex="0">
<i class="ph ph-video" aria-hidden="true"></i><span class="ns-tooltip">Video</span>
</span>
<span class="ns-lesson__time"><i class="ph ph-lock-simple" aria-hidden="true"></i></span>
</a>
<a class="ns-lesson" href="#0" data-access="members">
<span class="ns-lesson__index">04</span>
<span class="ns-lesson__body">
<span class="ns-lesson__title">Your first automation</span>
<span class="ns-lesson__sub"><span class="ns-laccess ns-laccess--soon"><i class="ph ph-clock" aria-hidden="true"></i>Recording soon</span></span>
</span>
<span class="ns-ltype ns-ltype--icon ns-ltype--lab ns-tooltip-host" tabindex="0">
<i class="ph ph-flask" aria-hidden="true"></i><span class="ns-tooltip">Lab</span>
</span>
<span class="ns-lesson__time"><i class="ph ph-lock-simple" aria-hidden="true"></i></span>
</a>
</div>Modules — training
The OTHER shape, and the only one that keeps sections. A training module carries a still, a sentence saying what it is for, and its own progress, because a module is a thing you choose to start — a course lesson is not. Courses never render this.
01 Security and access Profiles, permission sets and the sharing model — who can see which record, and why the answer is almost never “profiles”.
The permission set model 09:30 02 The role hierarchy 14:0502 Automation Flow first, Apex when flow runs out — and how to tell which situation you are in before you build.
01 Record-triggered flow 16:20markup
<div class="ns-curriculum ns-curriculum--modules" style="max-inline-size:34rem;inline-size:100%">
<details class="ns-curriculum__section" open>
<summary class="ns-curriculum__head">
<span class="ns-curriculum__cover ns-ph ns-ph--sm" aria-hidden="true"></span>
<span class="ns-curriculum__headbody">
<span class="ns-curriculum__title"><span class="ns-curriculum__index">01</span> Security and access</span>
<span class="ns-curriculum__desc">Profiles, permission sets and the sharing model — who can see which record, and why the answer is almost never “profiles”.</span>
<span class="ns-curriculum__meta"><span>4 posts</span><span>1h 02m</span><span class="ns-curriculum__done">2 done</span></span>
</span>
<i class="ph ph-caret-down ns-curriculum__toggle" aria-hidden="true"></i>
</summary>
<a class="ns-lesson" href="#0" data-state="done">
<span class="ns-lesson__index" aria-hidden="true"><i class="ph ph-check-circle"></i></span>
<span class="ns-lesson__body"><span class="ns-lesson__title">The permission set model</span></span>
<span class="ns-ltype ns-ltype--icon ns-ltype--article ns-tooltip-host" tabindex="0">
<i class="ph ph-article" aria-hidden="true"></i><span class="ns-tooltip">Article</span>
</span>
<span class="ns-lesson__time">09:30</span>
</a>
<a class="ns-lesson" href="#0" aria-current="true">
<span class="ns-lesson__index">02</span>
<span class="ns-lesson__body"><span class="ns-lesson__title">The role hierarchy</span></span>
<span class="ns-ltype ns-ltype--icon ns-ltype--video ns-tooltip-host" tabindex="0">
<i class="ph ph-video" aria-hidden="true"></i><span class="ns-tooltip">Video</span>
</span>
<span class="ns-lesson__time">14:05</span>
</a>
</details>
<details class="ns-curriculum__section">
<summary class="ns-curriculum__head">
<span class="ns-curriculum__cover ns-ph ns-ph--sm" aria-hidden="true"></span>
<span class="ns-curriculum__headbody">
<span class="ns-curriculum__title"><span class="ns-curriculum__index">02</span> Automation</span>
<span class="ns-curriculum__desc">Flow first, Apex when flow runs out — and how to tell which situation you are in before you build.</span>
<span class="ns-curriculum__meta"><span>6 posts</span><span>1h 48m</span></span>
</span>
<i class="ph ph-caret-down ns-curriculum__toggle" aria-hidden="true"></i>
</summary>
<a class="ns-lesson" href="#0">
<span class="ns-lesson__index">01</span>
<span class="ns-lesson__body"><span class="ns-lesson__title">Record-triggered flow</span></span>
<span class="ns-ltype ns-ltype--icon ns-ltype--video ns-tooltip-host" tabindex="0">
<i class="ph ph-video" aria-hidden="true"></i><span class="ns-tooltip">Video</span>
</span>
<span class="ns-lesson__time">16:20</span>
</a>
</details>
</div>Accessibility contract
- Sections are <details>/<summary>: open state, keyboard operation and find-in-page are the platform's, not a re-implementation
- Rows are links; state is spelled in visually-hidden text, not only drawn
- The lesson TYPE is an icon whose word is deferred, not deleted: it stays in the DOM for assistive tech and returns on hover OR keyboard focus, so it is never a mouse-only affordance
- Locked rows are dimmed and carry a lock glyph plus a word — never opacity alone
- Locked rows stay links (to the join page) and their lock is a glyph, not only 0.7 opacity
- Expand-all is labelled with the ACTION and flips once the sections do