Skip to content

Fix ripple effect ignoring component shape and extending into margins#3324

Merged
tonidero merged 3 commits into
RevenueCat:external/bahdaniec/fix/ripple_clippingfrom
bahdaniec:fix/ripple_clipping
Apr 15, 2026
Merged

Fix ripple effect ignoring component shape and extending into margins#3324
tonidero merged 3 commits into
RevenueCat:external/bahdaniec/fix/ripple_clippingfrom
bahdaniec:fix/ripple_clipping

Conversation

@bahdaniec

@bahdaniec bahdaniec commented Apr 10, 2026

Copy link
Copy Markdown
Contributor

Motivation

Resolves #3321

The ripple effect in Paywalls V2 ignores rounded corners and extends into the component's margin area. This happens because Modifier.clickable() is applied before StackComponentView applies .clip(shape) and margin internally — so the ripple fills the entire rectangular area including margins.

Description

Introduces an interactionModifier parameter on StackComponentView that gets threaded down to MainStackComponent and applied after .clip(composeShape) in all 4 code paths (standard, video background, nested badge, overlay). This ensures the ripple is bounded by the component's actual shape.

Updated all callers:

  • ButtonComponentView — moves clickable into interactionModifier
  • PackageComponentView — moves conditional clickable into interactionModifier, removes unused conditional import
  • TabControlButtonView — moves clickable into interactionModifier

Also adds a ButtonComponent to the bless sample paywall (#8) to make it easier to reproduce and verify the fix visually.

Testing: All existing unit tests pass — StackComponentViewTests, StackComponentViewWindowTests, ButtonComponentViewTests, PackageComponentViewTests, TabsComponentViewTests. Verified visually via the updated sample paywall.


Note

Medium Risk
Touches the core StackComponentView container and changes where click/interaction modifiers are applied, which could subtly affect touch targets and interaction behavior across multiple paywall components. Functionality is UI-scoped (ripple/press feedback) with no data/auth implications.

Overview
Fixes Compose ripple/press feedback on paywall components so it respects rounded corners and does not extend into margins by adding an interactionModifier to StackComponentView and applying it after clip(shape) across all rendering paths (standard, video background, nested badge, overlay/badge variants).

Updates callers (ButtonComponentView, PackageComponentView, TabControlButtonView) to pass their Modifier.clickable via interactionModifier (instead of on the outer modifier), and tweaks the paywall tester sample (#8) to use a ButtonComponent to make the issue easy to reproduce/verify.

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

bahdaniec and others added 2 commits April 10, 2026 10:45
Move Modifier.clickable() from callers into StackComponentView via a new
interactionModifier parameter, applied after .clip(shape) so the ripple
respects rounded corners and stays within component bounds.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace CTA StackComponent with a ButtonComponent in the bless sample
paywall to make it easier to reproduce and verify the ripple fix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@bahdaniec bahdaniec requested a review from a team as a code owner April 10, 2026 08:51
@tonidero

tonidero commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

Thank you so much for your contribution @bahdaniec! This looks good! We will get this merged and should be part of the next SDK release 👍

@tonidero tonidero changed the base branch from main to external/bahdaniec/fix/ripple_clipping April 15, 2026 10:31
@tonidero tonidero merged commit 0aa0dfc into RevenueCat:external/bahdaniec/fix/ripple_clipping Apr 15, 2026
3 checks passed
matteinn pushed a commit to matteinn/purchases-android that referenced this pull request May 5, 2026
…evenueCat#3395)

## Summary
- Follow-up to RevenueCat#3324, which fixed the original ripple-vs-margin issue
(RevenueCat#3321) by adding `.clip(composeShape)` on the click target so the
ripple respects rounded corners. That fix uses a graphics-layer clip,
which also crops descendants — stacks with nested children that
intentionally extend outside the parent (badges with offsets, drop
shadows, decorative elements) get visibly clipped during press.
- Introduces declarative `onStackClick: (() -> Unit)?`, `enabled:
Boolean`, `interactionSource: MutableInteractionSource?` parameters on
`StackComponentView`, threaded through `StackWithOverlaidBadge`,
`StackWithLongEdgeToEdgeBadge`, `StackWithShortEdgeToEdgeBadge`, and
`MainStackComponent`. This lets `MainStackComponent` split the click
gesture from the indication.
- **Plain stack path:** wraps in a `Box {}` containing the stack with
`clickable(indication = null)` (gesture only, no clip on the stack) and
a sibling
`Box(Modifier.matchParentSize().padding(margin).clip(composeShape).indication(source,
LocalIndication.current))` that draws the shape-clipped ripple. Children
render unclipped.
- **Other render paths** (video background, nested badge, overlay) keep
their existing `.clip(composeShape)` on the click target — only the
click wiring is swapped over to the new `onStackClick` parameter. The
overflow-clip fix therefore only applies to the plain render path; this
limitation is documented in the `onStackClick` kdoc so future callers
know what to expect.
- Updates the three call sites (`ButtonComponentView`,
`PackageComponentView`, `TabControlButtonView`) to use the new
declarative API.
- Adds a Compose preview
(`StackComponentView_Preview_Clickable_With_Overflowing_Child_Shadow`)
as a regression guard for the overflowing-child shadow case.

## Test plan
- [x] `./gradlew :ui:revenuecatui:testDefaultsBc8DebugUnitTest` (full
suite + Paparazzi `verify`) — green
- [x] `./gradlew detektAll` — green
- [x] Manual: build a paywall whose plain stack has a nested child
positioned to extend outside the parent (offset, negative padding, or
drop shadow); tap the parent — overflowing child must remain fully
visible during the press, and the ripple must follow the parent's
rounded corners

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches core Compose layout/click/semantics for paywall components, so
regressions could affect tap handling, accessibility, or visual clipping
across many paywalls despite being UI-only.
> 
> **Overview**
> Fixes clickable `StackComponentView` ripple rendering so the ripple
remains clipped to the stack shape **without clipping overflowing
children** (e.g. shadows/badges) by splitting gesture handling and
indication onto separate sibling nodes.
> 
> Introduces a new declarative click API on `StackComponentView`
(`onStackClick`, `enabled`, `interactionSource`) and updates existing
button/tab/package components to use it instead of applying
`Modifier.clickable` externally; adds Compose previews as regression
guards. The paywall-tester sample paywall is also updated to use a
`ButtonComponent` for its CTA.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
2471d57. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Siaržuk Bahdaniec <sweid531@gmail.com>
Co-authored-by: Claude Opus 4.6 (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.

Ripple effect ignores component shape and extends into margins

2 participants