NS Design System
v3.0.0 · 257 tokens

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.

24 comments

Priya Nair moderator pinned

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.

dev_arun

Does a Flow element count against the same SOQL limit as an Apex query, or does it get its own budget?

Swarnil Singhai author

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.

Removed

This comment was removed for breaking the house rules.

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.

Markdown is supported. Be useful or be brief.
Posting as dev_arun
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&hellip;"></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 in

Comments 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 &mdash; 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 &mdash; 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-pressed and the count is in the accessible name, so it is not announced as a bare number