Styling empty <figure>
elements
HTML’s <figure>
element is a really nice way to associate a caption with some content. It’s really easy to use! You simply put the content that needs captioning (an <img>
, say) inside a <figure>
element, and then you add a <figcaption>
element containing the caption:
<figure>
<img src=something alt=blahblahblah>
<figcaption>
blah blah blah
</figcaption>
</figure>
The caption can come before or after the content it’s captioning. This is also okay:
<figure>
<figcaption>
blah blah blah
</figcaption>
<img src=something alt=blahblahblah>
</figure>
When I’m writing a post, sometimes I don’t yet have the images I intend to include in it. I usually know something about the image, though, so I often end up writing <figure>
elements that only have <figcaption>
children:
<figure>
<figcaption>
blah blah blah
</figcaption>
</figure>
In addition to being invalid, this can end up looking pretty weird if the site’s layout assumes figures have non-caption content. So I used to add a <div>
to stand in for the image, like so:
<figure>
<div class=placeholder></div>
<figcaption>
Something about the image.
</figcaption>
</figure>
I’d then set the <div>
’s background image to be the image placeholder SVG I created for my site:
It recently occurred to me, though, that CSS is now powerful enough to handle this case without that <div>
, using a combination of the :has()
and :only-child
pseudo-classes.
The figure:has(figcaption:only-child)
selector selects <figure>
elements that contain only a <figcaption>
. Here’s how we can use this selector to add my image placeholder SVG:
figure:has(figcaption:only-child) {
width: 100%;
height: auto;
aspect-ratio: 4 / 3;
background-image: url(/images/placeholder.svg);
background-repeat: no-repeat;
background-position: center;
background-size: contain;
}
It’d be nice if the <figcaption>
appeared below the image, though, like it does when there’s a real image there. This is relatively easy to accomplish with a combination of relative positioning and tweaking some background properties:
figure:has(figcaption:only-child) {
position: relative;
background-position: top;
background-size: 90% 90%;
}
figure:has(figcaption:only-child) figcaption {
position: absolute;
bottom: 0;
left: 0;
right: 0;
}
There! Now we automatically get placeholder images for <figure>
elements that lack content.