CSS-Only Popup
Build a modal popup dialog with overlay using only CSS — triggered by the checkbox hack with smooth scale and fade transitions.
CSS-Only Popup
Modal popups are everywhere on the web — cookie notices, login forms, alerts. In this episode you will build a fully functional popup modal with an overlay using only CSS. No JavaScript required — we use the checkbox hack again to toggle visibility.
The HTML Structure
<input type="checkbox" id="popup-toggle" class="popup-checkbox">
<!-- Trigger Button -->
<label for="popup-toggle" class="popup-trigger">Open Popup</label>
<!-- Overlay + Modal -->
<div class="popup-overlay">
<div class="popup-modal">
<label for="popup-toggle" class="popup-close">×</label>
<h2>Welcome!</h2>
<p>This modal is built entirely with CSS.</p>
<label for="popup-toggle" class="popup-btn">Got it</label>
</div>
</div>
The hidden checkbox controls the popup state. The trigger button is a <label> that toggles the checkbox. The close button and "Got it" button are also labels for the same checkbox — clicking them unchecks it and closes the modal.
Hiding the Checkbox
.popup-checkbox {
display: none;
}
The Trigger Button
.popup-trigger {
display: inline-block;
padding: 12px 24px;
background: #3498db;
color: white;
border-radius: 6px;
cursor: pointer;
font-size: 16px;
transition: background 0.3s ease;
}
.popup-trigger:hover {
background: #2980b9;
}
The Overlay — Full-Screen Background
.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;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
}
The overlay covers the entire viewport with a semi-transparent dark background. It uses display: flex to center the modal both horizontally and vertically. Like the dropdown menu, we use opacity and visibility instead of display: none so it can be transitioned.
The Modal Box
.popup-modal {
background: white;
padding: 40px;
border-radius: 12px;
max-width: 400px;
width: 90%;
position: relative;
text-align: center;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
transform: scale(0.8);
transition: transform 0.3s ease;
}
.popup-modal h2 {
margin: 0 0 12px;
color: #2c3e50;
}
.popup-modal p {
margin: 0 0 24px;
color: #7f8c8d;
line-height: 1.6;
}
The modal starts at scale(0.8) so it can grow into its full size when shown, creating a "pop" entrance effect.
The Close Button
.popup-close {
position: absolute;
top: 12px;
right: 16px;
font-size: 28px;
color: #999;
cursor: pointer;
line-height: 1;
transition: color 0.2s ease;
}
.popup-close:hover {
color: #e74c3c;
}
The Action Button
.popup-btn {
display: inline-block;
padding: 10px 30px;
background: #27ae60;
color: white;
border-radius: 6px;
cursor: pointer;
font-size: 15px;
transition: background 0.3s ease;
}
.popup-btn:hover {
background: #219a52;
}
Showing the Popup on Checked
.popup-checkbox:checked ~ .popup-overlay {
opacity: 1;
visibility: visible;
}
.popup-checkbox:checked ~ .popup-overlay .popup-modal {
transform: scale(1);
}
When the checkbox is checked, the overlay fades in and the modal scales up from 0.8 to 1.0. The general sibling combinator (~) targets the overlay that comes after the checkbox in the HTML.
Closing by Clicking the Overlay
/* Make the overlay itself a label for the checkbox */
<label for="popup-toggle" class="popup-overlay">
<div class="popup-modal" onclick="event.stopPropagation()">
...
</div>
</label>
If you make the overlay a <label> for the checkbox, clicking anywhere on the dark background will close the popup. However, since we want clicks inside the modal to NOT close it, we need to prevent event propagation. In a pure CSS approach, you can wrap the overlay as a label but this requires careful structuring — the modal content should not be a label itself.
Alternative: Using :target for Popup
<a href="#popup">Open Popup</a>
<div id="popup" class="popup-overlay">
<div class="popup-modal">
<a href="#" class="popup-close">×</a>
<h2>Hello!</h2>
</div>
</div>
.popup-overlay:target {
opacity: 1;
visibility: visible;
}
The :target pseudo-class is another CSS-only approach. When the URL hash matches the element's ID, the popup becomes visible. Clicking a link with href="#" removes the hash and closes it.
Key Takeaways
- The checkbox hack combined with the general sibling combinator (
~) creates a toggle system for showing and hiding the overlay and modal - Use
opacity+visibilityfor animated show/hide — neverdisplay: nonewhich cannot be transitioned - Starting the modal at
scale(0.8)and transitioning toscale(1)creates a satisfying "pop" entrance - Both the close button and action button are labels for the same checkbox — clicking either one unchecks it and closes the popup
- The
:targetpseudo-class is an alternative approach that uses URL hash changes instead of checkboxes - A fixed overlay with
display: flexandalign-items/justify-content: centeris the easiest way to center a modal on the screen