What does "bulkified" actually mean here?
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
--dockedunder the product navbar, so the student can still navigate--embeddedfor 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.
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.
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