NS Design System
v3.0.0 · 257 tokens

101Slide deck

A presentation built out of the product's own parts. Six geometries — lead, center, stack, split, aside, full — and four tones, and that is the whole vocabulary; everything INSIDE a slide is a component that already exists (.ns-code, .ns-compare, .ns-statblock, .ns-checklist, .ns-timeline, .ns-card). A slide sizes itself in container units, so the same markup is legible on a projector, correct in a 15rem overview thumbnail, and readable on a phone — no scale transform, no resize listener. Light by default, because a projector in a lit room is a light surface; dark mode is the same deck under [data-theme="dark"].

Use it for

  • Teaching a course session, a workshop or a lunch-and-learn
  • Walking a room through a POC or a project you are building
  • A conference or meetup talk that has to be shared afterwards as a link
  • Embedding the session's slides inside the written lesson — data-mode="scroll"

Not for

  • A document — that is a lesson, and a lesson does not fit in 16:9
  • A dashboard on a wall — the deck is driven, not ambient
  • Anything where the audience reads at their own pace: the fragment reveal is a teaching device and becomes an obstacle the moment nobody is presenting

The frame

The three-row canvas every slide shares: head, body, running foot. The layout modifier changes what the BODY does and never where the head and the foot sit — which is what makes a 40-slide deck feel like one document instead of 40 posters. The 3px leading edge is the brand mark; it is on every slide so that none of them needs a logo.

Consequences

Three things that follow from that

  • 01Never query inside a loop200 records × one SOQL each is 200 queries against a limit of 100.
  • 02Trigger.new is a list, alwaysEven when a user saved one record.
  • 03One trigger per objectTwo run in an order the platform does not promise.
Module 01 06 / 25
markup
<div class="ns-deck" data-mode="scroll" style="inline-size:100%;background:none">
  <section class="ns-slide">
    <div class="ns-slide__inner">
      <div class="ns-slide__head">
        <span class="ns-kicker">Consequences</span>
        <h2 class="ns-slide__title">Three things that follow from that</h2>
        <span class="ns-slide__rule"></span>
      </div>
      <div class="ns-slide__body">
        <ul class="ns-slide__points">
          <li class="ns-slide__point"><span class="ns-slide__point-index">01</span><span><b class="ns-slide__point-title">Never query inside a loop</b><span class="ns-slide__point-note">200 records × one SOQL each is 200 queries against a limit of 100.</span></span></li>
          <li class="ns-slide__point"><span class="ns-slide__point-index">02</span><span><b class="ns-slide__point-title">Trigger.new is a list, always</b><span class="ns-slide__point-note">Even when a user saved one record.</span></span></li>
          <li class="ns-slide__point"><span class="ns-slide__point-index">03</span><span><b class="ns-slide__point-title">One trigger per object</b><span class="ns-slide__point-note">Two run in an order the platform does not promise.</span></span></li>
        </ul>
      </div>
      <div class="ns-slide__foot">
        <span class="ns-slide__where">Module 01</span>
        <span class="ns-slide__num">06 / 25</span>
      </div>
    </div>
  </section>
</div>

Tones

--sunken flips with the theme; --dark and --brand do NOT — they are the brand's own surfaces, identical in both themes, and they exist to mark a gear change in the talk. Use one dark slide per module and no more: a deck where every third slide is navy has no gear changes left. --grid adds the dissolving hairline motif, the same one the hero band carries.

Module 01

What a trigger actually is

Ten minutes. One idea, and then we look at real code.

Apex fundamentals04 / 25
The limit that shapes everything 100

SOQL queries per synchronous transaction.

Apex Developer Guide · Execution Governors
Module 0310 / 25
markup
<div class="ns-deck" data-mode="scroll" style="inline-size:100%;background:none">
  <section class="ns-slide ns-slide--lead ns-slide--dark ns-slide--grid">
    <div class="ns-slide__inner">
      <div class="ns-slide__head">
        <span class="ns-kicker">Module 01</span>
        <h2 class="ns-slide__title">What a trigger actually is</h2>
        <p class="ns-slide__lede">Ten minutes. One idea, and then we look at real code.</p>
      </div>
      <div class="ns-slide__foot"><span class="ns-slide__where">Apex fundamentals</span><span class="ns-slide__num">04 / 25</span></div>
    </div>
  </section>
  <section class="ns-slide ns-slide--center ns-slide--brand ns-slide--grid">
    <div class="ns-slide__inner">
      <div class="ns-slide__head">
        <span class="ns-kicker">The limit that shapes everything</span>
        <span class="ns-slide__figure-value">100</span>
        <p class="ns-slide__lede">SOQL queries per synchronous transaction.</p>
        <span class="ns-slide__caption">Apex Developer Guide · Execution Governors</span>
      </div>
      <div class="ns-slide__foot"><span class="ns-slide__where">Module 03</span><span class="ns-slide__num">10 / 25</span></div>
    </div>
  </section>
</div>

Explain, then show

--split: the claim on the left, the code that proves it on the right. This is the shape most technical teaching wants and the one most decks skip, putting the code on its own slide where nobody can see what it was supposed to demonstrate. The code block is .ns-code — the same component the lesson pages use, re-tuned to the slide's type scale and nothing else.

The pattern

Collect the ids, query once, map, then act

  • 01Walk the batch, collect the ids
  • 02One SOQL, outside the loop
  • 03Index it into a Map
CaseRouter.cls
Set<Id> ids = new Set<Id>();for (Case c : Trigger.new) ids.add(c.AccountId);// one query, outside the loopMap<Id, Account> byId = new Map<Id, Account>(  [SELECT Id, Tier__c FROM Account WHERE Id IN :ids]);
Module 0307 / 25
markup
<div class="ns-deck" data-mode="scroll" style="inline-size:100%;background:none">
  <section class="ns-slide ns-slide--split ns-slide--split-figure">
    <div class="ns-slide__inner">
      <div class="ns-slide__head">
        <span class="ns-kicker">The pattern</span>
        <h2 class="ns-slide__title">Collect the ids, query once, map, then act</h2>
      </div>
      <div class="ns-slide__body">
        <div class="ns-slide__col">
          <ul class="ns-slide__points ns-slide__points--tight">
            <li class="ns-slide__point"><span class="ns-slide__point-index">01</span><span class="ns-slide__point-title">Walk the batch, collect the ids</span></li>
            <li class="ns-slide__point"><span class="ns-slide__point-index">02</span><span class="ns-slide__point-title">One SOQL, outside the loop</span></li>
            <li class="ns-slide__point"><span class="ns-slide__point-index">03</span><span class="ns-slide__point-title">Index it into a Map</span></li>
          </ul>
        </div>
        <div class="ns-slide__col ns-slide__col--fill">
          <figure class="ns-code ns-code--dark" data-lang="apex">
            <figcaption class="ns-code__bar"><span class="ns-code__file"><i class="ph ph-code" aria-hidden="true"></i><span>CaseRouter.cls</span></span></figcaption>
  <div class="ns-code__body">
    <pre class="ns-code__gutter" aria-hidden="true">1
2
3
4
5
6</pre>
    <pre class="ns-code__pre"><code><span class="ns-code__line"><span class="ns-tok-type">Set</span><span class="ns-tok-punct">&lt;</span><span class="ns-tok-type">Id</span><span class="ns-tok-punct">&gt;</span> ids <span class="ns-tok-punct">=</span> <span class="ns-tok-kw">new</span> <span class="ns-tok-type">Set</span><span class="ns-tok-punct">&lt;</span><span class="ns-tok-type">Id</span><span class="ns-tok-punct">&gt;();</span></span><span class="ns-code__line"><span class="ns-tok-kw">for</span> <span class="ns-tok-punct">(</span><span class="ns-tok-type">Case</span> c <span class="ns-tok-punct">:</span> <span class="ns-tok-type">Trigger</span><span class="ns-tok-punct">.</span><span class="ns-tok-kw">new</span><span class="ns-tok-punct">)</span> ids<span class="ns-tok-punct">.</span><span class="ns-tok-fn">add</span><span class="ns-tok-punct">(</span>c<span class="ns-tok-punct">.</span><span class="ns-tok-type">AccountId</span><span class="ns-tok-punct">);</span></span><span class="ns-code__line">​</span><span class="ns-code__line"><span class="ns-tok-com">// one query, outside the loop</span></span><span class="ns-code__line"><span class="ns-tok-type">Map</span><span class="ns-tok-punct">&lt;</span><span class="ns-tok-type">Id</span><span class="ns-tok-punct">,</span> <span class="ns-tok-type">Account</span><span class="ns-tok-punct">&gt;</span> byId <span class="ns-tok-punct">=</span> <span class="ns-tok-kw">new</span> <span class="ns-tok-type">Map</span><span class="ns-tok-punct">&lt;</span><span class="ns-tok-type">Id</span><span class="ns-tok-punct">,</span> <span class="ns-tok-type">Account</span><span class="ns-tok-punct">&gt;(</span></span><span class="ns-code__line">  <span class="ns-tok-punct">[</span><span class="ns-tok-kw">SELECT</span> <span class="ns-tok-type">Id</span><span class="ns-tok-punct">,</span> <span class="ns-tok-type">Tier__c</span> <span class="ns-tok-kw">FROM</span> <span class="ns-tok-type">Account</span> <span class="ns-tok-kw">WHERE</span> <span class="ns-tok-type">Id</span> <span class="ns-tok-kw">IN</span> <span class="ns-tok-punct">:</span>ids<span class="ns-tok-punct">]);</span></span></code></pre>
  </div>
          </figure>
        </div>
      </div>
      <div class="ns-slide__foot"><span class="ns-slide__where">Module 03</span><span class="ns-slide__num">07 / 25</span></div>
    </div>
  </section>
</div>

The architecture slide

Boxes and arrows as TEXT, never a picture. A PNG of a diagram cannot restyle for dark mode, cannot be read by a screen reader, cannot be translated, and cannot be edited next year by the person who inherits the deck. aria-current marks the box you are talking about — its border brightens to brand, because elevation here is a border and never a lift.

Where your code runs

The order of execution, abridged

01 · loadRecord loadedFrom the DB, or initialised for an insert.
02 · beforeBefore triggersYou are here. No DML needed to change the record.
03 · rulesValidation rulesWhich is why a before trigger can fix data a rule would reject.
04 · afterAfter triggersThe record has an Id.
Module 0212 / 25
markup
<div class="ns-deck" data-mode="scroll" style="inline-size:100%;background:none">
  <section class="ns-slide">
    <div class="ns-slide__inner">
      <div class="ns-slide__head">
        <span class="ns-kicker">Where your code runs</span>
        <h2 class="ns-slide__title">The order of execution, abridged</h2>
        <span class="ns-slide__rule"></span>
      </div>
      <div class="ns-slide__body">
        <div class="ns-slide__flow">
          <div class="ns-slide__node"><span class="ns-slide__node-label">01 · load</span><span class="ns-slide__node-title">Record loaded</span><span class="ns-slide__node-note">From the DB, or initialised for an insert.</span></div>
          <i class="ph ph-arrow-right ns-slide__arrow" aria-hidden="true"></i>
          <div class="ns-slide__node" aria-current="true"><span class="ns-slide__node-label">02 · before</span><span class="ns-slide__node-title">Before triggers</span><span class="ns-slide__node-note">You are here. No DML needed to change the record.</span></div>
          <i class="ph ph-arrow-right ns-slide__arrow" aria-hidden="true"></i>
          <div class="ns-slide__node"><span class="ns-slide__node-label">03 · rules</span><span class="ns-slide__node-title">Validation rules</span><span class="ns-slide__node-note">Which is why a before trigger can fix data a rule would reject.</span></div>
          <i class="ph ph-arrow-right ns-slide__arrow" aria-hidden="true"></i>
          <div class="ns-slide__node"><span class="ns-slide__node-label">04 · after</span><span class="ns-slide__node-title">After triggers</span><span class="ns-slide__node-note">The record has an Id.</span></div>
        </div>
      </div>
      <div class="ns-slide__foot"><span class="ns-slide__where">Module 02</span><span class="ns-slide__num">12 / 25</span></div>
    </div>
  </section>
</div>

Hands on, and the check

Two slides teaching needs and no general presentation tool ships: an exercise with a countdown THE ROOM can see — so it ends because time ran out rather than because the presenter got bored — and a question whose options are LABELLED, because a room answering out loud needs a handle and “the third one” is not one. The timer runs past zero on purpose; an exercise four minutes over is a fact worth showing, and a clock frozen at 00:00 hides it. The answer is a <details>, so opening it in the room also puts it in the handout. Live — press the timer, open the answer.

Hands on
Your turn

Build the router in your own org

  1. 01Create CaseRouter.cls
  2. 02Wire it to a before-insert trigger
  3. 03Insert 200 cases and read the log

Time remaining — click to start

Module 0416 / 25
Quick check
Everyone answer

A user imports 500 cases. How many times does your trigger run?

AOnce — one import, one transaction
BThree times — 200, 200, 100
C500 times — once per record
Show the answer

B. A bulk load is chunked into batches of 200, and each batch is its own transaction with its own governor limits — which is why “it worked on my one test record” proves nothing.

Check17 / 25
markup
<div class="ns-deck" data-mode="scroll" style="inline-size:100%;background:none">
  <section class="ns-slide ns-slide--split">
    <span class="ns-slide__badge"><i class="ph ph-barbell" aria-hidden="true"></i> Hands on</span>
    <div class="ns-slide__inner">
      <div class="ns-slide__head"><span class="ns-kicker">Your turn</span><h2 class="ns-slide__title">Build the router in your own org</h2></div>
      <div class="ns-slide__body">
        <div class="ns-slide__col">
          <ol class="ns-slide__points ns-slide__points--tight">
            <li class="ns-slide__point"><span class="ns-slide__point-index">01</span><span class="ns-slide__point-title">Create <code class="ns-code-inline">CaseRouter.cls</code></span></li>
            <li class="ns-slide__point"><span class="ns-slide__point-index">02</span><span class="ns-slide__point-title">Wire it to a before-insert trigger</span></li>
            <li class="ns-slide__point"><span class="ns-slide__point-index">03</span><span class="ns-slide__point-title">Insert 200 cases and read the log</span></li>
          </ol>
        </div>
        <div class="ns-slide__col">
          <p class="ns-slide__caption">Time remaining — click to start</p>
          <button type="button" class="ns-slide__timer" data-deck-timer="1500" data-state="idle" aria-label="Exercise timer, 25 minutes. Press to start."><span data-deck-timer-value>25:00</span><span class="ns-slide__timer-unit">min</span></button>
        </div>
      </div>
      <div class="ns-slide__foot"><span class="ns-slide__where">Module 04</span><span class="ns-slide__num">16 / 25</span></div>
    </div>
  </section>
  <section class="ns-slide">
    <span class="ns-slide__badge"><i class="ph ph-question" aria-hidden="true"></i> Quick check</span>
    <div class="ns-slide__inner">
      <div class="ns-slide__head"><span class="ns-kicker">Everyone answer</span><h2 class="ns-slide__title">A user imports 500 cases. How many times does your trigger run?</h2></div>
      <div class="ns-slide__body">
        <div class="ns-slide__options">
          <div class="ns-slide__option"><span class="ns-slide__option-key">A</span><span>Once — one import, one transaction</span></div>
          <div class="ns-slide__option" data-state="correct"><span class="ns-slide__option-key">B</span><span>Three times — 200, 200, 100</span></div>
          <div class="ns-slide__option"><span class="ns-slide__option-key">C</span><span>500 times — once per record</span></div>
        </div>
        <details class="ns-slide__answer">
          <summary><i class="ph ph-caret-right" aria-hidden="true"></i> Show the answer</summary>
          <p><b>B.</b> A bulk load is chunked into batches of 200, and each batch is its own transaction with its own governor limits — which is why “it worked on my one test record” proves nothing.</p>
        </details>
      </div>
      <div class="ns-slide__foot"><span class="ns-slide__where">Check</span><span class="ns-slide__num">17 / 25</span></div>
    </div>
  </section>
</div>

The presenter chrome

All of it lives OUTSIDE the slides, so none of it appears in a printed handout, a PDF export or an overview thumbnail. The count and the 2px progress hairline are the same mono/rail pair the course bar uses for a course — here for a talk. The theme switch is the system's own: a deck is not a place for a second theme control.

Apex fundamentals · module 01 06 / 25
markup
<div style="inline-size:100%;border:1px solid var(--color-border);border-radius:var(--radius-card);overflow:hidden">
  <div class="ns-deck__rail" aria-hidden="true"><span style="--p:24%"></span></div>
  <div class="ns-deck__bar">
    <span class="ns-deck__deckname"><i class="ph ph-presentation-chart" aria-hidden="true"></i> Apex fundamentals · module 01</span>
    <span class="ns-deck__count"><b>06</b> / 25</span>
    <div class="ns-deck__tools">
      <button type="button" class="ns-navicon" aria-label="Previous slide"><i class="ph ph-arrow-left" aria-hidden="true"></i></button>
      <button type="button" class="ns-navicon" aria-label="Next slide"><i class="ph ph-arrow-right" aria-hidden="true"></i></button>
      <button type="button" class="ns-navicon" aria-label="All slides"><i class="ph ph-squares-four" aria-hidden="true"></i></button>
      <button type="button" class="ns-navicon" aria-label="Speaker notes"><i class="ph ph-note" aria-hidden="true"></i></button>
      <button type="button" class="ns-navicon" aria-label="Switch between presenting and the handout"><i class="ph ph-rows" aria-hidden="true"></i></button>
      <button type="button" class="ns-navicon" aria-label="Full screen"><i class="ph ph-projector-screen" aria-hidden="true"></i></button>
      <button type="button" class="ns-navicon" aria-label="Keyboard shortcuts"><i class="ph ph-question" aria-hidden="true"></i></button>
    </div>
  </div>
</div>

Keyboard

The set a presenter's clicker already sends, plus the panels. Advance reveals the next fragment before it moves to the next slide, and going BACK lands on a slide with everything already revealed — re-walking six fragments to reach the previous slide is how a presenter loses the room while pressing left arrow nine times.

Next, or reveal the next pointorSpace
Back
Go to slide 1212
All slidesG
Speaker notesN
Full screenF
Blackout — look at me, not the wallB
Close anythingEsc
markup
<div class="ns-deck__help-card" style="inline-size:min(28rem,100%)">
  <div class="ns-deck__help-row"><span>Next, or reveal the next point</span><span class="ns-kbd-seq"><kbd class="ns-kbd">→</kbd><span class="ns-kbd-seq__sep">or</span><kbd class="ns-kbd">Space</kbd></span></div>
  <div class="ns-deck__help-row"><span>Back</span><span class="ns-kbd-seq"><kbd class="ns-kbd">←</kbd></span></div>
  <div class="ns-deck__help-row"><span>Go to slide 12</span><span class="ns-kbd-seq"><kbd class="ns-kbd">1</kbd><kbd class="ns-kbd">2</kbd></span></div>
  <div class="ns-deck__help-row"><span>All slides</span><span class="ns-kbd-seq"><kbd class="ns-kbd">G</kbd></span></div>
  <div class="ns-deck__help-row"><span>Speaker notes</span><span class="ns-kbd-seq"><kbd class="ns-kbd">N</kbd></span></div>
  <div class="ns-deck__help-row"><span>Full screen</span><span class="ns-kbd-seq"><kbd class="ns-kbd">F</kbd></span></div>
  <div class="ns-deck__help-row"><span>Blackout — look at me, not the wall</span><span class="ns-kbd-seq"><kbd class="ns-kbd">B</kbd></span></div>
  <div class="ns-deck__help-row"><span>Close anything</span><span class="ns-kbd-seq"><kbd class="ns-kbd">Esc</kbd></span></div>
</div>

The whole deck

Twenty-five slides, each a DIFFERENT shape, in the order a real course session uses them: open → agenda → teach → show → do → check → close. It is a pattern library rather than a talk — delete the ones you do not need and duplicate the ones you do. Press G for the overview, N for the notes, and the bar's rows icon to switch to the handout.

markup
<p><a class="ns-btn ns-btn--primary ns-btn--sm" href="./demo-deck.html" target="_blank" rel="noopener">Present it <i class="ph ph-arrow-up-right" aria-hidden="true"></i></a></p>

Accessibility contract

  • The current slide carries aria-current — the state the room sees and the state a screen reader hears are the same attribute, and the CSS hides the rest from that one fact
  • → ↓ Space PgDn advance, ← ↑ PgUp go back, Home/End jump, G overview, N notes, F fullscreen, B blackout, ? shortcuts, Esc closes. Typing in a field is never intercepted
  • Fragments dim rather than unmount, so nothing reflows on reveal and nothing is missing from the accessibility tree
  • Overview thumbnails are inert clones — forty cloned slides full of links would otherwise be forty extra tab stops behind the deck
  • Diagrams are boxes of real text, never a PNG: a screen reader reads the architecture slide and a translator translates it
  • With no JS every slide is on the page and every fragment is visible — the handout, which is also what prints