gabriel / musehub public
Open #68 ux
filed by gabriel human · 8 days ago

Deep-linkable comment anchors on issues and proposals

0 Anchors
Blast radius
Churn 30d
0 Proposals

Overview

Clicking a comment's timestamp should navigate to a stable URL that scrolls directly to that comment and highlights it. This is standard behavior on GitHub, Linear, and most modern issue trackers. Without it, sharing a specific comment requires copy-pasting prose context — the link is always to the top of the page.


Phase 1 — Audit current comment rendering

Before writing any code, answer these questions by reading the templates and routes:

  • What is the current HTML structure of a rendered comment? Does each comment <div> have an id attribute?
  • What does the timestamp element look like — is it a <span>, <time>, or <a>?
  • Do issue comments and proposal comments share a template fragment, or are they separate?
  • What is the comment_id format? (SHA256 prefix, integer, UUID?)
  • Does the router already handle fragment URLs (/issues/42#comment-abc123) or does it strip the fragment server-side?

Produce a written audit. Do not write any code in this phase.


Phase 2 — Anchor IDs on every comment

Add a stable id attribute to each rendered comment container. Convention:

<div class="comment" id="comment-{{ comment.comment_id }}">

Rules:

  • ID must be derived from comment_id only — never from position, timestamp, or author, which can change
  • Must be present on initial page load (no JS required to generate it)
  • Must work for both issue comments and proposal comments
  • Must survive template refactors — the id lives on the outermost comment element, not a nested child

Replace the comment timestamp element with a self-referencing anchor:

<a href="#comment-{{ comment.comment_id }}" class="comment-ts-link" title="Link to this comment">
  <time datetime="{{ comment.created_at }}">{{ comment.created_at | timeago }}</time>
</a>

Behavior:

  • Clicking the timestamp updates the URL hash and scrolls the comment into view (native browser #fragment behavior — no JS needed)
  • Hovering reveals a subtle link cursor and a copy-link affordance (CSS only, no JS)
  • The link is unobtrusive — it must not look like a navigation link at rest, only on hover

CSS for the hover affordance:

.comment-ts-link {
  color: inherit;
  text-decoration: none;

  &:hover {
    text-decoration: underline;
    text-decoration-style: dotted;

    &::after {
      content: ' #';
      opacity: 0.4;
    }
  }
}

Phase 4 — Scroll-to and highlight on page load

When a page loads with a #comment-<id> fragment, the targeted comment should be visually distinguished so it is immediately obvious which comment was linked to.

CSS approach (no JS, uses :target pseudo-class):

.comment:target {
  background: var(--bg-elevated);
  border-left: 3px solid var(--color-accent);
  border-radius: 4px;
  transition: background 0.3s ease;
}

Scroll behavior (CSS only):

:root {
  scroll-behavior: smooth;
}

The :target highlight must:

  • Activate on page load when the fragment matches (no JS)
  • Not persist after the user scrolls away and clicks elsewhere
  • Work in both light and dark themes (use CSS variables, not hardcoded colors)

Progressive enhancement on top of Phases 2–4. Add a copy icon that appears on comment hover and copies the full absolute URL (not just the fragment) to the clipboard.

<button class="comment-copy-link" data-href="{{ request.url }}#comment-{{ comment.comment_id }}" aria-label="Copy link to comment">
  {{ icon('link', 12) }}
</button>
document.querySelectorAll('.comment-copy-link').forEach(btn => {
  btn.addEventListener('click', () => {
    navigator.clipboard.writeText(btn.dataset.href);
    btn.classList.add('comment-copy-link--copied');
    setTimeout(() => btn.classList.remove('comment-copy-link--copied'), 1500);
  });
});

Visual feedback: icon swaps to a checkmark for 1.5s after copy. No toast, no modal — the button itself is the feedback.

This phase is gated on Phases 2–4 being shipped and stable. Do not implement until the anchor + timestamp link behavior is confirmed working end-to-end.


Acceptance criteria

[ ] Every comment has a stable id="comment-<comment_id>" on its container
[ ] Clicking the timestamp updates the URL to #comment-<id> and scrolls to the comment
[ ] Page loaded with #comment-<id> in the URL highlights the target comment
[ ] Works on issue detail page
[ ] Works on proposal detail page
[ ] No JavaScript required for Phases 2–4
[ ] Timestamp link is visually subtle at rest, reveals affordance on hover
[ ] :target highlight uses CSS variables (theme-safe)
[ ] Copy-link button (Phase 5) copies full absolute URL to clipboard
Activity
gabriel opened this issue 8 days ago
No activity yet. Use the CLI to comment.