Skip to content

feat(touchable-ripple): MD3 state-layer opacity via android_ripple alpha#4997

Draft
adrcotfas wants to merge 3 commits into
@adrcotfas/chore_rn86from
@adrcotfas/remove_state_layer_pressed
Draft

feat(touchable-ripple): MD3 state-layer opacity via android_ripple alpha#4997
adrcotfas wants to merge 3 commits into
@adrcotfas/chore_rn86from
@adrcotfas/remove_state_layer_pressed

Conversation

@adrcotfas

Copy link
Copy Markdown
Collaborator

Motivation

MD3 interaction state layers are an opaque color drawn at a fixed opacity (0.1 pressed, 0.08 hover). Previously the theme shipped a pre-baked stateLayerPressed color (onSurface at 0.1 alpha) because PlatformColor can't be alpha-manipulated in JS, so the opacity had to be baked into the token ahead of time.

React Native 0.86's android_ripple.alpha (react/react-native#56395) removes that limitation: the alpha can now be applied at draw time, even over a PlatformColor. This lets us drop the pre-baked theme color and give TouchableRipple ownership of the state-layer opacity, so any component can pass its own opaque state-layer color and get the correct pressed/hover opacity for free.

This PR does that in three steps:

  1. Remove stateLayerPressed from the theme. TouchableRipple now defaults its ripple to onSurface and applies the pressed opacity itself, so the workaround color is no longer needed.
  2. Rework the TouchableRipple API. The ripple color is treated as opaque and the MD3 pressed opacity is applied automatically (multiplied into the color's own alpha, matching android_ripple.alpha), so a transparent ripple color stays invisible on web too. The public surface is just rippleColor plus the raw background escape hatch. Web and native now share one TouchableRippleCommonProps type, and background is typed as PressableAndroidRippleConfig instead of Object.
  3. Make the FAB ripple variant-aware. Each FAB variant gets a stateLayer role (the container's on-color), resolved following the container and ignoring a contentColor override, and passed to TouchableRipple as rippleColor. The pressed/hover state layer now matches the variant (e.g. primary → onPrimary, tonalPrimary → onPrimaryContainer) instead of always using onSurface.

Test plan

lint, typescript and tests pass

Postponed until #4996 is merged

stateLayerPressed pre-baked onSurface at the pressed opacity because PlatformColor can't be alpha-manipulated in JS. RN 0.86's android_ripple alpha makes that workaround unnecessary, so the field is dropped from the theme. TouchableRipple now defaults its ripple to onSurface and applies the opacity separately.
Replace the rippleAlpha prop with the MD3 pressed opacity applied
automatically, so the public API is just an opaque rippleColor plus
the raw background escape hatch.

- Extract a shared TouchableRippleCommonProps type so web and native
  expose one API, and type background as PressableAndroidRippleConfig
  instead of Object.
- Multiply the opacity into the color's own alpha (matching Android's
  android_ripple.alpha) so a transparent rippleColor stays invisible
  on web; fill the pressed opacity into a custom background only when
  it doesn't set its own alpha.
Add a stateLayer role to each FAB variant (the container's on-color) and resolve it following the container, ignoring a contentColor override. Shell passes it as rippleColor so the pressed and hover state layers match the variant instead of always using onSurface.
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