I'm trying to recreate a component I've mad ein pure css in a react tsx component, but im not sure how to proceed.
Here is the html/css that does exaclty what I want.
<body>
<div class="left-content">
<h1>Headings</h1>
<p>This section contains the headings and occupies the left 50% of the screen.</p>
</div>
<div class="container">
<ul id="cards">
<li class="card" id="card1">
<div class="card-body">
<h2>Card 1</h2>
</div>
</li>
<li class="card" id="card2">
<div class="card-body">
<h2>Card 2</h2>
</div>
</li>
<li class="card" id="card3">
<div class="card-body">
<h2>Card 3</h2>
</div>
</li>
<li class="card" id="card4">
<div class="card-body">
<h2>Card 4</h2>
</div>
</li>
</ul>
</div>
</body>
css:
body {
background-color: #2E3537;
font-family: 'Poppins', sans-serif;
}
:root {
--cards: 5;
--cardHeight: 87vh;
--cardTopPadding: 0em;
--cardMargin: 4vw;
}
.container {
width: 50%;
margin: 0;
position: absolute;
right: 0;
}
#cards {
list-style: none;
padding-left: 0;
display: grid;
grid-template-columns: 1fr;
grid-template-rows: repeat(var(--cards), var(--cardHeight));
gap: var(--cardMargin);
padding-bottom: calc(var(--cards) * var(--cardTopPadding));
margin-bottom: var(--cardMargin);
}
.card {
position: sticky;
top: 0;
padding-top: calc(var(--index) * var(--cardTopPadding));
}
#card1 .card-body {
background-color: #52B2CF;
}
#card2 .card-body {
background-color: #E5A36F;
}
#card3 .card-body {
background-color: #9CADCE;
}
#card4 .card-body {
background-color: #D4AFB9;
}
.card-body {
box-sizing: border-box;
padding: 30px;
border-radius: 50px;
box-shadow: 0 0 30px 0 rgba(0,0,0,0.3);
height: var(--cardHeight);
display: flex;
justify-content: center;
align-items: center;
transition: all 0.5s;
}
h2 {
font-size: 8em;
}
.left-content {
width: 50%;
float: left;
padding: 20px;
color: white;
box-sizing: border-box;
position:fixed;
}
.left-content h1 {
font-size: 3em;
}
.left-content p {
font-size: 1.5em;
}
and this is my attaempt at a reatc tsx component:
import React, { useEffect, useState } from 'react';
import { styled } from '@mui/system';
import { motion, useAnimation } from 'framer-motion';
import { useInView } from 'react-intersection-observer';
import placeholderImage from './placeholder.png'; // Assume you have a placeholder image
import './ThirdSection.css';
const BigRow = styled('div')({
backgroundColor: '#00E186',
color: 'white',
minHeight: '100vh',
display: 'flex',
flexDirection: 'row',
padding: '0',
margin: '0',
overflow: 'hidden',
});
const TextSection = styled('div')({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flex: '1',
width: '100%',
paddingTop: '60px', // Adjust padding to account for the fixed navbar
padding: '0',
margin: '0',
position: 'relative',
});
const StackedCardsContainer = styled('div')({
flex: '1 1 60%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '100vh',
// overflow: 'hidden',
margin: '20px',
position: 'relative',
});
const Image = styled(motion.img)({
width: '80%',
height: '80%',
objectFit: 'cover',
});
const TextContainer = styled('div')({
flex: '40%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
height: '100vh',
padding: '2rem',
textAlign: 'center',
});
const AnimatedText = styled(motion.h1)({
fontSize: '5rem',
fontWeight: 'bold',
});
const HorizontalLine = styled('hr')({
width: '100%',
borderTop: '1px solid white',
margin: '2rem 0',
});
const AnimatedH5 = styled('h5')({
fontSize: '1.5rem',
fontWeight: 'normal',
margin: '0rem 0',
});
const ThirdSection: React.FC = () => {
const [displayText, setDisplayText] = useState('');
const text = ' Subheading goes here.';
const controls = useAnimation();
const [ref, inView] = useInView({
threshold: 0.2,
triggerOnce: true,
});
useEffect(() => {
if (inView) {
controls.start('visible');
}
}, [controls, inView]);
const textVariants = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0, transition: { duration: 2 } },
};
useEffect(() => {
let index = 0;
const interval = setInterval(() => {
setDisplayText((prev) => {
if (index < text.length) {
return prev + text[index];
}
clearInterval(interval);
return prev;
});
index++;
}, 2);
return () => clearInterval(interval);
}, [text]);
return (
<BigRow ref={ref}>
<TextSection>
<TextContainer>
<AnimatedText
variants={textVariants}
initial="hidden"
animate={controls}
>
Big heading here
</AnimatedText>
<HorizontalLine />
<AnimatedH5>{displayText}</AnimatedH5>
</TextContainer>
<StackedCardsContainer>
<div className="container">
<ul id="cards">
<li className="card" id="card1">
<div className="card-body">
<h2>Card 1</h2>
</div>
</li>
<li className="card" id="card2">
<div className="card-body">
<h2>Card 2</h2>
</div>
</li>
<li className="card" id="card3">
<div className="card-body">
<h2>Card 3</h2>
</div>
</li>
<li className="card" id="card4">
<div className="card-body">
<h2>Card 4</h2>
</div>
</li>
</ul>
</div>
</StackedCardsContainer>
</TextSection>
</BigRow>
);
};
export default ThirdSection;
Where ThirdSection.css is the same stylesheet as before.
The main issue is that overflow:hidden is required for the bigrow parent, sinc otherwise the cards overflow ino the parent, but have overflow:hidden breaks the animation.
The partial solution is having overflow:scroll for the StackCardsContainer, but that doesn't look very good, as you have 2 scrolling bars, and also the textSection is then not fixed.
Any suggestions on how I can fix this? Any help would be greatly appreciated!
I've changed the code a bit to work the way the html one is working. Hope it helps.
Edited:
tsx
:
const BigRow = styled('div')({
height:'100%',
backgroundColor: '#00E186',
color: 'white',
display: 'flex',
flexDirection: 'row',
padding: '0',
margin: '0',
overflow: 'hidden',
});
const TextSection = styled('div')({
overflow: 'auto',
width: '100%',
position: 'relative',
});
const StackedCardsContainer = styled('div')({
flex: '1 1 60%',
alignItems: 'center',
justifyContent: 'center',
position: 'relative',
height:'100%'
});
const Image = styled(motion.img)({
width: '80%',
height: '80%',
objectFit: 'cover',
});
const TextContainer = styled('div')({
flex: '40%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
padding: '2rem',
textAlign: 'center',
position: 'fixed',
top: '50%',
left: '0',
transform:'translate(0, -50%)',
pointerEvents:"none",
});
const AnimatedText = styled(motion.h1)({
fontSize: '5rem',
fontWeight: 'bold',
});
const HorizontalLine = styled('hr')({
width: '100%',
borderTop: '1px solid white',
margin: '2rem 0',
});
const AnimatedH5 = styled('h5')({
fontSize: '1.5rem',
fontWeight: 'normal',
margin: '0rem 0',
});
export const ThirdSection: React.FC = () => {
const [displayText, setDisplayText] = useState('');
const text = ' Subheading goes here.';
const controls = useAnimation();
const [ref, inView] = useInView({
threshold: 0.2,
triggerOnce: true,
});
useEffect(() => {
if (inView) {
controls.start('visible');
}
}, [controls, inView]);
const textVariants = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0, transition: { duration: 2 } },
};
useEffect(() => {
let index = 0;
const interval = setInterval(() => {
setDisplayText((prev) => {
if (index < text.length) {
return prev + text[index];
}
clearInterval(interval);
return prev;
});
index++;
}, 2);
return () => clearInterval(interval);
}, [text]);
return (
<BigRow ref={ref}>
<TextSection>
<TextContainer>
<AnimatedText
variants={textVariants}
initial="hidden"
animate={controls}
>
Big heading here
</AnimatedText>
<HorizontalLine />
<AnimatedH5>{displayText}</AnimatedH5>
</TextContainer>
<StackedCardsContainer>
<div className="container">
<ul id="cards">
<li className="card" id="card1">
<div className="card-body">
<h2>Card 1</h2>
</div>
</li>
<li className="card" id="card2">
<div className="card-body">
<h2>Card 2</h2>
</div>
</li>
<li className="card" id="card3">
<div className="card-body">
<h2>Card 3</h2>
</div>
</li>
<li className="card" id="card4">
<div className="card-body">
<h2>Card 4</h2>
</div>
</li>
</ul>
</div>
</StackedCardsContainer>
</TextSection>
</BigRow>
);
};
css
:
html,body,#root,.App{
height: 100%;
}
body {
background-color: #2E3537;
font-family: 'Poppins', sans-serif;
}
:root {
--cards: 5;
--cardHeight: 87vh;
--cardTopPadding: 0em;
--cardMargin: 4vw;
}
.container {
width: 40%;
margin: 0;
position: absolute;
right: 0;
height: 100vh;
padding: 20px;
}
#cards {
list-style: none;
padding-left: 0;
display: grid;
grid-template-columns: 1fr;
grid-template-rows: repeat(var(--cards), var(--cardHeight));
gap: var(--cardMargin);
padding-bottom: calc(var(--cards) * var(--cardTopPadding));
margin-bottom: var(--cardMargin);
}
.card {
position: sticky;
top: 0;
padding-top: calc(var(--index) * var(--cardTopPadding));
}
#card1 .card-body {
background-color: #52B2CF;
}
#card2 .card-body {
background-color: #E5A36F;
}
#card3 .card-body {
background-color: #9CADCE;
}
#card4 .card-body {
background-color: #D4AFB9;
}
.card-body {
box-sizing: border-box;
padding: 30px;
border-radius: 50px;
box-shadow: 0 0 30px 0 rgba(0,0,0,0.3);
height: var(--cardHeight);
display: flex;
justify-content: center;
align-items: center;
transition: all 0.5s;
}
h2 {
font-size: 8em;
}
.left-content {
width: 50%;
float: left;
padding: 20px;
color: white;
box-sizing: border-box;
position:fixed;
}
.left-content h1 {
font-size: 3em;
}
.left-content p {
font-size: 1.5em;
}