Theme
On this page (7)
Component Skill — components/comment-item/component-skill.md

Native basis

An <article id>: Avatar, author link (+ a Badge for roles), a <time> that links to the comment's own id, the text, actions (Buttons), and replies nested in an <ol> on a thread line. :target lights up the comment a permalink opens.

Built from

Avatar, Badge, Button, Dropdown Menu, Code Mockup.

Web Platform APIs

:target<time>scroll-marginoverflow-wrap: anywhere

Classes

.mk-comment-item.mk-comment-item-main.mk-comment-item-head.mk-comment-item-author.mk-comment-item-time.mk-comment-item-body.mk-comment-item-actions.mk-comment-item-replies.mk-comment-item-menu.mk-comment-item-reactions.mk-comment-item-votes.mk-comment-item-thread

Data attributes

data-variant (compact, deleted), data-highlighted; :target highlights by permalink; data-variant (voted, accepted); .mk-comment-item-reactions, .mk-comment-item-menu, .mk-comment-item-votes, .mk-comment-item-thread (collapsible replies).

§Thread

A comment with two replies on a thread line. The timestamp is a permalink to the comment's own id; the author's reply carries a Badge.

§Highlighted

The comment a permalink opened lights up via :target - or mark it with data-highlighted (here). The flash is off under reduced motion.

§Compact and deleted

data-variant='compact' tightens a thread (small avatar, 14px); data-variant='deleted' keeps the place of a removed comment.

§Reactions, menu and pinned

A pinned comment (a Badge) with reaction chips - toggle buttons with aria-pressed, click one (the page script counts) - and a ⋯ Dropdown Menu at the end of the head.

§Q&A: votes and the accepted answer

data-variant='voted' puts a score column before the avatar; data-variant='accepted' marks the answer - with a Code Mockup in the comment text.

§Collapsed replies

Replies inside a native <details class="mk-comment-item-thread"> - "Show 2 replies" opens them, no script.

§CSS view file

/* -- Comment Item (website) -----------------------------------------
   One comment: avatar, author (+ a role Badge), the timestamp (a permalink
   to the comment's own id), the text and its actions (reply, like) - and
   its replies, nested on a thread line. A comment reached by its permalink
   (:target) or marked data-highlighted lights up. */
@layer components {
  .mk-comment-item {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: 0 0.75rem;
    padding-block: 0.875rem;
    scroll-margin-block-start: 5rem;
    & > .avatar { grid-row: span 2; }
  }
  .mk-comment-item-main {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
    min-width: 0;
  }
  .mk-comment-item-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem 0.5rem;
    font-size: 0.875rem;
  }
  .mk-comment-item-author {
    color: var(--foreground);
    font-weight: 600;
    text-decoration: none;
    &:is(a):hover { text-decoration: underline; text-underline-offset: 0.2em; }
    &:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; border-radius: var(--radius-sm); }
  }
  .mk-comment-item-time {
    color: var(--muted-foreground);
    font-size: 0.8125rem;
    text-decoration: none;
    &:hover { text-decoration: underline; text-underline-offset: 0.2em; }
    &:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; border-radius: var(--radius-sm); }
  }
  .mk-comment-item-body {
    font-size: 0.9375rem;
    line-height: 1.6;
    overflow-wrap: anywhere;
    & > * { margin: 0; }
    & > * + * { margin-block-start: 0.5em; }
    & a { color: var(--primary); text-underline-offset: 0.2em; }
    & blockquote {
      padding-inline-start: 0.75em;
      border-inline-start: 2px solid var(--border);
      color: var(--muted-foreground);
    }
  }
  .mk-comment-item-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.125rem;
    margin-inline-start: -0.5rem;
    color: var(--muted-foreground);
  }
  /* replies: nested comments on a thread line */
  .mk-comment-item-replies {
    grid-column: 2;
    margin: 0.25rem 0 0;
    padding: 0 0 0 1rem;
    border-inline-start: 2px solid var(--border);
    list-style: none;
    & > li > .mk-comment-item:last-child { padding-block-end: 0; }
  }
  /* reached by its permalink, or marked */
  .mk-comment-item:target,
  .mk-comment-item[data-highlighted] {
    margin-inline: -0.75rem;
    padding-inline: 0.75rem;
    border-radius: var(--radius-lg);
    background: color-mix(in oklch, var(--primary) 8%, transparent);
    animation: mk-comment-item-flash 1.6s ease-out;
  }
  /* compact: a tighter thread */
  .mk-comment-item[data-variant="compact"] {
    padding-block: 0.5rem;
    & .mk-comment-item-body { font-size: 0.875rem; }
    & .mk-comment-item-head { font-size: 0.8125rem; }
  }
  /* deleted: the text is gone, the thread stays */
  .mk-comment-item[data-variant="deleted"] .mk-comment-item-body {
    color: var(--muted-foreground);
    font-style: italic;
  }
  /* the ⋯ menu (a Dropdown trigger) at the end of the head */
  .mk-comment-item-menu { margin-inline-start: auto; }
  /* reactions: emoji chips - toggle buttons (aria-pressed), an add button */
  .mk-comment-item-reactions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
    margin-block-start: 0.125rem;
    & button {
      display: inline-flex;
      align-items: center;
      gap: 0.25rem;
      height: 1.625rem;
      padding: 0 0.5rem;
      border: 1px solid var(--border);
      border-radius: 999px;
      background: transparent;
      color: var(--muted-foreground);
      font: inherit;
      font-size: 0.75rem;
      font-variant-numeric: tabular-nums;
      cursor: pointer;
      transition: background-color 120ms, border-color 120ms, color 120ms;
      &:hover { background: var(--accent); color: var(--accent-foreground); }
      &[aria-pressed="true"] { border-color: color-mix(in oklch, var(--primary) 45%, transparent); background: color-mix(in oklch, var(--primary) 10%, transparent); color: var(--foreground); }
      &:focus-visible { outline: 2px solid var(--ring); outline-offset: 1px; }
      & svg { width: 0.875rem; height: 0.875rem; }
    }
  }
  /* voted: a score column (up / count / down) instead of the avatar column */
  .mk-comment-item-votes {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.125rem;
    grid-row: span 2;
    color: var(--muted-foreground);
    font-size: 0.8125rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    & output { color: var(--foreground); }
    & button[aria-pressed="true"] { color: var(--primary); }
  }
  .mk-comment-item[data-variant="voted"] {
    grid-template-columns: auto auto minmax(0, 1fr);
    & > .avatar { grid-row: auto; align-self: start; }
    & > .mk-comment-item-replies { grid-column: 3; }
  }
  /* accepted: the answer - a green rail and a check badge */
  .mk-comment-item[data-variant="accepted"] {
    padding-inline: 0.875rem;
    margin-inline: -0.875rem;
    border-inline-start: 3px solid oklch(0.62 0.15 150);
    border-radius: var(--radius-md);
    background: color-mix(in oklch, oklch(0.62 0.15 150) 7%, transparent);
  }
  /* collapsible replies: a native <details> - "Show 3 replies" */
  .mk-comment-item-thread {
    grid-column: 2;
    & > summary {
      display: inline-flex;
      align-items: center;
      gap: 0.375rem;
      margin-block-start: 0.375rem;
      padding: 0.25rem 0.5rem;
      margin-inline-start: -0.5rem;
      border-radius: var(--radius-sm);
      color: var(--primary);
      font-size: 0.8125rem;
      font-weight: 600;
      cursor: pointer;
      list-style: none;
      &::-webkit-details-marker { display: none; }
      &::before { content: ""; width: 0.375rem; height: 0.375rem; border-inline-end: 1.5px solid currentColor; border-block-end: 1.5px solid currentColor; rotate: -45deg; transition: rotate 150ms; }
      &:hover { background: var(--accent); }
      &:focus-visible { outline: 2px solid var(--ring); outline-offset: 1px; }
    }
    &[open] > summary::before { rotate: 45deg; }
    & > .mk-comment-item-replies { grid-column: auto; }
  }
  @keyframes mk-comment-item-flash {
    from { background: color-mix(in oklch, var(--primary) 22%, transparent); }
  }
  /* -- Accessibility ------------------------------------------- */
  @media (prefers-reduced-motion: reduce) {
    .mk-comment-item:target,
    .mk-comment-item[data-highlighted] { animation: none; }
    .mk-comment-item-reactions button,
    .mk-comment-item-thread > summary::before { transition: none; }
  }
  @media (prefers-contrast: more) {
    .mk-comment-item-time,
    .mk-comment-item-actions { color: var(--foreground); }
    .mk-comment-item-replies { border-inline-start-color: var(--foreground); }
  }
  @media (forced-colors: active) {
    .mk-comment-item:target,
    .mk-comment-item[data-highlighted] { outline: 2px solid Highlight; }
  }
}

Comments, ideas or improvements? Edit this page's source on GitHub