Animating a Pop-up
Build an animated pop-up modal from scratch — entrance and exit animations with overlay backdrop.
Animating a Pop-up
Let's build a real-world animated pop-up modal with a backdrop overlay, entrance animation, and exit animation — all with pure CSS and minimal JavaScript for toggling.
The HTML Structure
<button id="open-btn" class="btn">Open Pop-up</button>
<div class="popup-overlay" id="popup">
<div class="popup-box">
<button class="popup-close" id="close-btn">×</button>
<h2>Welcome! 🎉</h2>
<p>This is an animated pop-up modal built with CSS animations.</p>
<button class="btn popup-action">Got it!</button>
</div>
</div>
The Overlay CSS
.popup-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
/* Hidden by default */
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
}
.popup-overlay.active {
opacity: 1;
visibility: visible;
}
The Modal Box CSS
.popup-box {
background: #fff;
border-radius: 16px;
padding: 40px;
max-width: 420px;
width: 90%;
text-align: center;
position: relative;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
/* Entrance: start scaled down and moved down */
transform: scale(0.7) translateY(30px);
opacity: 0;
transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275),
opacity 0.3s ease;
}
.popup-overlay.active .popup-box {
transform: scale(1) translateY(0);
opacity: 1;
}
Styling the Close Button
.popup-close {
position: absolute;
top: 12px;
right: 16px;
background: none;
border: none;
font-size: 28px;
color: #999;
cursor: pointer;
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: background 0.2s ease, color 0.2s ease;
}
.popup-close:hover {
background: #f0f0f0;
color: #333;
}
The Action Button
.btn {
padding: 12px 28px;
background: #3498db;
color: white;
border: none;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: background 0.2s ease, transform 0.2s ease;
}
.btn:hover {
background: #2980b9;
transform: translateY(-2px);
}
.popup-action {
margin-top: 20px;
}
Modal Content Styling
.popup-box h2 {
font-size: 24px;
color: #2c3e50;
margin-bottom: 12px;
}
.popup-box p {
color: #666;
line-height: 1.6;
font-size: 15px;
}
The JavaScript (Minimal)
const popup = document.getElementById('popup');
const openBtn = document.getElementById('open-btn');
const closeBtn = document.getElementById('close-btn');
openBtn.addEventListener('click', () => {
popup.classList.add('active');
});
closeBtn.addEventListener('click', () => {
popup.classList.remove('active');
});
// Close when clicking overlay (outside the box)
popup.addEventListener('click', (e) => {
if (e.target === popup) {
popup.classList.remove('active');
}
});
The Animation Breakdown
| Element | Entrance | Exit |
|---|---|---|
| Overlay | Fades in (opacity 0 → 1) | Fades out (opacity 1 → 0) |
| Modal box | Scales up + slides up with elastic curve | Scales down + slides down |
Why This Works Without Keyframes
We use transitions here instead of keyframes because the pop-up has exactly two states (closed and open) toggled by a class. Transitions are simpler for binary state changes.
The cubic-bezier(0.175, 0.885, 0.32, 1.275) timing function creates an overshoot effect — the modal slightly overshoots its final scale before settling, giving it a bouncy, polished feel.
Key Takeaways
- Use
visibility+opacitytransitions (notdisplay) for smooth show/hide - The overlay fades while the modal box scales and slides independently
- An overshoot cubic-bezier curve gives the entrance a bouncy, premium feel
- JavaScript only toggles a CSS class — all animation logic stays in CSS
- Close on overlay click by checking
e.target === popup