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.
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