You will build a click-to-enlarge image modal using CSS positioning, a fixed overlay, and the :target pseudo-class.
What it is
An image modal is a temporary layer that appears above the page when a user clicks a thumbnail. In a CSS-only version, the thumbnail is an anchor link to an element with an id, and the modal is shown when that id matches the URL fragment using :target. The overlay uses position: fixed and inset: 0 to cover the viewport, while display: grid and place-items: center center the enlarged image.
Related terms: lightbox, overlay, backdrop, fixed positioning, modal dialog.
Why it matters
- Shows full-size images without leaving the current page.
- Works without JavaScript for simple galleries or demos.
- Keeps layout stable because the overlay is removed from normal flow.
- Provides a reusable pattern for popovers, dialogs, and image previews.
- Uses a small amount of CSS and clear HTML structure.
Syntax or steps
- Create a thumbnail link:
<a href="#modal">. - Create a modal container with
id="modal"andclass="modal". - Hide it by default with
display: none. - Show it when targeted with
.modal:target { display: grid; }. - Use
position: fixed,inset: 0, and a semi-transparent background. - Add a close link that points to
#or another empty fragment.
Example
<style>
.gallery {
display: grid;
gap: 1rem;
}
.thumb {
width: 120px;
cursor: zoom-in;
}
.modal {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.8);
display: none;
place-items: center;
padding: 1rem;
}
.modal:target {
display: grid;
}
.modal img {
max-width: 90vw;
max-height: 80vh;
border-radius: 8px;
}
.close {
position: absolute;
top: 1rem;
right: 1rem;
color: white;
font-size: 2rem;
text-decoration: none;
}
</style>
<div class="gallery">
<a href="#photo-modal">
<img class="thumb" src="photo.jpg" alt="Mountain view thumbnail">
</a>
</div>
<div id="photo-modal" class="modal">
<a href="#" class="close" aria-label="Close">×</a>
<img src="photo.jpg" alt="Mountain view full size">
</div>
Part-by-part explanation
The .gallery block contains the clickable thumbnail. The <a href="#photo-modal"> link changes the URL fragment to #photo-modal. The .modal element has that same id, so .modal:target becomes true and changes display: none to display: grid. The position: fixed and inset: 0 rules make the overlay cover the whole viewport. The place-items: center rule centers the enlarged image. The close link points to #, which removes the matching fragment and hides the modal.
Common mistakes
- Modal is always visible. If you omit
display: none, the overlay covers the page. Hide it by default and show only.modal:target. - Overlay does not cover the viewport.
position: absolutedepends on the nearest positioned ancestor. Useposition: fixedfor a true viewport overlay. - Close button does nothing. If the close link points to the same
id, the modal stays targeted. Point it to#or another sectionid. - Large images overflow. Set
max-widthandmax-heighton the modal image, and considerobject-fit: contain.
When to use it
A CSS-only modal is best for simple image previews. For accessible dialogs, JavaScript is usually better because it can manage focus, keyboard events, and ARIA state.
| Approach | Best for | Limitations |
|---|---|---|
CSS-only :target modal |
Simple galleries, demos, no-JavaScript pages | No focus trap, no ESC key, limited state |
| JavaScript modal | Accessible dialogs, forms, dynamic content | More code and event handling |
Practice
Guided exercise: Add a second thumbnail and modal with id="photo-modal-2". Give the second thumbnail href="#photo-modal-2". Expected output: clicking either thumbnail opens only its matching image.
Challenge: Add a caption below the enlarged image. Hint: make .modal use grid-template-rows: auto auto or place the image and caption in a column.
Quick check
Question: Why does .modal:target show the modal after clicking a thumbnail?
Answer: Because the thumbnail link changes the URL fragment to the modal’s id, and :target matches the element whose id equals that fragment.
Summary
A CSS image modal uses a hidden fixed overlay that becomes visible when its id matches the current URL fragment. The key pattern is position: fixed, inset: 0, and :target. This is useful for lightweight galleries, but JavaScript is better when accessibility and interaction state matter.