NS Design System
v3.0.0 · 257 tokens

111Assistant shell

The room the assistant lives in: conversation rail, thread, docked composer, one locked viewport. The page behind it does not scroll — a chat where the document scrolls is a chat whose composer walks off the screen mid-sentence.

Use it for

  • The assistant's own route — the full-screen learning tutor
  • --docked under the product navbar, so the student can still navigate
  • --embedded for a chat panel inside a lesson, where the page owns the scroll

Not for

  • A support ticket thread — that is Ticket thread, and it is a record with a status, not a conversation
  • A comment section — a chat shell implies a reply is coming

Full screen

The whole screen at once — rail, thread, composer, and every state a real assistant has. This is the visual test for the family.

markup
<p><a class="ns-btn ns-btn--outline ns-btn--sm" href="./demo-ai-chat.html" target="_blank" rel="noopener">Open the full-screen demo <i class="ph ph-arrow-up-right" aria-hidden="true"></i></a></p>

Embedded

The same shell with the viewport lock dropped, for a panel beside a lesson. The scroll region caps at 60vh and the surrounding page keeps its own scroll.

Ask about this lesson Apex basics · 03
You

What does "bulkified" actually mean here?

Assistant

It means the code handles 200 records in one call as cheaply as it handles one — no query and no DML inside the loop.

markup
<div class="ns-ai ns-ai--embedded">
  <div class="ns-ai__main">
    <header class="ns-ai__bar">
      <span class="ns-ai__bar-title">Ask about this lesson</span>
      <span class="ns-ai__bar-meta">Apex basics · 03</span>
    </header>
    <div class="ns-ai__scroll">
      <div class="ns-ai__inner">
        <article class="ns-aiturn ns-aiturn--user">
          <header class="ns-aiturn__head"><span class="ns-avatar ns-avatar--sm" aria-hidden="true">RS</span>You</header>
          <div class="ns-aiturn__body"><p>What does "bulkified" actually mean here?</p></div>
        </article>
        <article class="ns-aiturn ns-aiturn--agent">
          <header class="ns-aiturn__head"><span class="ns-aiturn__mark" aria-hidden="true"><i class="ph ph-sparkle"></i></span>Assistant</header>
          <div class="ns-aiturn__body"><p>It means the code handles 200 records in one call as cheaply as it handles one — no query and no DML inside the loop.</p></div>
        </article>
      </div>
    </div>
    <div class="ns-ai__foot">
      <form class="ns-aicomposer">
        <label class="ns-visually-hidden" for="ai-embed-in">Ask about this lesson</label>
        <textarea class="ns-aicomposer__area" id="ai-embed-in" rows="1" placeholder="Ask about this lesson…"></textarea>
        <div class="ns-aicomposer__bar">
          <span class="ns-aicomposer__spacer"></span>
          <button type="submit" class="ns-btn ns-btn--primary ns-btn--icon ns-btn--sm" aria-label="Send"><i class="ph ph-paper-plane-tilt" aria-hidden="true"></i></button>
        </div>
      </form>
    </div>
  </div>
</div>

Top bar

The conversation's identity plus the course it belongs to. The course is the mono half: a student's history is mostly “the one from the Apex module”, so the anchor matters as much as the question.

Why does my trigger hit governor limits? Apex basics · lesson 03
markup
<header class="ns-ai__bar" style="border:1px solid var(--color-border);border-radius:var(--radius-card)">
  <button type="button" class="ns-btn ns-btn--quiet ns-btn--icon ns-btn--sm" aria-label="Toggle conversations" aria-expanded="true"><i class="ph ph-sidebar" aria-hidden="true"></i></button>
  <span class="ns-ai__bar-title">Why does my trigger hit governor limits?</span>
  <span class="ns-ai__bar-meta">Apex basics · lesson 03</span>
  <span class="ns-ai__bar-actions">
    <button type="button" class="ns-btn ns-btn--quiet ns-btn--icon ns-btn--sm" aria-label="Share this chat"><i class="ph ph-share-network" aria-hidden="true"></i></button>
    <button type="button" class="ns-btn ns-btn--quiet ns-btn--icon ns-btn--sm" aria-label="Assistant settings"><i class="ph ph-gear" aria-hidden="true"></i></button>
  </span>
</header>

The standing note

Present on every assistant screen, never dismissible. An LMS assistant talks to people who cannot yet tell when it is wrong — which is the entire reason they are here.

The assistant can be wrong. Check anything it says against the lesson it cites.

markup
<p class="ns-ai__note">The assistant can be wrong. Check anything it says against the lesson it cites.</p>

Accessibility contract

  • The rail is an <aside aria-label>; the toggle carries aria-controls and aria-expanded, so the announced state cannot drift from the CSS collapse
  • Below 64rem the rail is an overlay and Escape closes it — a sheet you can only dismiss with a small button is a trap on a phone
  • The transcript is a list of <article>s, so a screen reader can move turn by turn