111Comments
The thread under a post. The hard part is not the list — it is restraint: comments are user-generated, arrive at any length, and will out-scroll the article if the page lets them. So there are no cards, no shadows and no per-comment borders, just a hairline between siblings and one level of indentation for a reply.
Use it for
- Discussion under a post or a lesson
- --op to mark the author answering, so a reader can find the authoritative reply
Not for
- Threading deeper than one level. A reply to a reply is a conversation two people are having while everyone watches — it belongs in the reply, addressed by name, and on a 360px screen a third level has nowhere to go but a 40-character column
- Vote scores, awards, share-this-comment. Engagement machinery, not reading
- Making the section look heavier than the article. That says the comments matter more, and they do not
The thread
One level of replies, enforced in CSS: a third level flattens back to the second rather than indenting again.
markup
<section class="ns-comments" style="max-inline-size:42rem;inline-size:100%">
<div class="ns-comments__head">
<h2 class="ns-comments__count"><b>24</b> comments</h2>
<label class="ns-commentform__hint">Sort
<select class="ns-select" style="margin-inline-start:var(--space-2)">
<option>Newest</option><option>Oldest</option><option>Most liked</option>
</select>
</label>
</div>
<div class="ns-comments__list">
<article class="ns-comment ns-comment--pinned">
<span class="ns-comment__avatar ns-ph ns-ph--sm" aria-hidden="true"></span>
<div class="ns-comment__main">
<div class="ns-comment__head">
<a class="ns-comment__author" href="#0">Priya Nair</a>
<span class="ns-comment__badge">moderator</span>
<span class="ns-comment__badge"><i class="ph ph-bookmark-simple" aria-hidden="true"></i> pinned</span>
<time class="ns-comment__time" datetime="2026-08-14">3 days ago</time>
</div>
<div class="ns-comment__text"><p>Worth adding: the 101-query error usually shows up in a test long before production, because tests run with a fresh limit counter per method. If your test passes with one record and fails with 200, this is why.</p></div>
<div class="ns-comment__actions">
<button type="button" class="ns-btn ns-btn--quiet ns-btn--xs" aria-pressed="false" aria-label="Like, 12 likes"><i class="ph ph-heart" aria-hidden="true"></i> 12</button>
<button type="button" class="ns-btn ns-btn--quiet ns-btn--xs">Reply</button>
</div>
</div>
</article>
<article class="ns-comment">
<span class="ns-comment__avatar ns-ph ns-ph--sm" aria-hidden="true"></span>
<div class="ns-comment__main">
<div class="ns-comment__head">
<a class="ns-comment__author" href="#0">dev_arun</a>
<time class="ns-comment__time" datetime="2026-08-15">2 days ago</time>
</div>
<div class="ns-comment__text"><p>Does a Flow element count against the same SOQL limit as an Apex query, or does it get its own budget?</p></div>
<div class="ns-comment__actions">
<button type="button" class="ns-btn ns-btn--quiet ns-btn--xs" aria-pressed="false" aria-label="Like, 3 likes"><i class="ph ph-heart" aria-hidden="true"></i> 3</button>
<button type="button" class="ns-btn ns-btn--quiet ns-btn--xs">Reply</button>
</div>
<div class="ns-comment__replies">
<article class="ns-comment ns-comment--op">
<span class="ns-comment__avatar ns-ph ns-ph--sm" aria-hidden="true"></span>
<div class="ns-comment__main">
<div class="ns-comment__head">
<a class="ns-comment__author" href="#0">Swarnil Singhai</a>
<span class="ns-comment__badge">author</span>
<time class="ns-comment__time" datetime="2026-08-15">2 days ago</time>
</div>
<div class="ns-comment__text"><p>Same budget. Everything in one transaction shares the 100, whichever tool spent it — which is exactly why a Flow looping over records hits it as fast as a trigger does.</p></div>
<div class="ns-comment__actions">
<button type="button" class="ns-btn ns-btn--quiet ns-btn--xs" aria-pressed="true" aria-label="Liked, 8 likes"><i class="ph ph-heart" aria-hidden="true"></i> 8</button>
<button type="button" class="ns-btn ns-btn--quiet ns-btn--xs">Reply</button>
</div>
</div>
</article>
<article class="ns-comment ns-comment--removed">
<span class="ns-comment__avatar ns-ph ns-ph--sm" aria-hidden="true"></span>
<div class="ns-comment__main">
<div class="ns-comment__head"><span class="ns-comment__author">Removed</span><time class="ns-comment__time" datetime="2026-08-16">1 day ago</time></div>
<div class="ns-comment__text"><p>This comment was removed for breaking the house rules.</p></div>
</div>
</article>
</div>
</div>
</article>
</div>
<button type="button" class="ns-btn ns-btn--outline ns-btn--block">Load 21 more comments</button>
</section>Leave a comment
Reuses the form layer wholesale — .ns-field, .ns-textarea, .ns-btn. There is no bespoke comment input, because a comment box is a textarea and pretending otherwise is how a design system grows a second form system.
markup
<form class="ns-commentform" style="max-inline-size:42rem;inline-size:100%">
<div class="ns-field">
<label class="ns-field__label" for="c-body">Leave a comment</label>
<textarea class="ns-textarea" id="c-body" rows="4" placeholder="Add to the discussion…"></textarea>
<span class="ns-field__help">Markdown is supported. Be useful or be brief.</span>
</div>
<div class="ns-commentform__row">
<span class="ns-commentform__hint">Posting as <strong>dev_arun</strong></span>
<button type="submit" class="ns-btn ns-btn--primary">Post comment</button>
</div>
</form>Signed out, closed, and empty
Three states that are not errors, so none of them is a warning: a hairline note for the first two, the system's own empty state for the third.
Sign in to join the discussion. Comments are open to anyone with an account — free or paid.
Sign inComments closed. This post is over a year old and the thread has stopped being useful — the replies are still here.
No comments yet
Be the first. Questions get answered here more reliably than anywhere else.
markup
<div style="display:grid;gap:var(--space-6);max-inline-size:42rem;inline-size:100%">
<div class="ns-comments__note">
<i class="ph ph-user-circle" aria-hidden="true"></i>
<p>Sign in to join the discussion. Comments are open to anyone with an account — free or paid.</p>
<a class="ns-btn ns-btn--outline ns-btn--sm" href="#0">Sign in</a>
</div>
<div class="ns-comments__note">
<i class="ph ph-lock-simple" aria-hidden="true"></i>
<p>Comments closed. This post is over a year old and the thread has stopped being useful — the replies are still here.</p>
</div>
<div class="ns-empty">
<i class="ns-empty__icon ph ph-chats-circle" aria-hidden="true"></i>
<p class="ns-empty__title">No comments yet</p>
<p class="ns-empty__text">Be the first. Questions get answered here more reliably than anywhere else.</p>
</div>
</div>Accessibility contract
- Each comment is an
<article>with a real<time datetime>, so “2 days ago” is also a machine-readable date - Every marker is a WORD — author, moderator, pinned — never a coloured ring or a bare rule, which is a private joke between the design and the people who already know what it means.
--pinned's brand edge agrees with the badge; it does not replace it - Replies are a nested list inside the comment they answer, so the structure survives with CSS off
- A removed comment leaves a tombstone rather than vanishing: a thread that silently loses its third reply reads as broken, and the replies that answered it are still there
- The like button carries
aria-pressedand the count is in the accessible name, so it is not announced as a bare number
24 comments
Worth adding: the 101-query error usually shows up in a test long before production, because tests run with a fresh limit counter per method. If your test passes with one record and fails with 200, this is why.
Does a Flow element count against the same SOQL limit as an Apex query, or does it get its own budget?
Same budget. Everything in one transaction shares the 100, whichever tool spent it — which is exactly why a Flow looping over records hits it as fast as a trigger does.
This comment was removed for breaking the house rules.