CardSwap Component
Relevant source files
The following files were used as context for generating this wiki page:The CardSwap component is a sophisticated UI module designed to display a stack of interactive cards that cycle through an automated animation sequence. It creates a 3D perspective effect where cards appear stacked behind one another, with the front-most card periodically dropping away and re-appearing at the back of the stack. This component is heavily reliant on the GSAP (GreenSock Animation Platform) library for creating and managing complex, timeline-based animations.
Its primary implementation is within the Hero widget, where it serves as a dynamic and visually engaging way to present key information about skills, work experience, and projects. The component is composed of a parent CardSwap container and child Card elements, allowing for flexible content within each card.
Sources: src/shared/ui/CardSwap/CardSwap.tsx, src/widgets/Hero/Hero.tsx:16, 172-359, package.json:20
Component Architecture
The system consists of two main React components: CardSwap and Card. They work together in a parent-child relationship to create the final effect.
Sources: src/shared/ui/CardSwap/CardSwap.tsx:10, 32
CardSwap (Parent Container)
This is the orchestrator component. It manages the state, positioning, and animation of all its Card children.
- It accepts configuration props to customize the animation and layout.
- It uses
React.Children.toArrayandReact.cloneElementto pass necessary props andrefs to each childCard. - It initializes and controls a GSAP timeline to execute the swap animation at a regular interval, managed by
setInterval. - It handles user interactions, such as pausing the animation on hover.
Sources: src/shared/ui/CardSwap/CardSwap.tsx:84-87, 107-110, 245-256
Card (Child Element)
This is a presentational component that acts as a container for the content of each card in the stack.
- It is a
forwardRefcomponent, allowing theCardSwapparent to get a direct reference to its DOM element for animation with GSAP. - It has base styles for positioning, 3D transformation, and appearance, which can be extended via the
customClassprop. - It accepts any valid React nodes as children, allowing for rich content like text, images, and even other components (e.g.,
ProjectsCarousel).
Sources: src/shared/ui/CardSwap/CardSwap.tsx:32-42, src/widgets/Hero/Hero.tsx:244-359
The following diagram illustrates the relationship between the components.
API and Configuration
The behavior and appearance of the CardSwap component are controlled through a set of props.
CardSwapProps
These props are passed to the main CardSwap component.
| Prop | Type | Default | Description |
|---|---|---|---|
width | number | string | 500 | The width of the component container and each card. |
height | number | string | 400 | The height of the component container and each card. |
cardDistance | number | 60 | The horizontal distance (x-axis and z-axis) between each card in the stack. |
verticalDistance | number | 70 | The vertical distance (y-axis) between each card in the stack. |
delay | number | 5000 | The time in milliseconds between each card swap animation. |
pauseOnHover | boolean | false | If true, the animation will pause when the mouse is over the component. |
onCardClick | (idx: number) => void | undefined | A callback function that fires when a card is clicked, passing the card's index. |
skewAmount | number | 6 | The amount of skewY (in degrees) applied to each card. |
easing | "linear" | "elastic" | "elastic" | The GSAP easing function to use for the animations. Determines animation physics. |
children | ReactNode | - | The Card components to be displayed. |
Sources: src/shared/ui/CardSwap/CardSwap.tsx:12-22, 89-98
CardProps
These props are passed to each child Card component.
| Prop | Type | Description |
|---|---|---|
customClass | string | Additional CSS classes to be applied to the card div for custom styling. |
...rest | React.HTMLAttributes<HTMLDivElement> | All other standard HTML attributes (e.g., onClick, role, tabIndex) are passed to the root div. |
Sources: src/shared/ui/CardSwap/CardSwap.tsx:24-26, 32-42
Animation System
The animation is powered by GSAP and orchestrated within a useEffect hook in the CardSwap component.
Initial Setup
On mount, the component performs the following setup:
- Creates an array of
refs, one for each childCard. - Calculates the initial position (
x,y,z) andzIndexfor each card using amakeSlothelper function. - Applies the initial position and skew to each card's DOM element using
gsap.set().
Sources: src/shared/ui/CardSwap/CardSwap.tsx:55-61, 107-116
The swap Animation Cycle
The core animation logic is encapsulated in the swap function, which is called repeatedly by setInterval.
This flowchart shows the sequence of events in a single swap animation.
The animation is managed by a single GSAP timeline (tl) for each cycle. Key moments in the timeline are marked with labels (promote, return) to precisely coordinate the movements of multiple cards.
pauseOnHover
If pauseOnHover is true, event listeners for mouseenter and mouseleave are added to the main container.
mouseenter: Pauses the current GSAP timeline and clears thesetIntervalto stop new animations from starting.mouseleave: Resumes the paused GSAP timeline and re-initializes thesetIntervalto continue the animation cycle.
Sources: src/shared/ui/CardSwap/CardSwap.tsx:171-186
Usage Example
The CardSwap component is used in the Hero widget to display three distinct cards.
// src/widgets/Hero/Hero.tsx:172-183
<CardSwap
cardDistance={30}
verticalDistance={50}
skewAmount={12}
delay={3000}
pauseOnHover={true}
width={500}
height={400}
>
{/* Card 1: Top Skills */}
<Card
onClick={(e) => {
e.stopPropagation();
navigate("/about#skills");
}}
// ...other props
>
{/* ... Card 1 Content ... */}
</Card>
{/* Card 2: Accenture */}
<Card
onClick={(e) => {
e.stopPropagation();
navigate("/about#experience");
}}
// ...other props
>
{/* ... Card 2 Content ... */}
</Card>
{/* Card 3: Projects Carousel */}
<Card
onClick={(e) => {
e.stopPropagation();
navigate("/projects");
}}
// ...other props
>
{/* ... Card 3 Content, including <ProjectsCarousel /> ... */}
</Card>
</CardSwap>
This example demonstrates:
- Configuration of the animation via props like
cardDistance,delay, andskewAmount. - Passing
onClickandonKeyDownhandlers to eachCardfor navigation usingreact-router-dom. - Embedding rich, interactive content inside each
Card, including text fromhomeContentand even other complex components likeProjectsCarousel.
Sources: src/widgets/Hero/Hero.tsx:172-359, src/shared/config/content.ts:4-38