Skip to content

feat(signature): add share popup with confetti, rank badge, and platform share#49

Merged
alexsoyes merged 8 commits into
mainfrom
feat/share-popup
Jun 30, 2026
Merged

feat(signature): add share popup with confetti, rank badge, and platform share#49
alexsoyes merged 8 commits into
mainfrom
feat/share-popup

Conversation

@alexsoyes

Copy link
Copy Markdown
Contributor

What

Adds a SharePopup.astro modal that opens after clicking the redesigned Sign and share button (SignButton.astro is now a <button> carrying data-github-url so the client can drive the flow).

Why

The previous share popup had broken hrefs (literal ${encodeURIComponent(...)} not interpolated server-side), a generic look, and no celebration. The signing moment deserves a proper payoff: you stamp your rank, you see your identity statement, you copy or post in one click.

Highlights

  • Confetti rain of 64 emojis spans the full viewport on sign (z-index above the dialog; disabled under prefers-reduced-motion).
  • Signatory rank #{count+1} is the hero, followed by an explicit identity statement "You are the Nth AI-Driven Developer" (localized FR/EN, with correct ordinals: 1st/1er, 2nd, 3rd, 11th…).
  • Quotation block: the share phrase lives in a real <blockquote> with display “ ” quote marks, italic copy, left alignment, and a clickable ai-driven-development.org link (no www).
  • Countdown (3-2-1) announces the GitHub PR opening, then the share buttons unlock:
    • X (brand black, --x-brand)
    • LinkedIn (brand blue, --linkedin-brand)
    • Copy pill (accent) — copies the full 2-line message and shows "Message copied/copié" feedback.
  • i18n: message language tracks navigator.language at open time; FR message if locale starts with fr, EN otherwise (UI stays EN per AGENTS.md).
  • Mobile: the card docks as a bottom sheet; reduced-motion: animation + confetti disabled.
  • Bug fix: X / LinkedIn URLs are now resolved client-side, fixing the previous un-interpolated ${encodeURIComponent(...)} hrefs.

Tokens

  • --x-brand and --linkedin-brand added to tokens.css (oklch only).
  • .confetti-emoji + @keyframes confetti-fall added to signature.css.

Verification

  • npm run build clean.
  • Manual inspection via Playwright: rank, statement, message HTML, quote block, button states, copy feedback, FR/EN locales, mobile bottom sheet, reduced motion.
  • Existing test suite: same 6 pre-existing failures (4 passing) — unchanged baseline.

Files

  • app/src/components/signature/SharePopup.astro (new)
  • app/src/components/signature/SignButton.astro (button + data-github-url)
  • app/src/components/sections/Signature.astro (passes rank={count+1} to popup)
  • app/src/components/ClientApp.astro (popup controller, confetti, copy)
  • app/src/styles/tokens.css (brand fills)
  • app/src/styles/sections/signature.css (confetti)

…orm share

Adds a SharePopup.astro modal that opens after clicking the redesigned
"Sign and share" button (SignButton.astro is now a <button> carrying
data-github-url so the client can drive the flow).

Highlights:
- Confetti rain of 64 emojis spans the full viewport on sign (z-index
  above the dialog, disabled under prefers-reduced-motion).
- Signatory rank #{count+1} is the hero, followed by an explicit identity
  statement ("You are the Nth AI-Driven Developer", localized FR/EN).
- The share phrase lives in a real <blockquote> with display quote marks,
  italic copy, left alignment, and a clickable ai-driven-development.org
  link (no www).
- A countdown (3-2-1) announces the GitHub PR opening, then the share
  buttons unlock: X (brand black), LinkedIn (brand blue), Copy (accent).
- Copy pill copies the full 2-line message and shows "Message copied/
  copie" feedback.
- Message language tracks navigator.language at open time; X / LinkedIn
  URLs are resolved client-side (fixes the previous un-interpolated
  ${encodeURIComponent} bug).
- Mobile ships as a bottom sheet; reduced-motion disables animation and
  confetti.

Tokens:
- Add --x-brand and --linkedin-brand to tokens.css.
- Add .confetti-emoji + @Keyframes confetti-fall to signature.css.

Verified: build clean, no console errors, FR/EN locales, copy, mobile
bottom sheet, reduced motion.
…ummary

share-offsite redirects internally to shareArticle but strips every query
param except url — the popup opens with a bare link, no pre-filled post
text, and no compact popup affordance. Switch to shareArticle directly
with mini=true (compact popup), url (canonical www URL), title (mirrors
og:title, localized FR/EN), summary (pre-fills the post with the full
share message — same UX as X's text= param), and source (bare domain).

Also add og:image:type and og:image:alt to Page.astro — strengthens the
LinkedIn Open Graph preview card.
LinkedIn's composer has long been documented (SO q/56373068, q/51189486)
to render the shareArticle `summary` param inconsistently — and may
silently drop it entirely when the value contains a literal newline
(`%0A`). Our previous summary was two lines joined by `\n` (the
 Clipboard / X context expects multiline), so when the shareArticle popup
 opened post-login, the post body appeared empty.

Fix: build a separate flat variant of the share message that uses an
em-dash separator on a single line (`buildMessageFlat`), used only for
the LinkedIn `summary` param. X's intent/tweet?text= and the Copy pill
keep the original multi-line text (Twitter renders it reliably).

Also intercept clicks on the X and LinkedIn share buttons and open them
via `window.open(href, 'aidd-share', 'width=750,height=620')`. The
750×620 size matches react-share's LinkedinShareButton and lets
LinkedIn's `mini=true` flag actually pick its compact share layout —
opening the same href as a plain target=_blank tab falls through to the
default 200 signatories list view and looks 'broken' to the user.

Sources:
- react-share master (LinkedinShareButton.tsx)
- Microsoft Learn, Share Plugin (last updated 2022-03-31) confirms
  shareArticle endpoint
- Empirical curl -I (2026-06-27) confirms shareArticle preserves all
  params through the login 302; share-offsite strips everything except
  `url`.
…llback

LinkedIn has historically dropped the shareArticle `summary` param
during pre-fill (SO q/56373068, q/51189486 — long-standing complaints
now confirmed by LinkedIn removing all non-OAuth share docs). The
composer body opens empty even when the URL carries summary=title=.
The official IN/Share plugin exposes only `data-url` — no text/title/
summary attribute exists — confirming LinkedIn no longer accepts
URL-based composer text pre-fill.

New strategy for the LinkedIn button:

1. **Web Share API first** — if `navigator.share({text, url, title})` is
   available (iOS/Android, modern Chrome/Edge desktop), call it. The
   native OS share sheet pre-fills LinkedIn's composer body `text`
   reliably — this is the only path that still works end-to-end in 2025.

2. **Clipboard + popup + hint fallback** — if Web Share isn't available,
   copy the share message to the clipboard, open the LinkedIn popup
   (750×620, shareArticle url-only), and surface a small hint next to
   the button: 'Message copied — paste it on LinkedIn' (FR: 'Message
   copié — collez-le sur LinkedIn'). The user is one Cmd/Ctrl-V away from
   the share text being in the composer.

X button stays unchanged — Twitter reliably honors `?text=` including
newlines, so the public body is pre-filled as before.

Files:
- ClientApp.astro: dodge Web Share detection via `navigator.share` +
  `navigator.canShare`, async clipboard.writeText with try/catch so the
  popup opens regardless of permission.
- SharePopup.astro: add `#share-linkedin-hint` element + styling (LinkedIn
  blue tinted pill, distinct from the green copy-confirmation feedback).
…unavailable

LinkedIn has deprecated URL-based composer pre-fill. The only path that
still reliably pre-fills LinkedIn's composer body in 2025 is the native
Web Share API (the OS share sheet carries `text` + `url`, and LinkedIn's
share target honors both). Replace the previous strategy (Web Share →
clipboard + popup + hint fallback) with a clean single approach:

- LinkedIn button is a `<button>` (was an `<a href="...">`); its click
  handler unconditionally calls `navigator.share({text, url, title})`.
- When `navigator.share` is unavailable (older browsers), the button is
  hidden at popup-open time via `liLink.hidden = true`, so the action
  row degrades cleanly to X + Copy.
- Drops the previous `buildMessageFlat` helper, the shareArticle URL
  construction, the clipboard-with-hint fallback, and the
  `share-linkedin-hint` element + its CSS.

X button unchanged — Twitter reliably honors `?text=` with newlines, so
the URL-intent popup keeps pre-filling the composer.

Sources:
- Stack Overflow q/56373068, q/51189486 (LinkedIn summary param not
  pre-filled, long-standing reports).
- Microsoft Learn share-plugin doc (last updated 2022-03-31) confirms
  IN/Share plugin exposes only `data-url`.
The previous commit (`08d653a`) hid the LinkedIn button entirely when
`navigator.share()` was unavailable. Problem: Firefox desktop still
doesn't ship Web Share in 2025, and Firefox has higher-than-average
share among the AI-driven-dev audience. Hiding the button entirely on
~5-10% of users is a usage-scope regression.

Revert to the dual-path strategy:
1. `navigator.share({text, url, title})` first — primary path, reliable
   pre-fill for Chrome/Edge desktop, iOS/Android, modern browsers.
2. Clipboard copy + LinkedIn url-only popup + localized hint
   ('Message copied — paste it on LinkedIn' / 'Message copié — collez-le
   sur LinkedIn') — fallback for Firefox and older browsers. The user is
   one Cmd/Ctrl-V away from the composer body being filled.

The hint only surfaces in the fallback branch, so the common Chromium
path remains visually silent. The button stays visible on every browser,
preserving brand identity and affordance.

Restores:
- the `#share-linkedin-hint` element in SharePopup.astro
- the .share-popup-linkedin-hint CSS
- the clipboard + popup + hint logic in the LinkedIn click handler
- the header comment describing the dual-path strategy

Verified via Playwright in 4 scenarios:
- Web Share available → navigator.share called, hint hidden
- Web Share unavailable → clipboard copy + popup 750x620 + hint visible
- FR fallback → 'Message copié — collez-le sur LinkedIn'
- X button unchanged (intent/tweet popup)
Manual polish: 'Manifeste du AI-Driven Development' → 'Manifeste de
l'AI-Driven Development' (correct French elision before a vowel).

Also drops the 'Rejoignez-moi →' / 'Join me →' lead on the second
message line — the URL now stands alone on line 2, which reads cleaner
in the citation block and stays shorter for the clipboard + X intent
(ranks prior were 'I just signed #39.\nJoin me → https...'; now
'I just signed #39.\nhttps://ai-driven-development.org/').
@alexsoyes alexsoyes merged commit 361b407 into main Jun 30, 2026
1 check failed
alexsoyes added a commit that referenced this pull request Jun 30, 2026
The Validate workflow (required check) has failed on every push/PR since
#49: the AC-6 component LOC budget (`.astro` ≤ 200) was breached by
ClientApp.astro (419) and SharePopup.astro (399).

Extract, verbatim and behavior-preserving:
- SharePopup `<style>` → src/styles/sections/share-popup.css (imported
  globally in Page.astro, mirroring every other section stylesheet). The
  classes are uniquely `.share-popup-*` prefixed, so global scope is
  equivalent to the previous scoped block.
- ClientApp script logic → ~/lib/share.ts (initSharePopup), ~/lib/scroll.ts
  (initSmoothAnchors), ~/lib/tweaks.ts (initTweaks), following the existing
  ~/lib/observers pattern.

The EDITMODE sentinel and TWEAK_DEFAULTS literal stay in ClientApp.astro
verbatim so the parent-iframe editor keeps finding the markers.

ClientApp.astro 419→51, SharePopup.astro 399→95. AC-6 passes; build + 52
unit tests green.

Co-authored-by: Claude Opus 4.8 (1M context) <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