NS Design System
v3.0.0 · 257 tokens

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 &amp; 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.

Video Article Quiz Lab Live Download
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

Complete
The data model18:00
02

Apex

1/4 done
Classes and triggers22:15 Bulkification19:30
03

Deployment

3 lessons
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 &amp; 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 &amp; 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 &amp; 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”. 4 posts1h 02m2 done The permission set model Article 09:30 02 The role hierarchy Video 14:05
02 Automation Flow first, Apex when flow runs out — and how to tell which situation you are in before you build. 6 posts1h 48m 01 Record-triggered flow Video 16:20
markup
<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