Comments
A shared presentation for ordinary proposal discussion and Chat replies.
The HTML contract
Use an article.comment.comment--layout as the stable outer item. Give it a unique, caller-owned id. The optional .comment-avatar comes first. Put the author, time, rich body, edited note, and footer inside .comment-content, in that reading order. Keep presentation utilities with the caller: use text-sm text-fg border-t pt-075 pb-2 on the outer item, text-muted text-xs for the timestamp and permalink, text-base mt-075 for the body, text-xs text-muted for edited metadata, and button button--small for action buttons when matching these existing examples. Preserve author-link styling such as font-bold text-fg underline too.
<article class="comment comment--layout text-sm text-fg border-t pt-075 pb-2" id="comment-42">
<a class="comment-avatar" href="/profiles/jane"
aria-label="Jane Doe's profile">
<img class="avatar avatar--lg avatar--square"
src="/jane.jpg" alt="">
</a>
<div class="comment-content">
<header class="comment-meta">
<span class="comment-author">
<a class="author font-bold text-fg underline" href="/profiles/jane">Jane Doe</a>
</span>
<a class="comment-permalink text-muted text-xs" href="#comment-42"
aria-label="Mar 15, link to comment 42">
<time class="comment-timestamp" datetime="2026-03-15T09:30:00-07:00">Mar 15</time>
</a>
</header>
<div class="comment-body prose text-base mt-075">
<p>Caller-rendered trusted rich content goes here.</p>
</div>
<p class="comment-edited text-xs text-muted">Edited
<time datetime="2026-03-15T09:40:00-07:00">Mar 15 at 9:40</time>
</p>
<footer class="comment-footer">
<ul class="comment-actions" aria-label="Comment actions">
<li><button class="button button--small" type="button">Reply</button></li>
</ul>
</footer>
</div>
</article>
Hook reference
.comment- Legacy selector retained. It does not gain new structural layout rules.
.comment--layout- Opt-in flex row for avatar and content. It owns the evidenced comment structure, not a chat bubble or card.
.comment-avatar- Optional avatar link or image wrapper. Use
alt=""when the visible author already names the person. A linked avatar needs an accessible link name. .comment-content- Flexible, shrinkable column containing metadata, body, edited text, and footer.
.comment-meta- Author and time row. Long values wrap instead of widening the page.
.comment-author- Visible author text, linked or unlinked. Keep profile links here separate from actions.
.comment-timestamp- Semantic
<time datetime>with caller-formatted visible text, whether or not it links. No locale or relative-time policy is imposed. .comment-permalink- Optional link to the comment's stable ID. It may wrap the timestamp; keep the link name meaningful and separate from action controls.
.comment-body- Caller-rendered paragraphs, lists, links, code, and media. Long links and inline code wrap; preformatted code scrolls within its own width; images and video stay within the body.
.comment-edited- Optional caller-owned edited text/time. The presentation does not infer edit eligibility, audit rules, or timestamps.
.comment-footer/.comment-actions- Optional caller-owned controls, separate from profile and permalink links. Use native links and buttons; the framework's normal visible focus remains active.
Time, links, and avatars
Supply a machine-readable datetime and the visible time text inside .comment-timestamp. The caller chooses the date format. An optional .comment-permalink may wrap the time; if it has an aria-label, start it with the exact visible time text, then identify the destination, for example “Mar 15, link to comment 42.” Keep action controls in the footer, outside that link.
When a visible author name already identifies the person, treat the avatar image as decorative with an empty alt. If the avatar links to a profile, name the link, such as “Jane Doe's profile.” An unlinked avatar remains decorative. A missing avatar is valid.
Rich content and ownership
The caller renders the body through its trusted content pipeline. The body may contain paragraphs, lists, links, code, figures, and images. Sanitization, allowed URL rules, protected-media authorization, and image alt text stay with that caller. This presentation never accepts raw HTML as a shortcut.
Do not add aria-live to a comment or its container. Chat owns arrival announcements and reading position; Governance owns proposal positions, removals, recorded decisions, outcome colors, history, and edit permissions.
Mapping the consumers
| Current source | Shared hooks | Retained by the domain owner |
|---|---|---|
Governance ordinary DiscussionComment: .comment wrapper, avatar/profile branch, author and time row, rich body | .comment--layout, optional .comment-avatar, .comment-content, .comment-meta, .comment-author, .comment-timestamp, optional .comment-permalink, .comment-body | Position labels/history, removed positions, outcome colors, explanatory text and edit permissions. Decision notes remain their existing specialized composition. |
Chat PostArticle: anchored article, accessible linked avatar, author/profile link, message permalink/time, rich body | The same shared hooks, with the existing post-ID as its stable outer anchor | Conversation order, pagination, reading position, moderation and permitted actions. Chat page header and reply composer remain unchanged. |
Live Wires Templ CommentComponent | New rich composition can target this documented DOM/CSS contract. | Keep Comment and CommentComponent output and behavior compatible: escaped plain body, existing DateShort formatting, avatar, badge, size/alignment classes and attributes. |
This is a producer contract for Templ issue #24. It does not implement that component or migrate either consumer.
For that Templ component, emit this structure from the caller's supplied values and slots. Render rich body, edited metadata, and action/footer content as Templ components; keep plain strings escaped. Do not add a raw-HTML escape hatch, Markdown parser, route or permission check.
Examples and checks
Open the rendered comment examples for the ordinary proposal and Chat cases, long values, optional avatar/profile link, rich content, actions, edited metadata, and multiple anchors.
Run the browser layout and semantic checks at desktop, 640px, 375px, and 320px.
Check the page at desktop width and at 375px and 320px. Use a 640px CSS viewport to check the reflow expected at 200% zoom from a 1280px baseline. Also check light and dark appearance, an inherited scheme, keyboard focus, no-JavaScript rendering, rich-content overflow, and media bounds. The DOM can verify semantic time, unique IDs, empty avatar alt text, named linked avatars, and that timestamp permalinks target existing IDs. Those checks do not replace testing with an actual screen reader.