Document cards have a thumbnail, then a footer with a title row (title on the left, comment count on the right) and a meta row (“Kiet · Edited 18s ago”). Today, hovering anywhere on the card opens a floating panel over the thumbnail showing the newest reply, centered with no relationship to the count.
Change this so the count is the trigger and the panel is anchored to it. Keep the existing visual tokens (colors, radii, type scale, popover shadow) and component conventions. Don’t introduce new tokens unless something below has no equivalent.
The behavior, live
Rest the pointer on a count · Tab reaches it, Esc closes · on touch, first tap opens and the next follows the link
Implements the timings and layout below, including keyboard, touch, and prefers-reduced-motion. Links show a toast here instead of navigating.
01Trigger
Hovering the card no longer opens the panel. Card hover only puts the count into its chip state (light rounded background, darker text), the same state it already shows on direct hover.
The count opens the panel on hover, keyboard focus, or tap.
Hover uses a 100ms intent delay before opening. Leaving the count starts a 200ms grace timer so the pointer can travel onto the panel; entering the panel cancels the close, leaving the panel or the card closes it.
The count stays in its chip state while the panel is open.
Clicking the count navigates to the document’s comment thread. On touch devices (hover: none), the first tap opens the panel and a tap on the open panel navigates.
Esc closes the panel and returns focus to the count. Clicking or tapping outside the card closes it.
02Placement
Position the panel absolutely inside the card. Right edge aligned to the count chip’s right edge (the footer’s horizontal padding). Bottom edge docked to the seam between thumbnail and footer, with no gap, so nothing from the thumbnail peeks out beneath it. It should read as coming from the “5”.
Width 300px. No caret. Radius 10px, white surface, the existing popover shadow plus a 1px hairline ring.
The card never changes size. No layout shift on open or close.
03Content
Header row: 18px avatar, then “FirstName · 2h” as a single span in the secondary text color at 13px. No space-between layout and no separate timestamp on the right.
Body: the comment text, 15px/20px, primary text color, 8px below the header. Clamp to two lines with an ellipsis. Panel padding 12px 14px 14px.
The entire panel is a link to the thread, scrolled to that comment.
Which comment to show: the newest reply not written by the viewer. Prefer an unread one if any exist; prefer one that @mentions the viewer above that. Skip resolved threads. If the newest eligible comment is image-only, show “FirstName attached an image”. If there is no eligible comment, the count doesn’t open a panel.
Hide the count entirely when a document has zero comments.
04Unread state
When the thread has replies the viewer hasn’t seen: number in the primary text color instead of secondary, plus a 6px dot in the product’s accent color at the top-right of the bubble icon, with a 1.5px ring in the card background color so it separates from the icon stroke.
Read state: number in the secondary color, no dot.
05Motion
Open: fade in and rise 4px over 140ms, ease-out. Close: the reverse.
Under prefers-reduced-motion: reduce, no transform and no transition.
06Semantics and accessibility
Count: an anchor to the thread with aria-label of “{n} comments” or “{n} comments, new replies”, and aria-describedby pointing at the panel so screen readers get the preview on focus.
Panel: an anchor to the specific comment. Keep it in the DOM when closed (hidden with opacity and pointer-events: none, not display: none) so the description resolves. Focusing the panel link also opens it.
Don’t place the panel inside any aria-hidden container (the decorative thumbnail is aria-hidden; the panel must be a sibling of it, not a child).
Visible focus rings on both the count and the panel.
07Done when
Ticks are saved only in this browser — use them while verifying an implementation.