NS Design System
v3.0.0 · 257 tokens

54Table of contents

Where you are in a long piece. Its own module rather than a blog feature — a post, a lesson, a doc page and a training unit all need the same outline, and three copies is three places for the scroll-spy to drift.

Use it for

  • Any prose over about four headings
  • --inline or --float on narrow screens, where a sticky rail has nowhere to stick

Not for

  • Three levels — a TOC that needs h4 is a TOC for a page that needs splitting
  • Short pages: an outline of two items is longer than the shortcut it offers
  • As the only navigation — it is an outline of one page, not a site map

Rail — the default

The hairline is the rail and the active item takes the 2px brand edge, the same current-item device the navbar and lesson row use.

markup
<nav class="ns-toc" aria-label="On this page">
  <span class="ns-toc__title">On this page</span>
  <a class="ns-toc__link" href="#0" aria-current="true">What is Apex</a>
  <a class="ns-toc__link ns-toc__link--sub" href="#0">Governor limits</a>
  <a class="ns-toc__link ns-toc__link--sub" href="#0">Bulkification</a>
  <a class="ns-toc__link" href="#0">Testing</a>
</nav>

Card, with reading progress

markup
<nav class="ns-toc ns-toc--card" aria-label="On this page" style="--ns-toc-progress:38%">
  <span class="ns-toc__progress" aria-hidden="true"></span>
  <span class="ns-toc__title">On this page</span>
  <a class="ns-toc__link" href="#0">What is Apex</a>
  <a class="ns-toc__link" href="#0" aria-current="true">Governor limits</a>
  <a class="ns-toc__link" href="#0">Testing</a>
</nav>

Numbered

For a procedure where the order IS the content. A CSS counter, so the numbers cannot get out of step with the list.

markup
<nav class="ns-toc ns-toc--numbered" aria-label="Steps">
  <a class="ns-toc__link" href="#0">Create the sandbox</a>
  <a class="ns-toc__link" href="#0" aria-current="true">Deploy metadata</a>
  <a class="ns-toc__link" href="#0">Run the test suite</a>
</nav>

Inline — the mobile strip

Scrolls sideways rather than wrapping: a wrapped strip changes where the article starts depending on how many headings it has.

markup
<nav class="ns-toc ns-toc--inline" aria-label="On this page">
  <a class="ns-toc__link" href="#0" aria-current="true">What is Apex</a>
  <a class="ns-toc__link" href="#0">Governor limits</a>
  <a class="ns-toc__link" href="#0">Bulkification</a>
  <a class="ns-toc__link" href="#0">Testing</a>
</nav>

Collapsible

markup
<details class="ns-toc ns-toc--collapsible">
  <summary>On this page<i class="ph ph-caret-down" aria-hidden="true"></i></summary>
  <a class="ns-toc__link" href="#0" aria-current="true">What is Apex</a>
  <a class="ns-toc__link" href="#0">Governor limits</a>
</details>

Accessibility contract

  • A real <nav> of real anchor links: it works with JS off, and the scroll-spy only adds [aria-current] on top
  • [data-toc-from] builds the list from the article's own headings for a CMS that emits a body but no outline; a hand-authored TOC is left exactly as it is
  • The progress bar is aria-hidden and never the only indicator — the marked link already says where you are