feat(signature): add share popup with confetti, rank badge, and platform share#49
Merged
Conversation
…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
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>
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.
What
Adds a
SharePopup.astromodal that opens after clicking the redesigned Sign and share button (SignButton.astrois now a<button>carryingdata-github-urlso 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
prefers-reduced-motion).#{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…).<blockquote>with display “ ” quote marks, italic copy, left alignment, and a clickableai-driven-development.orglink (nowww).--x-brand)--linkedin-brand)navigator.languageat open time; FR message if locale starts withfr, EN otherwise (UI stays EN per AGENTS.md).${encodeURIComponent(...)}hrefs.Tokens
--x-brandand--linkedin-brandadded totokens.css(oklch only)..confetti-emoji+@keyframes confetti-falladded tosignature.css.Verification
npm run buildclean.Files
app/src/components/signature/SharePopup.astro(new)app/src/components/signature/SignButton.astro(button + data-github-url)app/src/components/sections/Signature.astro(passesrank={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)