Skip to content

chore(landing): delete the Send-in-seconds scroll-jack (TASK-21788) - #3011

Closed
0xkkonrad wants to merge 2 commits into
devfrom
chore/landing-drop-scroll-jack
Closed

0xkkonrad wants to merge 2 commits into
devfrom
chore/landing-drop-scroll-jack

Conversation

@0xkkonrad

@0xkkonrad 0xkkonrad commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The "Send in seconds" fold froze the page. On the way down, once the CTA reached the sticky-bar band, LandingPageClient set document.body.style.overflow = 'hidden', pinned window.scrollY, and swallowed wheel and touchmove with preventDefault until the reader had pushed 500px of virtual delta into growing the button to 1.5x. This deletes that machinery. Scrolling is now ordinary scrolling everywhere on the landing page.

Part of the PWA-sunset landing work (TASK-21788), split out as its own PR because it is a straight deletion and touches no migration surface.

What changed

  • LandingPageClient.tsx: removed isScrollFrozen, buttonScale, animationComplete, shrinkingPhase, hasGrown, their mirror refs, frozenScrollY / virtualScrollY / touchStartY, handleScrollDelta, and the scroll / wheel / touchstart / touchmove effect with its document.body.style.overflow writes. buttonVisible and its footer-visibility effect stay — that is the separate fade-out on the footer.
  • hero.tsx: dropped the buttonScale prop, its default, and --cta-scale from getCtaStyle. The --cta-x / --cta-y / --cta-r entrance and the hover transform are untouched.
  • sendInSeconds.tsx: removed the <div id="sticky-button-target"> wrapper — the only thing that read it was the deleted handler. id="send-in-seconds" on the section stays, and fold 10 keeps its .cta-motion .cta-enter entrance.
  • globals.css: dropped the now-unsettable scale(var(--cta-scale, 1)) from .cta-motion and the cta-enter keyframe (no-op; see Divergences).
  • New tests: __tests__/LandingPageClient.scrollJack.test.tsx and __tests__/ctaScrollJackRemnants.test.tsx.

How verified

  • pnpm test -- src/components/LandingPage → 7 suites, 27 tests, all green (includes the two new suites and the pre-existing Footer / SEOFooter / CurrencySelect ones).
  • The new suite is a real guard, not a vacuous one. jsdom gives every element a zero rect, so the old freeze window could never open by accident; the test re-inserts a #sticky-button-target with getBoundingClientRect stubbed to {top: 600, bottom: 660}, which is inside the band the old handler froze on at jsdom's 768px viewport. Run against the pre-deletion component (copied out of HEAD into a scratch file), its tests fail: body overflow becomes hidden, wheel and touchmove come back defaultPrevented, and the hero receives a buttonScale. Against this branch all four pass.
  • Round-1 review fix: the fourth test used to assert #sticky-button-target was absent from a slot that never contained it, so it could not fail either way. It now asserts the thing this PR actually removed — the anonymous <div ref={sendInSecondsRef}> around sendInSecondsSlot — by checking the slot's parentElement is the render container (LandingPageClient returns a fragment). Re-adding <div>{sendInSecondsSlot}</div> to the component makes exactly that test fail, and reverting makes it pass again.
  • Round-1 review fix: ctaScrollJackRemnants.test.tsx split the fold-10 block in two. One it holds only the scroll-jack regression assertions (#send-in-seconds present, #sticky-button-target null, .cta-motion present); a second, separately named it holds the /send href and the .cta-enter entrance, so PR 2's "GET THE APP" rewrite of fold 10 can retarget or delete that one without touching the regression guard or reading as a scroll-jack regression.
  • pnpm exec eslint --quiet on the six touched files → clean, exit 0.
  • pnpm exec tsc --noEmit → 6 errors, all pre-existing TS2307 in untouched files, none in this diff (see Divergences hotfix: sdk version update #3).
  • pnpm exec prettier --write on the touched files, then re-run clean.
  • Browser pass, flag off (round-1 review fix — this is the one change in the set that ships to 100% of landing traffic, so it is verified in a real engine, not only jsdom). next dev --webpack -p 3053 in the worktree + Playwright 1.58.2 chromium, on / and /pt-br at 1440x900 and 390x844 — four combinations, all clean:
    • getComputedStyle(document.body).overflow is visible and document.body.style.overflow is empty both while fold 10 is centred in the viewport and after scrolling past it; same for <html>.
    • a dispatched cancelable wheel (deltaY 400) and touchmove at fold 10 both come back defaultPrevented === false.
    • a real mouse.wheel sequence at fold 10 moves the page 1472 / 1860 / 1499 / 2021 px on the four combinations — the page never pins.
    • #sticky-button-target is absent from the live DOM; scrollWidth - clientWidth is 0 (no horizontal overflow).
    • the .cta-enter entrance still fires after the keyframe edit: replaying the class gives a running CSSAnimation named cta-enter, duration 450ms, computed transform matrix(0.999914, 0.0130896, -0.0130896, 0.999914, 0, 4) — the 0.75deg rotation and 4px lift of the from frame, resolving correctly now that scale(var(--cta-scale, 1)) is gone.
    • fold-10 centring survived the wrapper removal: the .cta-motion element's centre X equals #send-in-seconds's centre X to 0.00px on all four combinations (720.00 vs 720.00 at 1440, 195.00 vs 195.00 at 390).
    • console: no React #418 / #423, no pageerror from application code. The only console errors are dev-server infrastructure (webpack-hmr websocket handshake failures, and ERR_CONNECTION_REFUSED on the run where earlyoom killed the server mid-pass — that combination was re-run clean afterwards).
    • screenshots: run/shots-scrolljack/fold10-{en,pt-br}-{1440x900,390x844}.png, raw measurements in run/shots-scrolljack/report.json and report-ptbr-390.json.

Divergences

  1. Also removed the dead --cta-scale from globals.css. The brief scoped the deletion to the components. Once hero.tsx stops writing the property, nothing in src, e2e, public, scripts or docs sets it, so .cta-motion's scale(var(--cta-scale, 1)) and the identical call in the cta-enter keyframe were permanently scale(1). Both removed — a no-op at runtime that stops the CSS from carrying a variable no component can set.

  2. Removed the #sticky-button-target wrapper and the ref wrapper around the send-in-seconds slot. The brief made the first conditional on a clean grep; the grep is clean, and the <div ref={sendInSecondsRef}> in LandingPageClient existed only to feed the freeze handler. Both were plain class-less block elements around block / inline-block children, so the layout is unchanged.

  3. pnpm typecheck is not clean on this worktree, for reasons this PR cannot fix. It reports exactly 6 TS2307 "Cannot find module" errors and nothing else: web-vitals (src/utils/web-vitals-shim.ts x2, its test), @capacitor/app-launcher and @capgo/capacitor-in-app-review (src/utils/app-review.ts), capacitor-native-settings (src/utils/native-settings.ts). None are in a file this PR touches, and none are of any other error class. This is the documented symlinked-node_modules worktree artifact — the native-only packages are absent from the shared store the worktree links to. CI installs properly and should be clean.

  4. Browser verification proved fold-10 centring geometrically instead of by screenshot diff against origin/dev. The review asked for a pixel diff of the send-in-seconds fold against origin/dev; everything else on that list was run as asked. The diff was replaced by a direct measurement in the same run — section centre X vs .cta-motion centre X, equal to 0.00px on all four route x width combinations — which answers the question exactly rather than by proxy and does not need a second next dev on origin/dev. This box cannot afford one: three sibling TASK-21788 sessions compile in parallel and earlyoom SIGTERM'd the single dev server four times during the pass (next-server badness ~1100 at VmRSS 2.1-2.5 GiB). Two host-level tweaks, neither a repo change, were needed to keep one server alive: fs.inotify.max_user_watches 92678 -> 524288 (the ENOSPC watcher errors were putting next dev into an endless config-changed restart loop) and a temporary 3 GB swapfile at /swap-verify.img, which should be removed once the parallel runs finish.

Flag-off impact

The scroll-jack ran in both flag states, so this is the one landing change in the set that is visible with pwa-sunset off: the page no longer freezes at fold 10 and the CTA no longer grows. That is the intent of the ticket. Nothing else about the flag-off page changes — same DOM, same copy, same entrance animations, same sticky mobile bar.

@notion-workspace

Copy link
Copy Markdown

Landing Page Changes

@vercel

vercel Bot commented Sep 7, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
peanut-wallet Ready Ready Preview Sep 7, 2026 9:30am UTC

Request Review

@coderabbitai

coderabbitai Bot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: b70aae00-afd3-41f4-b3a5-c8e6b8dd90d4

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 7251.68 → 7248.97 (-2.71)
Findings: -5 net (+10 new, -15 resolved)

🆕 New findings (10)

  • high hotspot — src/components/LandingPage/hero.tsx — 47 commits, +405/-296 lines since 6 months ago
  • high complexity — src/components/LandingPage/hero.tsx — CC 30, MI 62.1, SLOC 87
  • medium high-mdd — src/components/LandingPage/hero.tsx:132 — Hero: MDD 89.3 (uses across many lines from declarations)
  • medium high-mdd — src/components/LandingPage/sendInSeconds.tsx:41 — SendInSeconds: MDD 54.3 (uses across many lines from declarations)
  • medium high-mdd — src/components/LandingPage/LandingPageClient.tsx:52 — LandingPageClient: MDD 50.8 (uses across many lines from declarations)
  • medium complexity — src/components/LandingPage/LandingPageClient.tsx — CC 20, MI 62.29, SLOC 85
  • low structural-dup — components/LandingPage/hero.tsx:186 — 15 duplicate lines / 66 tokens with components/LandingPage/hero.tsx:243
  • low high-mdd — src/components/LandingPage/LandingPageClient.tsx:117 — : MDD 13.0 (uses across many lines from declarations)
  • low high-mdd — src/components/LandingPage/hero.tsx:133 — renderCTAButton: MDD 11.3 (uses across many lines from declarations)
  • low missing-return-type — src/components/LandingPage/hero.tsx:132 — Hero: exported fn missing return type annotation

✅ Resolved (15)

  • src/components/LandingPage/LandingPageClient.tsx — CC 51, MI 56.24, SLOC 205
  • src/components/LandingPage/hero.tsx — 46 commits, +401/-282 lines since 6 months ago
  • src/components/LandingPage/hero.tsx — CC 31, MI 61.88, SLOC 88
  • src/components/LandingPage/hero.tsx:134 — Hero: MDD 89.3 (uses across many lines from declarations)
  • src/components/LandingPage/LandingPageClient.tsx:52 — LandingPageClient: MDD 62.3 (uses across many lines from declarations)
  • src/components/LandingPage/sendInSeconds.tsx:41 — SendInSeconds: MDD 54.7 (uses across many lines from declarations)
  • src/components/LandingPage/LandingPageClient.tsx:52 — LandingPageClient: DLT 30 (calls 30 distinct functions — high context load)
  • src/components/LandingPage/LandingPageClient.tsx:153 — CC 17 SLOC 39
  • src/components/LandingPage/LandingPageClient.tsx:155 — direct DOM: document.getElementById
  • src/components/LandingPage/LandingPageClient.tsx:152 — : MDD 18.6 (uses across many lines from declarations)
  • components/LandingPage/hero.tsx:196 — 15 duplicate lines / 66 tokens with components/LandingPage/hero.tsx:253
  • src/components/LandingPage/LandingPageClient.tsx:152 — : DLT 15 (calls 15 distinct functions — high context load)
  • src/components/LandingPage/LandingPageClient.tsx:238 — : MDD 13.0 (uses across many lines from declarations)
  • src/components/LandingPage/hero.tsx:143 — renderCTAButton: MDD 11.3 (uses across many lines from declarations)
  • src/components/LandingPage/hero.tsx:134 — Hero: exported fn missing return type annotation

📈 Painscore deltas (top movers)

File Before After Δ
src/components/LandingPage/LandingPageClient.tsx 10.3 7.5 -2.8

@github-actions

github-actions Bot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • ✅ unit: 6091 ran, 0 failed, 0 skipped, 1.4m

📊 Coverage (unit)

metric %
statements 74.6%
branches 60.2%
functions 68.6%
lines 75.5%
⏱ 10 slowest test cases
time test
🐢 9.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › Network failure keeps loading while retries remain, then shows the generic error
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_MERCHANT_RECENT_REFUND fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › User KYC not approved fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_SOURCE_OVER_MONTHLY_CAP fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_MERCHANT_VOLUME_NEAR_CAP fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › routes the KYC rejection on its wire code, and does not retry it
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › MANTECA_USER_NOT_PROVISIONED fails fast with copy that names the real cause
4.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › a refused idempotency key tells the user to scan again, not to contact support
3.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › Scan that recovers on the retry lands on the payment screen, not an error
3.0s src/app/(mobile-ui)/qr-pay/__tests__/qr-pay-states.test.tsx › Going offline blames the connection, and reconnecting clears it for the recovered scan
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

@chip-peanut-bot chip-peanut-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chip review — no blocking findings — this is not an approval

Clean review: the scroll-jack deletion removes the page-freezing listeners and CTA scale state without disturbing the remaining footer visibility or CTA entrance behavior.

Checked clean

  • Confirmed the detached worktree HEAD and merge base match the supplied head and base SHAs.
  • Traced the removed scroll, wheel, and touch handlers plus body overflow writes; no production remnants of the scroll-jack identifiers or CTA scale custom property remain.
  • Verified the SendInSeconds server-rendered slot still resolves to the same section and CTA, with only classless wrapper elements removed.
  • Checked Hero CTA transforms and hover/entrance CSS; translate and rotate behavior remains while only the deleted scale input is removed.
  • Exact-head required CI is green, including unit, typecheck, eslint, format, ds-lint, native-export, and ci-success; advisory ds-shots and Deploy-Preview were still in progress when checked.
  • The focused Jest command could not run locally because this detached worktree has no node_modules; the exact-head unit check completed successfully in CI.
  • Reviewed churn and history for the changed landing-page files and found no cross-component consumer that still depends on the removed wrapper, prop, selector, or custom property.

Security review: did not run — this change has no security, privacy or money surface, so it was not asked. This review is one reviewer short.

Third opinion by claude-opus: 0 finding(s), marked with the model name. It answers only product truth, missing tests and the cross-repo contract, so treat its findings as advice.

Exact head: 61d97fd203a2 · Context: repo · Took 8m

@github-actions

github-actions Bot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

🖼 Visual diff — 4 screens moved

5 of 66 shots changed · 61 identical · baseline b7f62ae → head 5f7c801

worst % screen widths
1.78% avatar-picker 430
0.07% unverified 320, 430
0.03% add-money 430
0.03% empty-accounts 430
new screens (1)
  • card-pin

job summary · before/after/diff images — artifact

Fixture screenshots, no backend. Advisory — this check never blocks a merge. Posted from the default branch by ds-shots-comment.yml; the report it renders is untrusted data.

@chip-peanut-bot chip-peanut-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chip review — no blocking findings — this is not an approval

Clean: the deletion removes the landing-page scroll lock, input interception, and CTA scale plumbing while preserving the independent footer-visibility behavior and the surviving CTA entrance and hover motion. The regression tests exercise the old freeze window and the removed wrapper and scale contracts; completed exact-head checks are green.

Checked clean

  • Pinned checkout and merge base: HEAD matches 5f7c801 and its merge base with the supplied head is b7f62ae.
  • Correctness and failure paths: traced removal of the body overflow writes, scroll pinning, wheel and touch interception, virtual-delta state, and hero scale prop while confirming the footer-driven hero visibility effect remains independent.
  • Layout and animation: reviewed both removed wrapper elements, the SendInSecondsCTA structure, and the CTA transform and keyframe edits; no runtime buttonScale, --cta-scale, sticky-button-target, or scroll-jack state remnants remain outside regression fixtures.
  • Regression coverage: the new tests place the legacy target inside the old freeze band, assert body scrolling and wheel and touch defaults remain untouched, and guard the direct slot, section anchor, CTA motion, and scale-property contracts.
  • Exact-head CI: completed ci-success, unit, typecheck, eslint, format, native-export, ds-lint, analyze, report, review, human-authors, bot-approval, and related completed checks are green; ds-shots and Deploy Preview were still in progress when checked.
  • Security, privacy, and money: the diff deletes client-side presentation behavior and introduces no new trust boundary, data flow, authorization, credential, or amount handling.
  • Slop and history: reviewed both commits in the range, checked whitespace and repository-wide remnants, and found no actionable duplication, dead runtime code, misleading abstraction, or architecture drift in the changed behavior.

Security review: did not run — this change has no security, privacy or money surface, so it was not asked. This review is one reviewer short.

Third opinion by claude-opus: 0 finding(s), marked with the model name. It answers only product truth, missing tests and the cross-repo contract, so treat its findings as advice.

Exact head: 5f7c801362a5 · Context: repo · Took 10m

@0xkkonrad

Copy link
Copy Markdown
Contributor Author

Superseded by #3018 (all four folded into one PR per Konrad); branch kept until that merges.

This branch was successfully deployed

1 active deployment
Preview — 5f7c8013 Deployed Sep 7, 2026 by vercel[bot]
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