Skip to content

feat(referral): redesign the invite friends settings page#6367

Open
tsahimatsliah wants to merge 17 commits into
mainfrom
claude/refer-friend-settings-redesign-a23ebe
Open

feat(referral): redesign the invite friends settings page#6367
tsahimatsliah wants to merge 17 commits into
mainfrom
claude/refer-friend-settings-redesign-a23ebe

Conversation

@tsahimatsliah

@tsahimatsliah tsahimatsliah commented Jul 22, 2026

Copy link
Copy Markdown
Member

Changes

Redesigns /settings/invite into a compact, flat page with one section rhythm, adds a giveback cross-promo, and replaces the copy-link button with a split copy/share control.

  • Reward promoInvite 3 friends, get 1 month of Plus with a compact tracker: three rounded-rect slots that fill with the referred developers' avatars, ending in a 1 month of Plus chip. Once the goal is met the tracker becomes a flat Plus-tinted card naming the reward and the month it covers. Progress comes from referredUsersCount on the generic referral campaign and is clamped to the goal inside the component.
  • Your invitation link — the link field now carries a split control: the left half copies the link and cross-fades the glyph to a checkmark, the chevron opens the standard share list (X, WhatsApp, Facebook, Reddit, LinkedIn, Telegram, Email, native share). Below laptop width it collapses to one button that opens the native share sheet. This replaces the old "or invite via" row, which held the same eight buttons a few pixels below the field.
  • More ways to give backGivebackInviteCard, wearing the same treatment as the giveback founding-reward card (1px gradient frame over an opaque base washed with the same gradient at 8% via color-mix). Gated on the existing giveback flag.
  • Your referrals — unchanged list, now aligned with the rest of the page.
  • Notifications — two new types, referral_friend_joined and referral_plus_unlocked, both deep-linking to /settings/invite. No backend emits either one yet.
  • Storybook: Features/Referral/Invite friends page covers 0/1/2/3+ referrals, unlocked, existing-Plus member, giveback off, mobile width, the share menu open, and the two notification rows.

Caution

Nothing grants the free month yet. The reward is frontend framing only: no backend gives a Plus month on the third referral, and the dates shown are derived on the client from the third referral's join date. This page now promises it to every user, with no flag in front of it. Do not merge before fulfillment exists, or change the copy so it stops promising.

Notes for reviewers

  • ShareActions, SplitShareButton and CopyStateIcon are ported verbatim from the sharing-visibility branch (claude/share-split-copy-button), which ships them with its own PR and its own surfaces. Only the split variant is used here; the icon/inline variants come along so the two copies stay identical and the files dedupe cleanly whichever lands first. The two local additions are shortenUrl (the referral URL carries its own attribution, so it must go out unshortened) and a stopPropagation on each half of the split control — the text field's container focuses its input on click, which dismissed the menu the instant it opened.
  • InviteLinkInput takes an optional actionButton that replaces its built-in copy button. Gated on the new prop, so the other five consumers keep their exact DOM and their existing copy handling. logProps is now optional for the same reason: it only drives the built-in button.
  • UserList hard-codes px-6 on its rows and on its loading placeholder, and overrides the consumer's userInfoProps.className. Alignment is therefore handled with -mx-6 on a wrapper around the whole list, so rows, skeletons, and the empty state all line up with the headings.
  • Deleted the single-export components/referral/index.ts barrel per the repo's no-barrel rule; the only other consumer (settings/organization) now imports the source file directly.
  • GivebackInviteCard sizes its own charm rather than reusing GivebackMascot: that component bakes h-44 tablet:h-56 into the same class string a caller would override, and class names are concatenated rather than merged, so the hero size always wins. The charm render ships on solid black, so it is cut out with an SVG luminance-to-alpha filter — mix-blend-screen only works on a dark surface and made it vanish in light mode.

Events

No new event names. Existing events fire from new places:

Type event_name value
Change copy referral link now fired by the split control's copy half, target_id: invite friends page
Change invite referral now fired by the share menu, target_id is the provider
Change click giveback gift entry now also fired from the invite page giveback card with target_id: invite friends page

Experiment

None. referral_plus_reward was removed — the reward promo ships to everyone. The only flag on this page is the existing giveback one, which still gates the cross-promo section.

Manual Testing

Caution

Please make sure existing components are not breaking/affected by this PR

The page is auth-gated and the local webapp shell does not boot without a backend, so review it on the Vercel preview at /settings/invite, and the states in Storybook (pnpm --filter storybook dev) — the Storybook deployment is Ignored for PRs.

Verified locally: webapp suite (45 suites / 305 tests), shared suites for referral/share/fields/notifications (13 suites / 80 tests), pnpm --filter shared lint and pnpm --filter webapp lint clean, and the changed-file strict typecheck guard. The split control was checked in light and dark, on desktop and at mobile width, with the menu open and closed.

🤖 Generated with Claude Code

Preview domain

https://claude-refer-friend-settings-red.preview.app.daily.dev

…romo

Rebuild /settings/invite as a campaign surface: a marketing-inspired hero
with a cabbage-to-onion gradient headline promoting "invite 3 friends,
get 1 month of Plus free", a 3-slot progress chain filled with referred
users' avatars ending in a Plus reward chip, a how-it-works step grid,
and a giveback cross-promo card (gated on featureGiveback) inviting
users to contribute to the community. The reward framing is driven by
referredUsersCount from the generic referral campaign; share link input,
social share list, and the referrals list stay intact.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
daily-webapp Ready Ready Preview Jul 23, 2026 2:07pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
storybook Ignored Ignored Jul 23, 2026 2:07pm

Request Review

…ions

Replace the aurora hero and per-section bespoke styling with one visual
language: every block is a standard settings AccountContentSection, the
progress chain shrinks to a compact inline row (size-8 avatar slots +
neutral connectors + a single Plus-accent chip), how-it-works becomes a
flat numbered list, and the giveback promo becomes a plain float card
with a gift icon instead of gradients and mascot art. No gradients,
glows, blend modes, or animations — one accent color, one card
treatment, one section rhythm.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reuse the visual language of GivebackFoundingAward for the invite page's
giveback promo: a 1px avocado/cabbage/cheese gradient frame over an
opaque base washed with the same gradient at 8% via color-mix, a
gradient-text eyebrow, callout title, footnote body, primary CTA, and
the Giveback charm on a glow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tighten the offer copy to one short line, name Plus in the reward chip,
and switch the chip and empty invite slots from pills/circles to
rounded-10 rectangles so they match the ProfilePicture shape. The
progress caption moves above the slot row, the invite link and social
buttons get their own 'Your invitation link' section, and the
how-it-works list is removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
UserList hard-codes px-6 on each row and overrides the consumer's
className, so referral rows sat 24px right of every heading. Cancel the
section padding on the list wrapper so rows line up and the hover
highlight spans the card, drop the dead className config, and make the
empty state a compact left-aligned row instead of a centered block.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Move the two bespoke pieces of the invite page - the reward progress
tracker and the giveback cross-promo card - into
shared/src/features/referral so Storybook can render the real
components (the page body itself lives in webapp and isn't importable
there). Add Features/Referral/Invite friends page covering 0/1/2/3+
referrals, the unlocked reward, the existing-Plus-member note, the
giveback flag off, mobile width, and component-level showcases.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Engineering pass over the invite page redesign:

- Gate the "invite 3, get 1 month of Plus" promo behind a new
  referral_plus_reward flag (default off). Nothing in the product grants
  the free month yet, so with the flag off the page describes referrals
  and promises nothing.
- Fix the unlocked copy claiming "3 friends joined" for any count at or
  above the goal; it is now count-agnostic, and the tracker clamps
  0..INVITE_GOAL internally.
- Move the two new components to their conventional homes
  (components/referral, features/giveback) and delete the single-export
  components/referral barrel per the repo's no-barrel rule.
- Wrap the whole referrals list in the negative margin instead of passing
  it through scrollingProps, which only reached the loaded branch and let
  skeleton rows sit 24px off before snapping into place.
- Let the progress row wrap and drop its connector below mobileL, where
  the reward chip was clipped at 320px.
- Drop the never-attached scrollingContainer ref, the redundant clamp, an
  aria-label duplicating visible text, decorative alt text, and a border
  that resized the chip on unlock.
- Storybook: match the real settings chrome, add the flag-off story, and
  remove an inert isPlus decorator option.
- Tests: assert the exact giveback href and click logging, cover the
  flag-off and past-the-goal states, and exercise the avatar path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@tsahimatsliah tsahimatsliah changed the title feat(referral): redesign invite settings page around 3-invites Plus promo feat(referral): redesign the invite friends settings page Jul 23, 2026
@tsahimatsliah

tsahimatsliah commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

🧭 Task context (auto-maintained by /fresh)

  • Goal: Redesign /settings/invite around an "invite 3 friends, get 1 month of Plus" promo, cross-promote the giveback campaign, and keep the page compact, flat, and visually consistent.
  • Branch: claude/refer-friend-settings-redesign-a23ebemain
  • Updated: 2026-07-23

Current focus: PR is engineering-ready and pushed. Awaiting review; no known open work.

Decisions (and why):

  • Plus reward gated behind new referral_plus_reward flag, default false — nothing in the product actually grants the free month yet, so shipping the promise unconditionally was a trust/support liability. Flag-off copy reads "Grow the community" and promises nothing.
  • Flat/compact over a marketing-style hero — the first attempt (aurora glows, gradient headline, mascot card, how-it-works grid) was rejected as busy. Everything is a standard AccountContentSection with one card treatment and one accent.
  • The tracker escalates: a quiet one-line row while the goal is open, a flat Plus-tinted card once it's met (no border — the tint carries it, matching the rest of the page). Unlocking previously only recoloured a chip border, which didn't read as an achievement. The card is deliberately compact: Body-bold headline, 32px reward tile, p-3, and a one-size-down (24px) avatar row in the footer.
  • The free month's period is derived on the client from the join date of the INVITE_GOALth referral (sorted copy — the query specifies no order), because no backend field carries the grant. rewardPeriod is an optional prop so the page can swap in a real field without touching the component.
  • Two notification types added frontend-side (referral_friend_joined, referral_plus_unlocked) with a new DevPlus icon type; both land in the Updates filter category. No backend emits them yet.
  • The giveback charm's black background is cut out with an SVG filter (alpha derived from the artwork's own brightness, ramped so mid-dark art stays opaque), not mix-blend-screen and not a dark plate. Screen blending only worked over dark surfaces, and a dark tile was rejected as an unnecessary box. Cloudinary can't strip it at source (the URL is signature-locked; e_make_transparent 404s).
  • The giveback card reuses the GivebackFoundingAward treatment (1px gradient frame + 8% color-mix wash) so the two read as one family. It is the page's single accent moment.
  • Progress slots use rounded-10 to match ProfilePicture's radius — filled avatars and empty slots must share a silhouette.
  • No em dashes in this feature's user-facing copy or Storybook story names (explicit user request). Code comments are untouched; the numeric date range keeps its en dash (2 Jul 2026 – 2 Aug 2026) — flagged to the user, not yet challenged.
  • Components live in conventional homes: InviteRewardProgress in components/referral/, GivebackInviteCard in features/giveback/. Deleted the single-export components/referral/index.ts barrel per the no-barrel rule; settings/organization import updated as a consequence.

Key files:

  • packages/webapp/pages/settings/invite.tsx — the page: 4 sections (reward promo, invitation link, giveback, referrals); derives rewardPeriod
  • packages/shared/src/components/referral/InviteRewardProgress.tsx — locked row + flat unlocked card; InviteSlots has an isCompact variant; clamps joinedCount to INVITE_GOAL internally
  • packages/shared/src/features/giveback/components/GivebackInviteCard.tsx — gradient cross-promo card + the charm cutout filter
  • packages/shared/src/components/notifications/utils.ts — the two referral notification types + NotificationIconType.DevPlus
  • packages/shared/src/lib/featureManagement.tsfeatureReferralPlusReward (default false)
  • packages/webapp/__tests__/AccountInvitePage.tsx — 9 tests (flag on/off, 0/2/3/12 counts, reward period, avatars, giveback href + click logging)
  • packages/storybook/stories/features/referral/ — "Features/Referral/Invite friends page", 12 stories (incl. Notifications)
  • packages/storybook/stories/components/notifications/_mock.tsx — referral rows also feed "Components/Notifications/Full page"

Done:

  • Page redesigned, flattened, and iterated per feedback (compact progress, Plus named in the chip, rounded-rect slots, caption above the row, "Your invitation link" heading, how-it-works removed)
  • Referrals list left-aligned with the rest of the page
  • Giveback charm fixed for light mode via the SVG cutout filter
  • Dominant → then compacted → then flattened unlocked reward card, with start/end dates
  • Referral notifications designed and shown on the notifications page mock
  • Em dashes stripped from feature copy and story names
  • Storybook page covering every state
  • Independent review pass; all findings fixed (flag gate, count-agnostic copy, loading-state alignment, 320px clipping, dead code, a11y, tests)
  • Verified: webapp suite 45/45 (306 tests), shared notifications/referral/giveback suites green, eslint clean, changed-file strict typecheck clean, shared tsc errors all in the unrelated pre-existing useUserExperienceForm.spec.tsx

Next:

Gotchas:

  • UserList hard-codes px-6 on its rows and on its loading placeholder, and overrides the consumer's userInfoProps.className. Align it with -mx-6 on a wrapper around the whole <UserList>; passing it via scrollingProps.className only reaches the loaded branch and leaves skeletons misaligned.
  • GivebackMascot bakes h-44 tablet:h-56 into the same class string a caller would override, and class names are concatenated (not tailwind-merged), so smaller numeric overrides silently lose. The invite card sizes its own charm for that reason.
  • The giveback charm artwork ships on solid black. GivebackMascot's dark-only consumers hide the same light-mode bug the invite card had; the cutout filter in GivebackInviteCard is the fix to lift if they ever land on a light surface.
  • Adding a NotificationIconType requires updating three full Records in notifications/utils.ts (icon, icon theme, icon style) — TS enforces it.
  • The Storybook Vercel deployment is Ignored for PRs — no hosted link. Review locally: pnpm --filter storybook exec storybook dev -p 6013.
  • packages/storybook has no eslint binary installed, so pnpm --filter storybook lint fails on main too.
  • /settings/invite is auth-gated, so visual review happens on the Vercel preview, not locally.
  • .claude/launch.json is intentionally untracked (machine-specific port).
  • __tests__/MyFeedPage.tsx "should request user feed" is flaky under the full suite; it passes in isolation. Re-run, don't debug.

To continue this task in a fresh chat: check out claude/refer-friend-settings-redesign-a23ebe, then run /fresh https://github.com/dailydotdev/apps/pull/6367.

The charm render sits on solid black and needs mix-blend-screen to drop
it, which only works over a dark surface — on the light theme it blended
away entirely. Give it its own tile in the artwork's own black so the
treatment holds in both themes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The unlocked state only recoloured a chip border, so hitting the goal
looked the same as missing it. Once the goal is met the tracker now
becomes a full-width Plus card that names the reward, marks it earned
and states the period the free month covers.

The period is derived client-side from the join date of the third
referral; no backend field carries the grant yet.

Adds the two notification types the feature would emit — a per-join
"friend joined through your invite" and the one-off "free month
unlocked" — with their icon/category mapping and rows in the
notifications page mock.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The dark tile made the card carry a box it didn't need. Derive the
alpha channel from the artwork's own brightness instead, so the black
background falls out and the charm reads on any surface without a
blend mode or a backdrop.

Also drops the em dashes from this feature's copy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tsahimatsliah and others added 2 commits July 23, 2026 12:54
Smaller headline and reward tile, less padding, and a one-size-down
avatar row in the footer, so the card sits at the same weight as the
other cards on the page.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the accent border and let the tinted background carry the card on
its own, matching the flat treatment the rest of the page uses.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tsahimatsliah and others added 2 commits July 23, 2026 15:25
Replace the invite link's copy button with the split control from the
sharing-visibility work: the left half copies with a cross-fade
confirmation, the chevron drops the standard share menu. Its own DS
border is the seam, and both halves tighten one padding step so the
hairline is not sitting in a canyon.

The menu holds the same eight networks the "or invite via" row did, so
that row goes; keeping both duplicated it 40px apart. The split halves
stop their own clicks — the field container focuses its input on click,
which dismissed the menu the instant it opened.

Drop `referral_plus_reward` with it: the "invite 3 friends, get 1 month
of Plus" promo, its tracker and the Plus-member note now ship to
everyone, and the "Grow the community" fallback and its test go away.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`.claude/launch.json` is local preview tooling and has nothing to do with
the referral page, so it leaves the PR.

`logProps` only drove InviteLinkInput's built-in copy button. The invite
page replaces that button, so the prop was passed and never read; make it
optional and stop passing it where it does nothing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pull the reworked control from `claude/share-split-copy-button`: the
geometry moves into a standalone `SplitShareButton`, the copy glyph swap
into `CopyStateIcon`, and `ShareActions` composes them.

The dropdown changes with it. The "Share" heading goes — the control it
drops from is already labelled, so it only repeated it. The list is a
4-column grid rather than `flex-wrap`, so the columns divide the content
box exactly and the padding reads equal on all four sides, and the
padding needs `!p-4` to beat the dropdown's own `p-1.5` at equal
specificity. The chevron loses `pressed`: it renders `aria-pressed`,
which paints a held-down fill, and Radix already sets `aria-expanded`.

The two local deltas ride along unchanged — `shortenUrl` for the
referral link, and each half stopping its own click so the surrounding
field cannot steal focus and dismiss the menu.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

1 participant