0

I'm trying to create the material ripple effect with styled-components (which is unable to import the material web-components mixins). I want to stick with using the after element for the foreground effect, to keep the accesibility tree intact.

However, most notably on mobile, the ripple transition is causing reflow in the button's content. It would seem to happen because of the display change (from none to block), but I have tried some alternatives which don't share this artifact, and this side-effect is still present.

Here's my code (I'm using some props to set the ripple, but you can hard-set them if you want to reproduce): [Here was an outdated version of the code]

Thanks for the attention.

Edit: The bug only happens when I add a hover effect to the button, very weird. Below follows the link and a code sample (you will have to set a react repository in order to reproduce it, unfortunately)

https://github.com/Eduardogbg/ripple-hover-reflow-bug

import React, { useRef, useReducer } from 'react';
import ReactDOM from 'react-dom';
import styled from 'styled-components'

const ButtonBase = styled.button`
  cursor: pointer;

  width: 250px;
  height: 6vh;
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
  outline: none;
  position: relative;
  overflow: hidden;
  border-width: 0;

  background-color: cyan;

  :hover {
    filter: brightness(1.06);
  }

  ::after {
    content: '';
    pointer-events: none;
    
    width: ${({ ripple }) => ripple.size}px;
    height: ${({ ripple }) => ripple.size}px;

    display: none;
    position: absolute;
    left: ${({ ripple }) => ripple.x}px;
    top: ${({ ripple }) => ripple.y}px;

    border-radius: 50%;
    background-color: ${({ ripple }) => ripple.color};

    opacity: 0;
    animation: ripple ${({ ripple }) => ripple.duration}ms;
  }

  :focus:not(:active)::after {
    display: block;
  }
  
  @keyframes ripple {
    from {
      opacity: 0.75;
      transform: scale(0);
    }
    to {
      opacity: 0;
      transform: scale(2);
    }
  }
`

const rippleReducer = ref => (ripple, event) => {
  const { x, y, width, height } = ref.current.getBoundingClientRect()
  const size = Math.max(width, height) 
  
  return {
    ...ripple,
    size, 
    x: event.pageX - x - size / 2,
    y: event.pageY - y - size / 2
  }
}

const DEFAULT_RIPPLE = {
  size: 0,
  x: 0,
  y: 0,
  color: 'white',
  duration: 850
}

const Button = props => {
  const ref = useRef(null)

  const [ripple, dispatch] = useReducer(
    rippleReducer(ref),
    { ...DEFAULT_RIPPLE, ...props.ripple }
  )

  return (
    <ButtonBase
      ref={ref}
      className={props.className}
      ripple={ripple}
      onClick={event => {
        event.persist()
        dispatch(event)
      }}
    >
      {props.children}
    </ButtonBase>
  )
}

ReactDOM.render(
  <div style={{
    backgroundColor: 'red',
    width: '500px', height: '500px',
    display: 'grid',
    placeItems: 'center'
  }}>
    <Button>
      <span style={{ fontSize: '30px' }}>
        abacabadabaca
      </span>
    </Button>
  </div>,
  document.getElementById('root')
);
eduardogbg
  • 553
  • 4
  • 18
  • Please add rendered HTML - how would even begin to test this with what you've given here? Please read [how to create a minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). – disinfor Sep 22 '20 at 20:09
  • well, I don't see how the HTML could help. I did this way so it was a matter of copy-paste to reproduce in a react repo with styled-components. But I can just remove the react stuff and leave with the CSS, then it would be a matter of applying the class to a button. – eduardogbg Sep 22 '20 at 21:37
  • We shouldn't have to do work to test your code. The rendered HTML and CSS would allow us to test your button and then help you. – disinfor Sep 22 '20 at 22:03
  • sure, I know it's inconvinient to install the dependencies. I tried to boil it down to just CSS but it didn't happen. It possibly has to do with some JS I wrote. I'll post a minimal repo that reproduces it – eduardogbg Sep 22 '20 at 22:42
  • I couldn't reproduce the bug because it seems like it only happens when I add a hover effect. Wasn't expecting that. Will update the question – eduardogbg Sep 23 '20 at 00:23

1 Answers1

0

The problem seems to be related to this chromium bug that was supposedly solved a few years ago: Image moves on hover when changing filter in chrome

Setting transform: translate3d(0,0,0); looks like a fix, though my eye isn't pixel-perfect.

eduardogbg
  • 553
  • 4
  • 18