Using CSS to style thumbnails and captions
This article was first published on tjkdesign.com (01-23-2007).
The challenge:
- captions must be centered underneath their image
- captions may contain up to 3 lines of text
- elements must flow (number of image/caption pairs in a row depends on the width of the parent container)
- thumbnails may be of various widths
- text resizing must not break the layout
- markup must be semantic (no structural hack)
- markup must be "hook free" (no
id
, noclass
) - CSS must be plain and simple (no filters, no Conditional Comments)
- must be browser-friendly
Take a sneak peek
You can also check the following examples that use "zoom
", "-mox-inline-stack
", "table-cell
", etc.:
- using the bottom of the image as a base line
- using the bottom of the item as a base line
- displaying the items in a grid
- centering thumbnails (horizontally/vertically)
Let's look at this step by step.
The markup
I think a dl
(Definition List) is the most appropriate element to use here as we are dealing with a list of elements that are paired. I'll be using the dt
s to hold the thumbnails and the dd
s to hold the captions (you're free to argue this point).
Also, note that I have added a
elements as in real life these thumbnails would be linked to larger images.
<dl>
<dt><a href="#"><img src="img/00.jpg" alt="Teacups" /></a></dt>
<dd><em>Teacups</em>Pte de Vanves</dd>
<dt><a href="#"><img src="img/01.jpg" alt="Soldiers" /></a></dt>
<dd><em>Soldiers</em>Pte de Vanves</dd>
<dt><a href="#"><img src="img/02.jpg" alt="Paris facade" /></a></dt>
<dd><em>Paris facade</em></dd>
<dt><a href="#"><img src="img/03.jpg" alt="La Conciergerie" /></a></dt>
<dd><em>La Conciergerie</em></dd>
<dt><a href="#"><img src="img/04.jpg" alt="Pont Neuf" /></a></dt>
<dd><em>Pont Neuf</em> The oldest bridge in Paris</dd>
<dt><a href="#"><img src="img/05.jpg" alt="Jeanne d'Arc" /></a></dt>
<dd><em>Jeanne d'Arc</em> Near the Louvre</dd>
<dt><a href="#"><img src="img/06.jpg" alt="The Louvre" /></a></dt>
<dd><em>The Louvre</em></dd>
<dt><a href="#"><img src="img/07.jpg" alt="Place Vendôme" /></a></dt>
<dd><em>Place Vendôme</em></dd>
<dt><a href="#"><img src="img/08.jpg" alt="Sculpture" /></a></dt>
<dd><em>Sculpture</em></dd>
<dt><a href="#"><img src="img/09.jpg" alt="" />The Seine</a></dt>
<dd><em>The Seine</em> View from the left bank</dd>
</dl>
This is how it appears in the browser without any style applied
The concept
It is beneficial to understand how things work before applying the necessary styles, so some background colors and a border will help illustrate the "basics".
- Step 1: We apply background colors to
dt
s anddd
s and we set a border around thedl
, which helps us notice a few things:dt
s seem to be the same width as their parent container (thedl
), but browsers apply a left margin to thedd
s.- There is a gap below each image.
- Step 2: We've fixed both issues by using "
margin-left:0
" in thedd {...}
rule and "vertical-align:center
" in theimg {...}
rule. Note that we could have used a different fix for the images (see Doctypes issues), but I chose that one because it is the most useful for what comes later (centering the elements). - Step 3: It's time to give a width to these
dt
s anddd
s. Any value will do as long as we make these elements at least as large as the largest element they contain (it can be an image or a string of text that you do not want to wrap). For our example, 10.5em seems to be the perfect value as it creates some "room" that will keep these elements from getting to close to each other at small text-size value (the reason why I'm usingem
rather thanpx
here). - Step 4: We remove the border around the images and add "
float:left
" to ourdt,dd {...}
rule so that these elements flow next to each other. Note that the border around thedl
has now collapsed; this is because floats are removed from the elements flow. - Step 5: To make sure the
dl
wraps these floats we can apply one of these two declarations (I chose the latter as it is more browser-friendly):overflow:hidden
float:left
- Step 6: This is the secret weapon; setting proper margin values on the
DD
s does all the magic. I simply replace the "margin-left:0
" declaration we had with "margin:75px 0 0 -10.5em
". Note that the top margin equals the height of our images while the left margin has a negative value equal to the width of the element itself. - Step 7: Again, it is a margin value that lets us make sure thumbnails can "escape" (I don't want to use the term "clear" here) the ones with a two line caption, when displayed on multiple rows. So I add
margin-bottom:2.5em
to thedt {...}
rule. 2.5em should be just enough to pass underneath thedd
s containing a caption with two lines of text. - Step 8: We create a new rule,
em {display:block;},
to display the text within theem
elements on its own line. Note that because of this new line it is necessary to increase the space below thedt
s to make sure elements can freely flow to the left without being "caught" by a previousdd
. - Step 9: The last thing to do, besides removing the background and border declarations, is to center images and text. To do so, I simply use "
text-align:center
" in thedl {...}
rule.
The CSS rules
Short, plain and simple (note that some values must match.)
dl {
float: left;
text-align: center;
}
dt,
dd {
width: 10.5em;
float: left;
}
dt {
margin-bottom: 4em;
}
/* vertical space between the images */
dd {margin:75px 0 0 -10.5em;
}
img {
vertical-align: bottom;
border: 0;
}
With a few more rules we end up with a nice gallery of thumbnails and captions that flow.
Note that I am using the pseudo-classes :focus
, :hover
and :active
to display a red "border" around the images. This is to give a visual clue to users.
Special thanks go to:
- Ted Drake for letting me use his photos of Paris.
- Vicki Falkland for checking my grammar and spelling.