Episode 8 of 8

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

PhaseWhat HappensDuration
Hover startsFront cover begins rotating open from left edge0 – 0.8s
Cover openingShadow shifts and inside greeting becomes visible0.2 – 0.8s
Pop-out elementsContent inside scales up and fades in0.5 – 0.9s
Mouse leavesCover rotates back closed, shadows reset0 – 0.8s

Key Takeaways

  • perspective on the parent and transform-style: preserve-3d on the card are both required for 3D effects
  • transform-origin: left center creates a book-like hinge on the left edge of the cover
  • backface-visibility: hidden prevents the back of elements from showing through during rotation
  • Using rotateY(-160deg) instead of -180deg looks 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