Skip to content

Fade the multipage paywall header during page transitions#3525

Merged
vegaro merged 14 commits into
mainfrom
cesar/multipage-paywall-header-transition
Jun 4, 2026
Merged

Fade the multipage paywall header during page transitions#3525
vegaro merged 14 commits into
mainfrom
cesar/multipage-paywall-header-transition

Conversation

@vegaro

@vegaro vegaro commented Jun 2, 2026

Copy link
Copy Markdown
Member

Multipage paywall headers used to pop. When one page has a header and the next doesn't, the header vanished the instant the slide finished (or snapped in at the start). Now the header rides the slide.

The header fades with the page transition:

  • present → absent: the outgoing header fades out
  • absent → present: the incoming header fades in
  • both present, or neither: unchanged (no fade)

It rides the same animatable.value progress as the slide, read inside graphicsLayer { alpha }, so it stays in lock-step and adds no recompositions per frame. The old !toHasHeroImage special-case is dropped: the header now fades even when the incoming page has a hero image, instead of dropping abruptly.

Screen_recording_20260604_075517.mp4

Note

Low Risk
Paywall UI and transition animation only; layout workaround for leaving headers is localized but worth a quick visual pass on header↔no-header and header↔hero flows.

Overview
Multipage workflow paywalls now fade the header in sync with the slide when a step gains or loses a header, instead of popping at the end of the transition.

Header selection returns a WorkflowHeaderPresentation (step id + ENTERING / LEAVING / STABLE). Present→absent uses the outgoing header with LEAVING; absent→present uses the incoming with ENTERING; when both steps have headers, behavior stays STABLE with the existing outgoing/incoming pick rules. The old case that dropped the header immediately when navigating to a hero page without a header is replaced by a LEAVING fade.

Alpha follows the same transition progress as the slide via graphicsLayer (no per-frame recomposition). LEAVING headers are drawn as a Box overlay in main content—not in the scaffold header slot—so headerHeightPx on the incoming step is not set from the fading header (hero top inset stays correct).

Tests cover presentation selection (including the hero behavior change) and headerAlpha mapping.

Reviewed by Cursor Bugbot for commit f632931. Bugbot is set up for automated code reviews on this repo. Configure here.

vegaro and others added 5 commits June 2, 2026 06:53
…sitions

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace selectWorkflowHeaderStepId with selectWorkflowHeaderPresentation,
which returns a WorkflowHeaderPresentation(headerStepId, role) so callers
can drive fade animations for present<->absent header transitions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…enerate test

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@codecov

codecov Bot commented Jun 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.96%. Comparing base (c3e9a45) to head (f632931).
⚠️ Report is 5 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3525   +/-   ##
=======================================
  Coverage   79.96%   79.96%           
=======================================
  Files         370      370           
  Lines       15014    15014           
  Branches     2071     2071           
=======================================
  Hits        12006    12006           
  Misses       2167     2167           
  Partials      841      841           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
@vegaro vegaro force-pushed the cesar/multipage-paywall-header-transition branch from 396c88f to 67fa041 Compare June 2, 2026 14:39
vegaro and others added 3 commits June 2, 2026 17:32
Move the null checks into the when guard so the branch body smart-casts
fromInfo/toInfo, dropping the non-null asserts workaround and its
explanatory comment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vegaro vegaro marked this pull request as ready for review June 2, 2026 15:35
@vegaro vegaro requested a review from a team as a code owner June 2, 2026 15:35
@vegaro vegaro requested a review from facumenzella June 2, 2026 15:36

@facumenzella facumenzella left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Haven't manually tested it but looks legit. A video in the PR description would be amazing 👍

…dant null/header checks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
vegaro and others added 4 commits June 3, 2026 17:36
…hero top inset

When the outgoing step had a header and the incoming step is a hero (no header),
HeaderOverlayLayout was writing the LEAVING header's height into currentState.headerHeightPx.
The incoming hero step's ZLayer then used that non-zero value as its top inset instead of
the status-bar fallback, pushing its content down by the wrong amount.

Fix: when role is LEAVING, pass headerContent=null to PaywallComponentsScaffold so
HeaderOverlayLayout leaves headerHeightPx=0, and render the fading header as a Box
overlay inside the mainContent lambda instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ode in the common case

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b5fb3c4. Configure here.

…entState

When headerStepId differs from currentStepId (LEAVING fade, some STABLE cases),
header actions such as opening a package sheet were dispatching against the wrong
step's selection and sheet state. headerOnClick now uses headerState to match the
step whose header is actually rendered.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vegaro vegaro added this pull request to the merge queue Jun 4, 2026
Merged via the queue into main with commit 6fd2c5c Jun 4, 2026
38 checks passed
@vegaro vegaro deleted the cesar/multipage-paywall-header-transition branch June 4, 2026 06:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

2 participants