Christmas Card Animation
Create a CSS-only Christmas card that opens and closes with a 3D fold animation — using perspective, rotateY, and backface-visibility.
Christmas Card Open & Close Animation
In this final episode of CSS Tips and Tricks, you will build a CSS-only Christmas card that opens and closes with a realistic 3D folding animation. The card will use perspective, rotateY, and backface-visibility to create the effect of a card being opened like a book.
The HTML Structure
<div class="card-scene">
<div class="card">
<div class="card-front">
<h2>🎄 Merry Christmas 🎄</h2>
<p>Open me!</p>
</div>
<div class="card-inside-left">
<p>Inside Left</p>
</div>
<div class="card-inside-right">
<h3>🎅 Happy Holidays! 🎅</h3>
<p>Wishing you warmth, joy, and lots of code!</p>
</div>
</div>
</div>
The card has three visible faces — the front cover, the inside-left (back of the cover), and the inside-right (the actual greeting). When the card opens, the front cover rotates to reveal the inside.
Setting Up 3D Space
.card-scene {
width: 400px;
height: 300px;
perspective: 1200px;
margin: 80px auto;
}
The perspective property on the scene container creates the 3D space. A value of 1200px gives a subtle, realistic depth. Lower values (like 400px) would create a more dramatic, fish-eye effect.
The Card Container
.card {
width: 100%;
height: 100%;
position: relative;
transform-style: preserve-3d;
cursor: pointer;
}
transform-style: preserve-3d is essential — it tells the browser that children of this element should be rendered in 3D space, not flattened. Without it, the rotateY on the front cover would not look three-dimensional.
The Inside Right (Background)
.card-inside-right {
position: absolute;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #ffecd2, #fcb69f);
border-radius: 8px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
box-sizing: border-box;
box-shadow: inset 0 0 30px rgba(0,0,0,0.05);
}
.card-inside-right h3 {
font-size: 28px;
margin: 0 0 12px;
color: #c0392b;
}
.card-inside-right p {
font-size: 16px;
color: #6d4c41;
text-align: center;
line-height: 1.6;
}
This is the greeting side — the message the recipient sees when the card is opened. It sits behind the front cover in 3D space.
The Inside Left (Back of Front Cover)
.card-inside-left {
position: absolute;
width: 50%;
height: 100%;
left: 0;
background: linear-gradient(135deg, #f5f7fa, #c3cfe2);
border-radius: 8px 0 0 8px;
display: flex;
align-items: center;
justify-content: center;
backface-visibility: hidden;
transform: rotateY(180deg);
color: #7f8c8d;
font-style: italic;
}
The inside-left is the back side of the front cover. It has backface-visibility: hidden so it only shows when the cover is flipped open. The rotateY(180deg) pre-rotates it so it appears correctly when the cover swings open.
The Front Cover
.card-front {
position: absolute;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #c0392b, #e74c3c);
border-radius: 8px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: white;
transform-origin: left center;
transition: transform 0.8s ease;
z-index: 2;
box-shadow: 2px 0 10px rgba(0,0,0,0.2);
}
.card-front h2 {
font-size: 28px;
margin: 0 0 8px;
}
.card-front p {
font-size: 14px;
opacity: 0.8;
}
The front cover is the red face of the card. The critical property is transform-origin: left center — this makes the cover hinge from the left edge, like a book or card being opened. The 0.8-second transition creates a smooth, natural opening motion.
Opening on Hover
.card:hover .card-front {
transform: rotateY(-160deg);
}
On hover, the front cover rotates -160 degrees around the Y axis from the left edge. This swings the cover open to the left, revealing the greeting inside. We use -160 degrees instead of -180 to leave the cover slightly angled, which looks more natural than a perfectly flat open position.
Adding Shadows for Depth
.card-front {
transition: transform 0.8s ease, box-shadow 0.8s ease;
}
.card:hover .card-front {
transform: rotateY(-160deg);
box-shadow: -5px 0 15px rgba(0,0,0,0.15);
}
.card-inside-right {
transition: box-shadow 0.8s ease;
}
.card:hover .card-inside-right {
box-shadow: inset 2px 0 8px rgba(0,0,0,0.1),
0 4px 20px rgba(0,0,0,0.1);
}
As the card opens, the shadow shifts to simulate a light source from the right. The inside-right panel also gains a subtle inner shadow, creating the illusion of depth inside the card.
Adding a "Pop-Out" Element Inside
.card-inside-right .popup-element {
transform: scale(0.5);
opacity: 0;
transition: transform 0.4s ease 0.5s, opacity 0.4s ease 0.5s;
}
.card:hover .card-inside-right .popup-element {
transform: scale(1);
opacity: 1;
}
Elements inside the card can have delayed transitions so they "pop" into view after the card finishes opening. The 0.5-second delay means the pop-out starts after the card is most of the way open.
Closing the Card
Since the transition is defined on the base .card-front state and not on :hover, moving the mouse away smoothly reverses the rotation. The cover swings closed and the front of the card returns to view.
Full Animation Sequence
| Phase | What Happens | Duration |
|---|---|---|
| Hover starts | Front cover begins rotating open from left edge | 0 – 0.8s |
| Cover opening | Shadow shifts and inside greeting becomes visible | 0.2 – 0.8s |
| Pop-out elements | Content inside scales up and fades in | 0.5 – 0.9s |
| Mouse leaves | Cover rotates back closed, shadows reset | 0 – 0.8s |
Key Takeaways
perspectiveon the parent andtransform-style: preserve-3don the card are both required for 3D effectstransform-origin: left centercreates a book-like hinge on the left edge of the coverbackface-visibility: hiddenprevents the back of elements from showing through during rotation- Using
rotateY(-160deg)instead of-180deglooks more natural — a slightly angled open position - Transitions on the base state (not
:hover) ensure the animation reverses smoothly when the mouse leaves - Delayed transitions on inner elements create a sequenced reveal — card opens first, then content appears
- Dynamic box-shadows that change during the animation add realistic depth and lighting cues