feat(snapshot): square share images for every daily.dev surface - #6544
Draft
tomeredlich wants to merge 138 commits into
Draft
feat(snapshot): square share images for every daily.dev surface#6544tomeredlich wants to merge 138 commits into
tomeredlich wants to merge 138 commits into
Conversation
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ass card (#6462) Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…rfaced (#6463) Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…#6461) Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
I had only drawn the profile list row. The surface people actually meet a hot take on is HotAndColdModal: an emoji tile, centred Title3, the quote in Body-tertiary, an upvote pill and a bordered author footer, with the three reaction buttons and 'Add your own hot take' beneath. The snapshot already ships there, on the top card only and at Float, beside the upvote pill — so the profile-list placement is a placement of an existing card, not a new feature. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Each section is now Today plus the chosen option, with the alternatives removed: - Swipe modal: snapshot promoted from Float to labeled and filled. - Profile list: snapshot beside the upvote at XSmall. - Reading history: an icon-only copy link before the menu, always visible rather than hover-gated. Dead 'lead' branches dropped from the list and history rows. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both snapshot variants removed, along with the now-unreachable snapshot target inside InviteScreen. The section keeps its Today state as a reference point and states the conclusion outright: no change proposed, because an image of a referral cannot be clicked and the page already carries the most complete share UI in the product. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The settings page is complete and unreachable: a labelled TextField, a Primary Copy link and the seven-target row, buried three levels into settings, long after the moment a new account is most willing to bring someone with them. So the invite is not a payload problem, it is a placement one — which makes it a surface of its own rather than a suggestion attached to the feed export. The new page draws #6366's step on the real funnel chrome: FunnelStepCtaWrapper's glass branch, the 32rem rail, StepHeadline, three invite slots, the glass bar CTA and step dots. Snapshot is deliberately absent throughout — a referral image cannot be clicked. Invite & feed export becomes Copy my feed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Scoped to custom feeds and drawn where it belongs: FeedSettingsGeneralSection already gates feed name, the emoji picker, the default-feed toggle and delete behind isCustomFeed, so an export is a property of a feed you built rather than a control on the feed header. The 'Share this feed' block sits between the default toggle and Happening Now placement. Replaces the invented feed-header mock with the real modal: seven tabs, Cancel and a disabled Save, and every General block in its shipped order. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Pick emoji and the snapshot control were filling the panel width because the block is a flex column. Both now size themselves, matching Make default and Delete feed at w-40. 'Choose an icon' also has no description in production, so the empty line is gone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Reframed on the product call: a custom feed is something you built, so the thing worth sending is the feed, not a picture of its posts. Copy link leads, the top-20 text list becomes the fallback, and snapshot is out entirely — which revises #6362's sharing-map row. Adds the recipient side, which is what makes the share worth anything: open the link, see whose feed it is and what adding it will do, get a new feed named after the sharer. FeedSettingsCreate's /feeds/new?entityId=&entityType= flow is the precedent — same thing with a set instead of a single tag. Also drawn: the added state, and the feed limit, which FeedSettingsCreate already treats as a real error. Four blockers named, three of them not frontend. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Copy on add, not a live subscription. - Sign-in required: a feed lives in an account, so a session-less visitor gets a sign-in state and opens the link again afterwards. No logged-out preview, which is also why the sample posts are dimmed and not links. - 'Just show me the posts' removed, and the feed-limit copy now names the only two ways out — delete a feed or upgrade. - The remaining open question restated plainly: whether an added feed consumes a feed slot. The story was titled 'Copy my feed' while its heading read 'Share my feed', which made the two halves look like separate pages; renamed so the sharer and recipient sections are visibly one page. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add stays live and the failure comes back as a real toast — NotifContainer with the Error variant's AlertIcon — naming both ways out. Nothing is disabled up front, because we cannot know the recipient is at their cap until they press, and a dead button explains nothing. That also settles the last open question: an added feed uses a slot like any other, so there is one kind of feed, one limit and one message. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Sep 2, 2026
Three more celebrations already ship with no share route, drawn before and after: TopReaderBadgeModal, which already generates an image for its Download button and shares none of it; the ten-tier streak milestone system in streakTiers.ts, which is far richer than the day count we had been drawing; and ReputationPrivilegesModal. Six more listed rather than drawn, because each needs a call on whether the moment is worth interrupting: the achievement unlock modal, levelling up, curating the profile showcase, being awarded, a post that did well via analytics or a boost, and going Plus. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…d Plus Being awarded and a post that did well move from the listed set into drawn before/after frames, joining the top reader badge and the streak tiers. Reputation privileges and going Plus removed. The awards moment is drawn from the recipient's side — ListAwardsModal's cores total and giver list — and analytics gets two placements, an icon in the header and a labeled control under the stats where the numbers have just made the case. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Sep 2, 2026
…o choose from The Snapshot button becomes a Share button: the icon and label are the ones people already recognise, and pressing it opens a menu that previews the designed 1080² card before offering it. The menu opens immediately with a skeleton rather than waiting on the render. Rasterizing takes a second or two, and a control that does nothing visible for that long reads as broken — so the capture is deferred a tick, letting the menu paint first, and Copy link stays usable while the card is still rendering. Seven layouts are up for review in Features/Snapshot/Menu styles, all live against the same card: rows, rows-centred, an overlay pill on the card, a compact no-preview variant, Tsahi's split button from #6369, and the two ways the image can sit in the share modal — its own section or a single tile. The `menuVariant` prop exists to serve that page and comes out with the losers. The image is one action, not a mode over the sheet's other targets. A link needs a destination, which is why there are twenty of them; an image does not, because the OS share sheet and the clipboard are the destinations, and no web share intent takes an image anyway. So the image owns its own action and the squads and external tiles keep sharing the link — which is what keeps this on one screen instead of two. Pressing it hands the PNG to the native sheet where the platform takes files, copies it to the clipboard otherwise (with a toast, since the clipboard has no UI of its own), and downloads it only as a last resort. Capture moves into `useSnapshotCapture` so the dropdown and the modal section render from one implementation. Its `isActive` gate matters twice: a feed never carries a 1080px card per item until someone asks to share, and because the modal pre-renders on open, the click handler reaches `clipboard.write` with no await in front of it — still inside the user gesture the clipboard requires. Two surfaces had no card at all, from the #6579 and #6581 reviews: - `FeedSnapshotCard` carries a shared feed's recipe — tags and source count — rather than its posts, which are a standing filter and would go stale. - `AwardSnapshotCard` names the sender. It is the only status moment that comes from someone else, and a gift with no giver reads as self-congratulation. Tsahi's `SplitShareButton` and `CopyStateIcon` come across from #6369 unchanged apart from four additive props — `onOpenChange`, `header`, `menuLayout` and a width override — so his grid is untouched when nobody passes them. `SocialShareList`, `SocialShareButton` and `SocialShareContainer` gain optional size and compact props, defaulting to today's values, so the dropdown can tighten without moving anything else in the app. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The fixture pinned `endDate` to a literal 2026-09-01, which passed, so the running-sale test started failing on its own. Main already replaced it with a relative date and a comment saying why; this branch forked before that landed. Kept as its own commit so it can be dropped when the branch rebases onto main. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two corrections after catching up with the branch. FeedSnapshotCard goes: ac9dacd already settled that a custom feed is shared as the feed itself, not a picture of its posts, and took snapshot off that surface entirely. The card was written from the #6579 review page without checking that decision first. The clipboard write now goes through copyShareImage instead of calling `clipboard.write` inline. The util hands ClipboardItem a promise rather than a resolved blob, which is what keeps Safari from dropping the write for losing the gesture — and there is no reason for two of these. The toast moves to ToastType.Success rather than a ✅ in the string. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Sep 3, 2026
The ten surface pages each drew the surface as it ships, the placement we want, and one or two louder alternatives. That is the shape of a decision being made, not one already taken — and the pages are read now to review the placements the sibling PRs implement. Each page keeps only what those PRs ship, labelled with the flag that gates it where there is one. Gone: the ⋯-menu and share-sheet route on the post page, the seven-post-type audit, the four directory surfaces before their copy link, the hot-take and history rows before theirs, the labeled-and-filled pushes, the profile's visitor menu, and the three status moments we listed without drawing. The props and furniture that existed only to switch between the two halves go with them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #6426 — base retargets to
mainonce that merges. The diff here is one commit.Adds a Snapshot control that exports a surface as a branded 1080×1080 PNG, and the twenty card designs it captures. Easier to review in Storybook than in the diff: Features/Snapshot → Share images (every image), Spec (dimensions and content contract), Edge cases (how each card degrades).
Changes
Not in this PR
Design review only. No data wiring — every card reads Storybook fixtures. No feature flag. No share flow: the button downloads a PNG, and
onCaptureis the seam for whatever replaces that. RTL is not handled yet.Events
None.
Experiment
None.
Testing
typecheck-strict-changedclean; eslint clean on every touched file.pnpm --filter storybook buildverified, and the static bundle re-checked in a browser — cards render and capture from the production build.🤖 Generated with Claude Code
The surface pages
The ten pages under Features/Snapshot/Surfaces each used to draw the surface as it ships, the placement we want, and a louder alternative or two. They now show only what the sibling PRs implement, labelled with the flag that gates it where there is one — the before halves and the options we did not take are gone, along with the props and page furniture that existed only to switch between them.
Review links