NS Design System
v3.0.0 · 257 tokens

89Enrol card

The card that carries the one action the course page exists for: price, enrol, what is included, and the fine print under it. Sticky in the rail on desktop; a fixed bottom bar on a phone (--bar), because the action must never be scrolled past. The price block is deliberately two different sizes — a discount that renders as two equal numbers makes the reader do arithmetic to find out which one they pay.

Use it for

  • The course detail rail, sticky
  • --bar as the phone counterpart of the same card
  • A path or bundle page, with the bundle's price

Not for

  • Repeating every fact from the hero — it carries the DECISION, not the description
  • More than one primary button. "Enrol" and "Buy now" are the same action wearing two hats

In the rail

markup
<aside class="ns-buybox" style="max-inline-size:20rem">
  <span class="ns-buybox__media ns-ph" aria-hidden="true"></span>
  <div class="ns-buybox__body">
    <span class="ns-price">
      <span class="ns-price__now">$49</span>
      <s class="ns-price__was">$129</s>
      <span class="ns-price__off">62% off</span>
    </span>
    <div class="ns-buybox__actions">
      <a class="ns-btn ns-btn--primary ns-btn--lg ns-btn--block" href="#">Enrol now</a>
      <a class="ns-btn ns-btn--outline ns-btn--block" href="#"><i class="ph ph-play" aria-hidden="true"></i> Preview 2 lessons</a>
    </div>
    <p class="ns-buybox__note">30-day refund, no questions asked.</p>
    <ul class="ns-buybox__list">
      <li><i class="ph ph-video" aria-hidden="true"></i>6h 20m of video</li>
      <li><i class="ph ph-article" aria-hidden="true"></i>12 written lessons</li>
      <li><i class="ph ph-flask" aria-hidden="true"></i>5 hands-on labs</li>
      <li><i class="ph ph-arrow-down" aria-hidden="true"></i>Downloadable source</li>
      <li><i class="ph ph-infinity" aria-hidden="true"></i>Lifetime access</li>
      <li><i class="ph ph-medal" aria-hidden="true"></i>Certificate of completion</li>
    </ul>
    <div class="ns-share">
      <span class="ns-share__label">Share</span>
      <button type="button" class="ns-btn ns-btn--quiet ns-btn--icon ns-btn--sm" aria-label="Copy link"><i class="ph ph-link-simple" aria-hidden="true"></i></button>
      <button type="button" class="ns-btn ns-btn--quiet ns-btn--icon ns-btn--sm" aria-label="Share on LinkedIn"><i class="ph ph-linkedin-logo" aria-hidden="true"></i></button>
      <button type="button" class="ns-btn ns-btn--quiet ns-btn--icon ns-btn--sm" aria-label="Share on X"><i class="ph ph-x-logo" aria-hidden="true"></i></button>
    </div>
  </div>
  <p class="ns-buybox__foot">142 enrolled in the last 30 days</p>
</aside>

Free course

No price theatre. A free course says free once, in the success ink, and the button says what happens next.

markup
<aside class="ns-buybox" style="max-inline-size:20rem">
  <div class="ns-buybox__body">
    <span class="ns-price ns-price--free"><span class="ns-price__now">Free</span></span>
    <div class="ns-buybox__actions">
      <a class="ns-btn ns-btn--primary ns-btn--lg ns-btn--block" href="#">Start lesson 01</a>
    </div>
    <p class="ns-buybox__note">No card. Sign in to keep your progress.</p>
    <ul class="ns-buybox__list">
      <li><i class="ph ph-video" aria-hidden="true"></i>48m of video</li>
      <li><i class="ph ph-infinity" aria-hidden="true"></i>Yours forever</li>
    </ul>
  </div>
</aside>

Phone bar

The same card as a fixed bottom bar below 64rem. It is display:none above that breakpoint — the rail card is already there, and two enrol buttons on one screen is one too many.

page content
$49$129 Enrol now
markup
<div style="position:relative;block-size:6rem;border:1px dashed var(--color-border);border-radius:var(--radius-card);display:grid;place-items:center;color:var(--color-muted);font-size:var(--size-fine)">
  page content
  <div class="ns-buybox ns-buybox--bar" style="position:absolute;inset-inline:0;inset-block-end:0">
    <span class="ns-price ns-price--sm"><span class="ns-price__now">$49</span><s class="ns-price__was">$129</s></span>
    <a class="ns-btn ns-btn--primary" href="#">Enrol now</a>
  </div>
</div>

Accessibility contract

  • The old price is inside <s>, so it is announced as struck rather than read as the current price
  • The bar variant respects env(safe-area-inset-bottom) — without it the button lands under the home indicator
  • The includes list is a real <ul>; its icons are aria-hidden and each item reads as a sentence