Screenshot of a lightbox effect recreated in CSS3

A few years ago I wrote a blog article titled Lightbox Can Bite Me. I’ve since removed that post, replacing it with this new and improved “Lightbox in CSS3” technique: the code has changed, but my frustration with the original plugin has only deepened.

Everyone on the web is familiar with the Lightbox effect: one of the first popular JavaScript gallery plugins, variations on the idea quickly became ubiquitous. My frustration stemmed from that very ubiquity: many of my students use a Lightbox clone for their portfolios, but few ever bother to customize anything about the plugin. This lack of imagination becomes especially apparent when 20 of them show their portfolios at the same time during .

It’s my hope that recreating Lightbox in the simpler syntax of CSS will lead to more people customizing the effect for their own sites with more variability and creativity.

Before I begin, I should point out that this code has three small drawbacks:

  • First, it is best suited for relatively short pages, as the vertical position of the large image is based on the height of the content, not the dimensions of the browser window. Pages that extend past the bottom of the browser window will drive the large images down when they are displayed.
  • Second, it uses to center the page content, which is only supported in recent browsers.
  • Finally, for the purpose of simplicity all of the images used in the example are all the same size; it’s completely possible to use images of different aspect ratios, at the cost of more code.

I’ve covered many of the techniques I use here in previous articles: the markup is simply an evolved version of the very first simple CSS gallery I ever wrote about, while the :target selector has been covered for use in tab-based navigation and imagemaps.

The HTML is fairly simple:

<body id="core">
	<dl id="gallery">
			<a href="#pic1"><img src="false-creek-small.jpeg" alt="False Creek, Vancouver, British Columbia"></a>
		<dd id="pic1">
			<a href="#core"><img src="false-creek.jpeg" alt="False Creek, Vancouver, British Columbia"></a>
			<a href="#pic2"><img src="lake-louise-small.jpeg" alt="Lake Louise, Alberta, Canada"></a>
		<dd id="pic2">
			<a href="#core"><img src="lake-louise.jpeg" alt="Lake Louise, Alberta, Canada"></a>
			<a href="#pic3"><img src="wheat-field-small.jpeg" alt="Wheatfield, Saskatchewan, Canada"></a>
		<dd id="pic3">
			<a href="#core"><img src="wheat-field.jpeg" alt="Wheatfield, Saskatchewan, Canada"></a>

The difference between this markup and my original simple CSS gallery is the use of id attributes and links. Each <dd> element has an id, and inside each associated <dt> element is a link to that id. Finally, each large image is linked back to the id on the <body> element. These linked id’s are used to initiate and close the animation for the large gallery images.

The base CSS:

html {
	min-height: 100%;
	position: relative;
body {
	margin: 0; height: 100%; 
dl { float: left; }
dd a {
	background: #fff;
	display: block;
	transition: 4s box-shadow ease-in; 
dt { 
	margin-left: 1.2rem;
	width: 150px;
	margin-top: 1.2rem;
dt img { 
	width: 150px; height: 150px;
dd img {
	width: 640px; height: 427px;
dd {
	margin-left: 0;
	background: rgba(0,0,0,0);
	position: absolute;
	top: 0; bottom: 0;
	width: 100%;
	height: 100%;
	display: flex;
	align-items: center;
	justify-content: center;
	visibility: hidden;
}Not all this code is going to visibly affect our gallery yet. The interesting part is the position: relative on the <html> tag, which is required to correctly position our <dd> elements, which contain the large image versions. Those <dd> tags are placed in the top left corner of the browser window, and extend its full width, with the height decided by the content of the page. (If no content goes past the bottom edge, the height of the <dd> element is the height of the browser window).

Next, the CSS for the presentation of the large version of the images:

@keyframes photopresent { 
	0% { 
		width: 0;
		height: 0;
		opacity: 0;
	30% {
		width: 640px;
		height: 0;
		opacity: 0;
	60% {
		width: 640px;
		height: 427px;
		opacity: 0;
		margin: 20px; 
	100% {
		width: 640px;
		height: 427px;
		opacity: 1;
		margin: 20px;
dd:target {
	visibility: visible;
	background: rgba(0,0,0,0.75);
	transition: 1.5s background linear;
dd:target a {
	box-shadow: 0 0 8px 8px rgba(0,0,0,0.35);
dd:target a img {
	animation: photopresent 3s forwards;

Clicking on the thumbnail image will fade the body and bring in the associated large image in the sequence long-associated with the Lightbox JavaScript plugin. Because :target places the id in the URL, clicking on links will animate the associated large image in the gallery, which is really useful for highlighting pieces in a portfolio to a client.

Naturally it is possible to take this a lot further: adding code and content to allow captions to appear on rollover, reversing the animation when the large image is clicked rather than having it disappear, etc. There are many design options: it is my hope that this humble beginning might inspire you to start exploring the possibilities.

Photographs by Kenny Louie, BriYYZ and Laszlo Ilyes, licensed under Creative Commons and used with permission.