Automatic Figure Numbering with CSS Counters

Learn about CSS counters, the figure element and how to combine both for automatically numbering figures in a web page.

Automatic-Figure-Numbering-with-CSS-Counters

When writing articles, blog posts, tutorials, magazine entries or anything else, you will often want to include some images, charts, photographs, or even videos and code snippets to illustrate your content.

That being said, you will most likely want to attach some kind of caption to these elements, and perhaps number them so your readers can keep track of your thoughts.

And that’s exactly what we are going to do in today’s tutorial: combining the usage of the <figure> element with CSS counters to make your inserted elements (especially images) sexy as hell!

The figure element

The <figure> element is intended to be used along with the <figcaption> element to mark up images, illustrations, photos, diagrams and code snippets among other things. Here is what the spec says about this element:

The figure element represents a unit of content, optionally with a caption, that is self-contained, that is typically referenced as a single unit from the main flow of the document, and that can be moved away from the main flow of the document without affecting the document’s meaning.

Here is the basic markup for a figure:

<figure>
	<img src="path/to/your/image.jpg" alt="" />
	<figcaption>Here is the legend for your image<figcaption>
</figure>

Here are a few notes regarding the figure element:

  • The <figcaption> element is optional
  • You can only have one <figcaption> element in a <figure> element
  • You can embed as many other elements as you want in a <figure> element
  • When dealing with an image, you can leave the alt attribute empty if you include a <figcaption> to prevent screen readers from reading twice the same content

For more information about the <figure> element, I recommend you this great article from HTML5Doctor. There is also this entry at Mozilla Developer Network and of course the official specification.

Examples

For example, if you want to show a snippet of code, you can do it this way with the <figure> element:

<figure>
	<code>body { background: white; }</code>
	<figcaption>Some illustrated code with figure<figcaption>
</figure>

Basically, instead of adding your images this way:


<img src="cute-kitty.jpg" alt="This is a cute kitty!" />

… you can do something like this:


<figure>
	<img src="cute-kitty.jpg" alt="" />
	<figcaption>This is a cute kitty!<figcaption>
<figure>

Browser support

The <figure> is part of the “new” HTML5 elements, which are not understood by a range of old browsers including Internet Explorer 8. Since you don’t want to make your layout explode because of this tutorial, I’d recommend you include a polyfill to support these elements.

The most known and used polyfill for HTML is html5shiv which you can embed directly from the Google CDN by adding this line into your files:

<!--[if IE lte 8]><script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->

Note, how we use IE-specific conditional comments to prevent other browsers and IE versions greater than 8 from loading this script.

If you wish to know the story behind the html5shiv polyfill, please read this wonderful blog post from Paul Irish.

CSS Counters

CSS Counters have to be one of the most unknown CSS properties in the whole range of properties there is. It makes automatic numbering possible exclusively through CSS, without the help of neither HTML nor JavaScript.

This module relies on two properties and one value:

  • counter-reset which is used to initialize and reset one or several counters
  • counter-increment which is used to increment one or several counters
  • counter() is a valid value for ::before and ::after pseudo-elements, accepting a counter name as parameter in order to display its value

Pretty straight forward, isn’t it? Basically, you initialize a counter with the name you want to the value you want (mostly 0) and you tell a given selector to increment this counter at each occurrence. This counter can then be displayed using CSS generated content and the style and location can be specified with the :before and :after pseudo-elements.

The most basic implementation of a CSS counter has to be this one:

/* 1. We initialize the counter */
body {
	counter-reset: myAwesomeCounter;
}

/* 2. We tell each occurrence of this element to increment the counter */
.myAwesomeElement {
	counter-increment: myAwesomeCounter;
}

/* 3. We display the value of the counter before the element */
.myAwesomeElement:before {
	content: counter(myAwesomeCounter);
}

Note: I lied when I said “2 properties and 1 value”, there is also a counters() value which is almost never used. Please refer to this entry at MDN for more information about it.

Example

Back to our case, shall we? We want to number our images so that they are prefixed by “Fig. 1 – …”, “Fig. 2 – …” and so on, right? Let’s do it simply.

.article {
	counter-reset: figures;
}

.figure {
	counter-increment: figures;
}

.figure figcaption:before {
	content: 'Fig. ' counter(figures) ' - ';
}

Those 3 lines of CSS are enough to number our figures automagically. Isn’t that awesome?

Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.

Wrapping everything

The basics

Now that we know how to use both the <figure> element and CSS Counters, it is time to make what we wanted to do: embellish our blog posts.

But before jumping into the code, wouldn’t it be cool if we could easily make floated or centered figures, just by adding a simple class? Sure, it would be. Let’s do this!

We will start by giving our figures some decent styles. Nothing too fancy, something simple and elegant enough to make a kind of frame to your images.

.figure {
	padding: 0.9em;
	border: 3px solid #f5bca8;
	background: #fff;
	margin: 0 auto 1em;
}

In order to horizontally center images and prevent them from breaking out of their container (the <figure> element), we need to add some rules to them (could as well be <video> or something else).

.figure img {
	margin: 0 auto;
	display: block;
	max-width: 100%;
}

Now the caption! We make it stand out a bit, change the typography and center it horizontally. But frankly the styling is up to you. Just remember a caption should be removed without too much hassle, so don’t write a wall of text.

.figure figcaption {
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 2px;
	font-size: 0.8em;
	padding: .5em;
	text-align: center;
	color: #fff;
	background: #f5bca8;
}

Numbering

Great, we still haven’t implemented the counter to number our figures. As we’ve seen in the previous section, it is very easy to do.

.article {
	counter-reset: figures;
}

.figure figcaption {
	counter-increment: figures;
}

.figure figcaption:before {
	content: 'Fig. ' counter(figures) ' - ';
}

If you don’t necessarily want to number your images, you can limit this to a given class on the parent element. Perhaps you’ll give your wrapper a .numbered-figures class so that it enables image numbering. Easy enough:

.numbered-figures 							{ counter-reset: figures; }
.numbered-figures .figure figcaption 		{ counter-increment: figures; }
.numbered-figures .figure figcaption:before { content: 'Fig. ' counter(figures) ' - '; }

Variations

We have the basics for our system, but we still haven’t set up a way to have floated figures across the page. We will then make two classes:

.figure-left {
	float: left;
	margin: 0 1em .5em 0;
	width: -webkit-min-content;
	width: -moz-min-content;
	width: min-content;
}

.figure-right {
	float: right;
	margin: 0 0 .5em 1em;
	width: -webkit-min-content;
	width: -moz-min-content;
	width: min-content;
}

For those of you who do not know min-content, it is a valid value for width, min-width, max-width, height, min-height and max-height among other properties include flexbox and grid layout.

In our case, we want the figure element to be as small as possible; basically, we want it to wrap around the image. Because <figure> is a block-level element, it stretches to the width of its parent (100%). We could set it to float: left or display: inline-block to make it collapse to the widest piece of content but if the caption happens to be wider than the image we have a problem.

We could hard code the width to the figure element depending on the image, but it is inflexible and non-responsive. That’s why we introduce the min-content value. To put it simple, it tells the <figure> element to reduce its width so that the image fits inside it perfectly even if the caption has to wrap.

This value is supported by Firefox 3+ with the -moz- prefix and Chrome 18+ with the -webkit- prefix. The unprefixed version is currently not supported by any browser but might be in the future so we leave it.

Non-supportive browsers behave as expected: no width is set, the floated <figure> element wraps around the widest element, either the image or the caption.

Note: there are other values similar to min-content like max-content, fit-content and available. Please refer to this entry at MDN or the working draft of CSS Intrinsic & Extrinsic Sizing Module Level 3for further information about these.

Last but not least, we need to change/remove the max-width value on images for floated figures. Either you want images to have their own size, and you need to set max-width to none, or you want to set a maximum width (which I recommend) and you define whatever you want:

.figure-right img,
.figure-left img {
	max-width: 300px; /* Adjust to suit your needs */
}

Small screens

To make sure floated figures don’t behave oddly on small screens, we need to override a couple of styles to make them full-width and horizontally centered. If you’re building your site using a mobile first approach, you’ll do it the other way but it doesn’t matter really.

@media (max-width: 767px) {
	.figure-left,
	.figure-right {
		float: none;
		margin: 0 0 1em 0;
		width: 100%;
	}

	.figure img {
		max-width: 100%;
	}
}

Usage

Using this is easy as a pie. Either you want an horizontally centered figure in which case you simply have to use the .figure class. Or — most likely — you want to float the figure either on the left or on the right in which case you have to use both the .figure class and a variation class (e.g .figure-left).


<figure class='figure'>
	<img src="path/to/your/image.jpg" alt="" />
	<figcaption>Here is the legend for your image<figcaption>
</figure>


<figure class='figure figure-left'>
	<img src="path/to/your/image.jpg" alt="" />
	<figcaption>Here is the legend for your image<figcaption>
</figure>


<figure class='figure figure-right'>
	<img src="path/to/your/image.jpg" alt="" />
	<figcaption>Here is the legend for your image<figcaption>
</figure>

Final words

That’s pretty much it guys, the only thing left to do is to implement this on your site. Please have a look at the associated demo to see what it looks like or see it live on my own site.

Thanks for reading and happy coding!

Kitty Giraudel

Non-binary trans accessibility & diversity advocate, frontend developer, author. Real life cat. She/her.

Stay in the loop: Get your dose of frontend twice a week

Fresh news, inspo, code demos, and UI animations—zero fluff, all quality. Make your Mondays and Thursdays creative!

Feedback 22

Comments are closed.
  1. Nice article!
    Btw can someone explain me how the figurecaption “:before” pseudo element is working on ie7 too?!

    As far as i know it’s not supported in old ie.

    thanks 😀

    • ::after and ::before pseudo-elements are not supported by Internet Explorer 7. 😉

  2. Thank you so much…
    tutorial in this website very nice for me…
    great ‘n amazing. thank you again… ^^,

  3. Hello,

    Tutorials with perfect timing by Tympanus is tremendous. I try to implement every tutorial in my projects. But it is some what critical when it comes to integrate with CMSes like WordPress.

    Could you publish a guide to help us integrate Tympanus articles like this or any other articles at Codrops with WordPress?

    Everytime, I try to convert these to WordPress, I stuck somewhere with no solution and help. Hope some day you could help us with a guide or something that help us to turn the snippets as WordPress plugins.

    Thanks!

  4. With ie10 in compatibility mode ie7, i see that pseudo-element of picturecaption.

    Testing with IEtester, ie7, i correctly don’t see the caption.

    oooooook.

  5. While the technique is fantastic as usual, it misses a fairly important point: figures are numbered to have a reference in the text. If the numbering automatic you risk automatic mismatched references.

    There are other use cases for CSS counters, the simples one being custom <ol> styling

    • Fair point. I thought about this yesterday.

      A simple way to fix this “issue” would be to give a specific class to figures you want to number and only increment the counter on these elements.

  6. One problem which I notice will come when you want to translate website to other language. It’s really bad when you have something hardcoded in files like CSS because it’s common file for each language version. Everything else is good.

  7. If you wat support for ie7 9 you can integrate this ie7.js (ie9.js)
    And also have support for the basic html element
    http://ie7-js.googlecode.com/svn/test/index.html
    Support for CSS media queries in older browsers
    https://code.google.com/p/css3-mediaqueries-js/
    And yes it is posible to integrate codrops tutorial in wordpress via custom fileds from specific js per post and specific css per post
    examples: http://www.zurita-bach.com/ciudadano-buelna-fotos-de-la-grabacion/
    http://www.zurita-bach.com/vivir-a-destiempo-parejas-disparejas/

  8. Well, this is nicely written and everything, but as Federico Brigante already pointed out, the tool you described is completely worthless by itself. Unless you have an ID of some kind and in the text you can say something similar to “…as can be seen in fig. …”, numbering images is not even worth those three simple lines. On the contrary even, if you’re not going to reference images, numbering them is even distracting, so this technique actually makes things worse

    I’d love to use a simple method like this, but unless you can also give some hints on how to do those vital references, I would actually discourage this usage of counters.

    • Even if I see what you mean, I don’t see the problem. What prevents you from doing “(see fig. X)” in your posts to reference to the figures?

    • The problem is, if you still have to write “(see fig. 3)” in your article, you lose the advantage of automatic numbering… When you move or insert images, you again have to change all references manually or you end up with wrong numbers. And since you have to do the references manually, it’s better to also do the labeling manually since then, it’s easier to see when you’ve forgotten something.

    • Ralf, I did some digging looking for more info about what your brought up and there simply ins’t any way to reference a figure within the new HTML5 set. What I did manage to come up with though is to expand this idea.

      Using a link tag with an anchor jump tag you should be able to have a click reference to the figure. Then by adding a class to the link you can reference the figure by reusing the figure counter. This means you can only reference the next upcoming figure, though you could use some inline CSS to affect the incrementing. If you needed a more flexible counter you could create your own separate counter for the links and then manage incrementing and decrementing those as needed.

    • That’s highly unfortunate, especially as it seems this is pretty close to a nice solution already… I’m not an expert on this, but would it be possible through JavaScript to get the actual value that these CSS counters applied to a specific element? That way and with IDs you could at least get close to what we want.

      This makes you think; with all those fancy semantic and similar tags, they’d have put more effort into references on the display level, too. 😉

  9. Very clear, descriptive article about one of the lesser-known amazing features of CSS. Unfortunately, now everyone knows my secrets! 😀

  10. I think you’re missing out on two features. First the ability to change to counter style. I saw in your demo, the decimal attribute and found that all list-style-types are available: Counter Styles

    And that with this you can create nested counters using the counters property which you state is useless. With the counters and figures you could make 1A and 1B (though not sure if nested figures works, but by applying some fancy css you can make it work).

  11. I’ve been reading up on this a little, recently, as I’m writing a tutorial with figures that could use numbering. I saw a comment on another post that advised strongly against CSS counters because they cannot be read by screen-readers. He advised using ordered lists instead. I’m not sure how figure numbers could be achieved with ordered lists though.