You will be able to automatically number headings, figures, or steps in CSS using counters, so content stays ordered when items are added, removed, or reordered.
What it is
CSS counters are named numeric variables that can be reset, incremented, and inserted into generated content. They are commonly used with pseudo-elements such as ::before and ::after. A counter is not a DOM element; it is a rendering feature that helps create labels like “Section 1”, “Figure 3”, or nested numbering like “2.1.3”.
Related terms: counter-reset, counter-increment, counter(), counters(), content, and ::before.
Why it matters
- Keeps numbering automatic when content changes.
- Reduces manual editing and mismatched numbers.
- Works with generated content for headings, figures, footnotes, and steps.
- Supports nested numbering for documents and technical writing.
- Separates presentation from content, leaving HTML cleaner.
Syntax or steps
- Choose a counter name, such as
section. - Reset it where numbering should start, often on a container.
- Increment it on each item that needs a number.
- Insert the value with
counter(section)insidecontent.
body {
counter-reset: section;
}
h2::before {
counter-increment: section;
content: "Section " counter(section) ": ";
}
Example
The following example numbers sections and subsections using nested counters.
<article>
<h2>Introduction</h2>
<h3>Background</h3>
<h3>Method</h3>
<h2>Results</h2>
<h3>Main Findings</h3>
</article>
article {
counter-reset: section subsection;
}
h2 {
counter-increment: section;
counter-reset: subsection;
}
h2::before {
content: "Section " counter(section) ": ";
}
h3 {
counter-increment: subsection;
}
h3::before {
content: counter(section) "." counter(subsection) " ";
}
Expected rendered headings:
- Section 1: Introduction
- 1.1 Background
- 1.2 Method
- Section 2: Results
- 2.1 Main Findings
Part-by-part: counter-reset creates counters and sets their starting value. counter-increment increases a counter when the selector matches. content inserts the counter value into the pseudo-element. Resetting subsection on each h2 restarts subsection numbering for each section.
Common mistakes
- Forgetting
counter-reset. If a counter is never reset, it may start at zero or inherit an unexpected value. Fix: reset it on a stable container such asbodyorarticle. - Incrementing the wrong element. If
counter-incrementis placed on a parent instead of each item, all children may share one number. Fix: increment on the element that represents one numbered item. - Using
counter()withoutcontent. Counters only appear in generated content. Fix: putcounter()inside a::beforeor::afterrule withcontent. - Assuming counters are accessible. Generated content may not be announced consistently by assistive technology. Fix: use real list markup or visible text when numbering is essential to meaning.
When to use it
Use CSS counters for visual numbering of headings, figures, or steps. Use HTML ordered lists when the numbering is part of the document structure or needs to be copied, printed, or read by assistive technology.
| Need | Use | Why |
|---|---|---|
| Automatic visual section numbers | CSS counters | Keeps HTML clean and updates when headings change. |
| Numbered steps or list items | <ol> |
Preserves list semantics and accessibility. |
| Complex nested numbering | CSS counters with counters() |
Can produce labels like 1.2.3 without JavaScript. |
| Numbers that must be selectable or copied | HTML text or list markup | Generated content is not always copied reliably. |
Practice
Guided exercise: Add a counter named figure to number images. Reset it on main, increment it on figure, and display “Figure 1:” before each figcaption.
main {
counter-reset: figure;
}
figure {
counter-increment: figure;
}
figcaption::before {
content: "Figure " counter(figure) ": ";
}
Challenge: Number ordered list items with CSS counters instead of the browser default. Hint: reset a counter on ol, increment it on li, and use li::before with content.
Quick check
Question: Why must counter(section) usually appear inside a content property?
Answer: Because CSS counters are inserted through generated content; without content, the counter value has no place to render.
Summary
CSS counters let you create automatic, presentation-level numbering by resetting, incrementing, and inserting named counters into generated content. They are useful for visual labels, but semantic numbering should often remain in HTML.