Skip to content

feat(snapshot): designed share cards for the sharing initiative - #6544

Merged
idoshamun merged 23 commits into
mainfrom
snapshot-share-images
Sep 10, 2026
Merged

feat(snapshot): designed share cards for the sharing initiative#6544
idoshamun merged 23 commits into
mainfrom
snapshot-share-images

Conversation

@tomeredlich

@tomeredlich tomeredlich commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

The designed share cards for the sharing initiative, plus the Storybook pages that review them. Each card is a real React component at 1080 wide, rasterized by the capture pipeline #6556 shipped. The background gradient is seeded from the subject's id, so a given post or profile always produces the same image.

What is here

  • Cards in features/snapshot/: achievement, achievements, award, badges and awards, level up, discussion, tag / source / squad, invite, leaderboard, list (briefing, best-of), post, profile, reading overview, streak. SnapshotEyebrow, SnapshotIdentity, SnapshotStats and SnapshotLevelRing are their shared pieces.
  • Storybook under Features/Snapshot: every card, its edge cases, button placements per surface, and the sharing map.

What a reviewer should know first

No card is mounted in the product yet. This PR ships no control on any surface and no flag. The sibling PRs do the wiring: #6580 (profile), #6563 (briefing), #6570 (happening now).

It sits on #6556, which merged first. That PR landed newer copies of the primitives this branch introduced. Main's versions won every conflict: SnapshotButton (target plus useArmedCard staging, clipboard first, SharePost logging), captureShareImage, copyShareImage, snapshotText and SnapshotFrame. This branch's own card-based button and its useSnapshotCapture hook are gone, so the sibling PRs should mount cards the way TextSnapshotButton and PollSnapshotButton do.

HighlightTextSnapshotCard gained an optional label, rendered on the logo row. It is the one live component this PR touches. The post and discussion cards now render through it instead of carrying their own copy of its layout. Through snapshotSource, an unattributed post credits its domain rather than the literal "unknown".

Events

None. Nothing here renders in the product.

Experiment

None.

Testing

  • shared: 407 suites / 2947 tests. webapp: 86 / 685.
  • typecheck-strict-changed clean, eslint clean on shared, full webapp tsc clean.
  • Storybook tsc clean for the snapshot stories. Share images and Edge cases load with no errors.

Review links

Preview domain

https://snapshot-share-images.preview.app.daily.dev

@vercel

vercel Bot commented Aug 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
daily-webapp Ready Ready Preview Sep 10, 2026 12:04pm UTC
storybook Ready Ready Preview Sep 10, 2026 12:04pm UTC

Request Review

Storybook-only. Every surface in the sharing initiative gets a review
page, and the snapshot cards those pages render come with them.

- Fourteen card components under features/snapshot, drawn as twenty
  variations. Each is a real React component rasterized off-screen by
  snapdom, so the pages show what would ship. The background gradient is
  seeded from the subject's id, so a given post or profile always
  produces the same image.
- A page per surface under Features/Snapshot/Surfaces, plus Share images
  (every card), Button placements (where the control sits), the sharing
  map (which surfaces lead with a link and which with an image), the
  spec, and the edge cases each card degrades through.
- SnapshotButton and the imageShare utils, which the pages need to render
  and export a card.

No app surface is touched: nothing under components/cards, components/post,
components/profile, features/profile or webapp changes, and the review
pages draw the surrounding UI themselves rather than mounting the real
components. Placement is argued in Storybook here; wiring it into the app
belongs to the per-surface PRs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tomeredlich and others added 22 commits September 6, 2026 16:41
The CEO's note on surfaces 1, 1b, 2, 4 and 12 was that the export loses to
a plain screenshot, because a screenshot carries more text than we do.

Two causes. The square was one: every card was locked to 1080x1080, so a
title and a summary had to fit a fixed box and the clamps cut them. Frames
now take a `grow` flag that lets the height follow the content, floored at
the square and capped at 9:16, and the capture measures the mounted card
instead of assuming it. The five text surfaces drop their line clamps.

The other was the highlight card: it exported only the marked run, which
threw away the point the reader was making by marking it. It now takes the
passage plus the selection, windows the passage around the mark, and sets
the mark apart the way the page does.
Surface 1 exported a summary of a post; the reference the CEO shared was a
screenshot of the page itself. So the card now carries what the page shows —
the source with its Follow affordance, the headline beside its thumbnail,
the date/read time/domain line, the whole TLDR, the tags and the engagement
counts. The page's own controls are dropped: a still image has nothing to
press, so "Read post" and the overflow menu are noise.

At 780 the card could not hold that without setting the headline four lines
deep next to a stamp-sized thumbnail. Frames take a `wide` flag that trades
the gradient stage for a gradient border — 964 wide, tighter padding — which
is the room the article needed. Only the post card uses it for now.
Widening the card bought room and then left it as padding and gaps. The
reclaimed space goes to the article instead: the inner padding drops from 40
to 32, the block gaps tighten, and the headline, TLDR, meta and counts all
step up a size. The frame grows to absorb it, so nothing is cut.
The summary is the part worth sending, so it goes first and in white at
headline scale, and the rest of the card credits it: title, source, date,
under a rule. The page furniture comes off — thumbnail, follow, tags,
upvote/comment/impression counts, read time and domain. It competed with
the copy for the room we had just freed up, and a still image has nothing
to press.
Bold at that size read as a headline, which put it in competition with the
actual headline below it.
1008 instead of 964, so the purple reads as a border on the black box
rather than a stage it sits on. The frame pads by the same gutter, so the
card gains on all four sides.
Three things, all about the same card.

The square was still a floor: a growing frame could get taller but never
shorter, so a short TLDR paid for dead gradient underneath it. The floor
comes off in both the frame and the capture, which now measures the card
in both directions.

The TLDR was still styled as the display copy it used to be — balanced
wrapping and negative tracking, at 1.35 leading. All three fight a normal
weight paragraph, so it reads as body text now: plain wrapping, no
tracking, 1.55 leading.

Source and date share a line, dot-separated. Two stacked lines of grey
read as two separate facts; the credit is one.
The headline goes, and the rule under the copy with it. The TLDR already
says what the headline says, at more length and in the reader's interest;
printing both made the card argue with itself. With one block of copy and
one line of credit there is nothing left for a divider to separate — the
space does that on its own.
Bold, under the TLDR, above the credit. No rule between them — the gap
reads as the break now that the copy is one block.
The TLDR gets 44px of air before the rule and the credit block 32 after,
so the break belongs to the copy above it rather than sitting flush.
Headline, source and date come off, and the rule with them. What is left
is the summary and the daily.dev mark, which is attribution enough for an
image whose only job is to carry the text.
Source and date come back under the rule, so the image says where the text
came from. The headline stays off: the TLDR already says what it says.
They sit next to each other everywhere this feature gets reviewed, and a
highlight is a post's own text, so looking like two different products was
the wrong signal. The copy scale moves into snapshotText as one function
both cards call, the passage drops to body weight and 1.55 leading, and the
credit is the post card's line — round avatar, one row, dot-separated.

The post title comes off the highlight card with it: the passage is the
subject, and the title was a second headline competing for the same job.
Surfaces 1, 1b and 2 were three cards that happened to carry text. They are
one card now: white copy at a single size over the wide frame, credited to
its source, and nothing else.

What came off along the way — the highlight card's surrounding passage,
quote glyph and URL, the post card's date, the Happening now eyebrow,
headline and timestamp. Each was a second thing competing with the copy for
the same glance.

The size is a constant rather than a scale. These frames grow and shrink to
fit now, so type no longer has to shrink to reach the bottom of a fixed
square, and a shared image that changes size with its length reads as two
different cards. The credit block moves into SnapshotCredit so the two
cards cannot drift apart.
An optional eyebrow on the post card, painted with the surface's own
wordmark gradient. It says which part of the product the copy came from,
which the TLDR alone cannot — and unlike the headline it does not repeat
what the copy already says.
The label naming the surface — Hot take, Reading overview, Squad, Level up
and the rest — sat above the card's content, where it read as a headline for
the copy or the stats under it. It is a sibling of the mark, not a title, so
it rides the logo row on the far right.

One SnapshotEyebrow now draws all of them, fed through the frame's new
logoAside slot. Three separate implementations go away with it: the inline
spans in the entity and list cards, and SnapshotIdentity's trailing label,
which no card passes any more.

Surfaces 4 and 12 also become the post card's treatment — a watercooler
post is a person's own words and a comment is too, so both are copy at one
size credited to whoever wrote it. The discussion card drops the "From the
discussion" label, the post title and the upvote/reply counts with it. The
hot take stops splitting one sentence across two type styles.
A hot take is one short statement, so it and its upvote count sit centred
under the mark rather than ranged left in a column they do not fill.

The invite card's link had a rule above it that separated nothing: the whole
card is one invitation, and the space already sets the URL apart.
Take the merged captureShareImage from #6426 and keep the width, height
and capture timeout the snapshot cards need on top of it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The selection on its own loses whatever made it worth marking — the
sentence it answers, the claim it qualifies — so the card now quotes the
whole paragraph and marks the chosen run inside it, bold and white over a
marker tint while the rest of the paragraph recedes to context.

The card takes `passage` plus offsets rather than one string, which is the
shape a DOM selection already has. Passages over the limit are windowed
around the marked run instead of truncated from the top, so a selection
near the end of a long paragraph still arrives with context either side;
each cut end moves to a word boundary and is ellipsed, an end that reached
the paragraph's own edge is left whole.

Also fixes the Edge cases story, which crashed on a SNAPSHOT_TEXT_LIMIT
that was used but never imported, and gives its over-the-cap case a
passage that actually exceeds the cap.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
#6556 landed the post-page placements first, carrying newer copies of the
snapshot primitives this branch introduced. Main's versions are kept for all
six conflicts: SnapshotButton is the one the post page already mounts, and
the capture, clipboard, text windowing and CSS changes on main are supersets
of this branch's.
#6556 shipped SnapshotButton as a target-based button with its own
off-screen staging (useArmedCard). This branch's card-based button went
with the merge, which left useSnapshotCapture as a second capture
pipeline with no caller, so it is removed.

- PostSnapshotCard and DiscussionSnapshotCard were copies of
  HighlightTextSnapshotCard's passage-plus-credit layout. They now render
  it, with a new optional label on the logo row for the surface eyebrow.
  The post card also credits through snapshotSource, so an unattributed
  link names its domain instead of the API's literal "unknown".
- The boxed stat tile was pasted into three cards and had already drifted;
  it is SnapshotTile in SnapshotStats now.
- Top reader badges keyed on the keyword, which repeats when the same tag
  is earned in different months.
- The profile cover bleed hardcoded the frame's padding; it reads the
  exported constant.
- Imports go to lib/numberFormat rather than the lib barrel.
- SnapshotContent only ever rendered in Storybook, so it moves there.
- The Sharing map carried internal traffic and share counts; this repo is
  public, so the table and the per-network numbers are gone.
- Its advice to put the link on the clipboard with every snapshot is
  replaced with what shipped: the clipboard is image-only.
- The post page surface mockup is dropped, as it was from #6556: those
  placements are live now, so the real page is the reference.
- The post edge cases rendered a title, meta row and seven-line TLDR the
  post card no longer has; they now render PostSnapshotCard, including an
  unattributed source.
- PostSnapshot.stories used the card prop of the removed button and
  duplicated the post placement in Share images.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants