Recreating iOS App Icon and Open/Close Animation as a ReactJS Component

I want to recreate the iOS open and close animation for each app icon on my React website. Each App is rendered by an AppIcon component:

const Content = styled(animated.div)`
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(0);
z-index: 1;
background-color: white;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
`;

const AppIcon = ({ icon, name, content, isAppOpen, setIsAppOpen, swiperRef}) => {
    const[isOpen,setIsOpen] = useState(false);
    const app_icon = useRef();

    const closeApp = () => {
        if(isOpen)
        {
            setIsOpen(false);
            setIsAppOpen(false);
        }
    }

    const openApp = () => {
        if(!isAppOpen && !isOpen)
        {
            setIsAppOpen(true);
            setIsOpen(true);
        }
    }

    const contentSpring = useSpring({
        from: {
            position: 'absolute',
            z: -1,
            top: '50%',
            left: '50%',
            width: '0%',
            height: '0%',
        },
        to: {
            position: isOpen ? 'fixed' : 'absolute',
            z: isOpen ? 0 : -1,
            top: isOpen ? '0%' : '50%',
            left: isOpen ? '0%' : '50%',
            width: isOpen ? '100vw' : '0%',
            height: isOpen ? '100vh' : '0%' 
        },
        config: { duration: 300,
                tension: 280,
                friction: 30,
                mass: 0.2,
                clamp: true, },
        });

    return (
        <div className="app-icon-container">
            <div ref={app_icon} className="app-icon" onClick={openApp}>
                <img className="app-icon-image" src={icon} alt=""/>
            </div>
            <p className="app-icon-label">{name}</p>
            <Content style={contentSpring}>
                {isOpen && content}
                <div className="outer" onClick={closeApp}>
                <div className="inner">
                    <label>Back</label>
                </div>
                </div>
            </Content>
        </div>
    );
};

The component is styled with the following CSS:

.app-icon-container {
  position: relative;
  width: 80px;
  height: 120px;
  font-family: system-ui, -apple-system, BlinkMacSystemFont;
}

.app-icon {
  position: absolute;
  top: 0;
  left: 0;
  width: 80px;
  height: 80px;
  border-radius: 22.37%;
  background: transparent;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.2s ease-in-out;
  transition: filter 0.05s;
}

.app-icon:hover {
  filter: invert(0.2);
}

.app-icon-image {
  max-width: 100%;
  max-height: 100%;
  position: relative;
}

.app-icon-label {
  position: absolute;
  bottom: 0;
  left: 0;
  font-size: 16px;
  margin-top: 8px;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  width: 100%;
}

.content-div {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: #f2f2f2;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}


.outer {
  position: relative;
  margin: auto;
  width: 70px;
  margin-top: 200px;
  cursor: pointer;
}

.inner {
  width: inherit;
  text-align: center;
}

label { 
  font-size: .8em; 
  line-height: 4em;
  color: #000;
  transition: all .2s ease-in;
  opacity: 0;
  cursor: pointer;
}

.inner:before, .inner:after {
  position: absolute;
  content: '';
  height: 1px;
  width: inherit;
  background: #000;
  left: 0;
  transition: all .2s ease-in;
}

.inner:before {
  top: 50%; 
  transform: rotate(45deg);  
}

.inner:after {  
  bottom: 50%;
  transform: rotate(-45deg);  
}

.outer:hover label {
  opacity: 1;
}

.outer:hover .inner:before,
.outer:hover .inner:after {
  transform: rotate(0);
}

.outer:hover .inner:before {
  top: 0;
}

.outer:hover .inner:after {
  bottom: 0;
}

The isAppOpen and setIsAppOpen are state and setState variables passed in from the parent component, which is a fullscreen div. I use these variables to make sure only one app is open at a time on the website. I use the local isOpen and setIsOpen state variables and useSpring to handle the opening/closing animation of the app’s Content div. The html that goes into the Content div is passed in by the content variable from the parent. This part in the Content div is just a fancy close button:

<div className="outer" onClick={closeApp}>
<div className="inner">
   <label>Back</label>
</div>
</div>

Now, my issue. The animation works but is janky looking and does not match the behavior of the iOS open/close animation. First, on open, the content div is expanding from the center of the screen rather than the center of the AppIcon. On close, the div is instantly re-positioned so that its top-left corner is center on the AppIcon and then it condenses in size.

What I ideally want to achieve is this: https://codepen.io/Colir/pen/pooMvzK. Here the icons and content divs are separated and vanilla JS is used to animate the content divs based on click EventListeners. They seem to be using getBoundingClientRect to calculate a transform from the app tile’s corners to the screen’s corners.

What would be the best way to go about doing this in my AppIcon component? I think my animation is janky because I’m switching between two different position types (absolute to fixed) using Spring. The rendered AppIcon component is slightly larger than the app’s icon image (120px by 80px) so that it contains the the icon image (80px by 80px) and name label. The Content div’s initial size is (0px by 0px) and when opened, is scaled to the size of the viewport. I use position: fixed to center the opened Content div on the screen.

Do I need to separate the Content div from AppIcon, and make it its own component? That way it’s position/size could be set relative to the full screen div rather than the AppIcon component. I’d keep an array of states in the parent for each AppIcon and corresponding Content div component. Should I forgo react-spring and use the getBoundingClientRect method (somehow integrate the Codepen I linked into the AppIcon and Content components)?