CSS Counter Generator
Configure CSS counters for ordered lists and section numbering. Choose counter name, style, increment, nesting levels, and separator — get a live preview and copy-ready CSS.
Configuration
e.g. . → 1.2.
Live Preview
Rendered using the generated CSS (always with ol / li)
- Item text
- Item text
- Item text
- Item text
- Item text
- Item text
- Item text
- Item text
- Item text
Generated CSS
ol {
counter-reset: item;
list-style: none;
padding-left: 0;
}
ol ol {
counter-reset: item;
}
li::before {
counter-increment: item;
content: counters(item, ".", decimal) " ";
}Your documentation has nested headings — Chapter 1, Section 1.1, Subsection 1.1.1 — and manually numbering them breaks every time you reorder a section. An <ol> handles flat lists, but nested “1.2.3” numbering across heading levels requires CSS counters with counter-reset and counter-increment on different selectors. The syntax is finicky enough that most developers give up and number manually.
Why This Tool (Not Hand-Writing the CSS)
CSS counters require coordinating three properties (counter-reset, counter-increment, content) across multiple selectors with the right nesting hierarchy. Getting the reset/increment placement wrong produces duplicate numbers or skipped counts. This tool lets you configure the counter name, numbering style (decimal, Roman, alpha, Greek), increment value, nesting levels, and separator — then generates the complete CSS with a live preview. Everything runs in your browser; no data is sent anywhere.
What Are CSS Counters?
CSS counters are variables maintained by the browser that can be incremented and displayed using CSS — no JavaScript required. They are the native CSS mechanism for numbering ordered lists, chapter/section headings, table rows, footnotes, and any other sequence of repeating elements.
The three core CSS properties are:
counter-reset— creates a counter and sets it to a starting value (default 0)counter-increment— increments a counter by a specified amount (default 1)content— displays the counter value via the::beforeor::afterpseudo-element
A minimal counter looks like this:
ol {
counter-reset: item;
list-style: none;
}
li::before {
counter-increment: item;
content: counter(item) ". ";
}
Every <li> inside the <ol> gets a sequentially numbered label — generated entirely by CSS.
counter() vs counters()
CSS provides two functions for reading counter values:
counter(name, style?)
Reads the innermost instance of the named counter. Used for flat (non-nested) sequences:
li::before {
counter-increment: section;
content: counter(section, upper-roman) ". ";
}
counters(name, separator, style?)
Reads all nested instances of the counter, separated by the given string. This is the key function for creating hierarchical numbering like 1.2.3:
li::before {
counter-increment: item;
content: counters(item, ".", decimal) " ";
}
The browser automatically maintains separate counter instances for each nested list, so a deeply nested <li> shows its full path (e.g., 3.2.1).
Counter Styles
The counter() and counters() functions accept any CSS <list-style-type> keyword as their second (or third) argument:
| Keyword | Example output |
|---|---|
decimal | 1, 2, 3, 4 |
decimal-leading-zero | 01, 02, 03, 04 |
lower-roman | i, ii, iii, iv |
upper-roman | I, II, III, IV |
lower-alpha | a, b, c, d |
upper-alpha | A, B, C, D |
lower-greek | α, β, γ, δ |
none | (no output) |
@counter-style (CSS3) lets you define completely custom styles — arbitrary strings, image markers, symbolic sequences — but the common keywords above cover virtually every real-world use case.
Common Use Cases
Nested Ordered Lists
Replace the browser’s default list numbering with full hierarchical labels (1, 1.1, 1.1.1):
ol {
counter-reset: item;
list-style: none;
padding-left: 0;
}
ol ol {
counter-reset: item;
}
li::before {
counter-increment: item;
content: counters(item, ".") " ";
}
Section / Chapter Headings
Number document sections automatically without touching HTML or JavaScript:
body {
counter-reset: h2counter;
}
h2 {
counter-reset: h3counter;
}
h2::before {
counter-increment: h2counter;
content: counter(h2counter) ". ";
}
h3::before {
counter-increment: h3counter;
content: counter(h2counter) "." counter(h3counter) " ";
}
Legal / Outline Numbering
Use upper-roman for top-level and upper-alpha for sub-sections (I.A, I.B, II.A…):
.outline {
counter-reset: roman;
}
.outline > li::before {
counter-increment: roman;
content: counter(roman, upper-roman) ". ";
}
.outline > li > ol {
counter-reset: alpha;
}
.outline > li > ol > li::before {
counter-increment: alpha;
content: counter(roman, upper-roman) "." counter(alpha, upper-alpha) " ";
}
Table of Contents
Auto-number TOC entries in print stylesheets:
@media print {
.toc {
counter-reset: toc;
}
.toc-item::before {
counter-increment: toc;
content: counter(toc) ". ";
}
}
How counter-reset Works
A common point of confusion: counter-reset: item sets the counter to 0, not 1. The first counter-increment: item then brings it to 1. So the sequence 1, 2, 3 comes naturally from the default configuration.
To start from a different value — say 5 — you set counter-reset: item 4 (one less than the desired first value).
To count downward from 10: counter-reset: item 11 and counter-increment: item -1.
Nesting and Scope
Each new element that has counter-reset creates a fresh instance of that counter. Nested lists work because:
- The outer
<ol>resets counteritemto 0. - Each inner
<ol>also resets counteritemto 0, creating a new scope. counters(item, ".")reads all active instances from outermost to innermost.
This means nested lists count independently — the inner list’s counter does not affect the outer list’s counter.
Browser Support
CSS counters (counter-reset, counter-increment, counter()) are supported in every major browser since IE8. The counters() function (nested counters) is fully supported since Chrome 2, Firefox 1.5, Safari 3, and IE8.
The @counter-style rule for fully custom counter styles requires Chrome 91+, Firefox 33+, and Safari 17+. The keyword styles (decimal, lower-roman, etc.) used by this generator work everywhere.
Frequently Asked Questions
Do CSS counters require JavaScript? No. CSS counters are a purely CSS feature — no JavaScript is involved. The browser tracks and renders counter values natively.
Can I use CSS counters with elements other than <li>?
Yes. CSS counters work on any element. Common non-list uses include numbering <h2>/<h3> headings, <figure> captions, <dt> terms in definition lists, and table rows.
Why does my nested list show only one level of numbering?
You need counters() (with an “s”) instead of counter(). The counters() function shows the full hierarchical path by reading all active instances of the counter. Also confirm that each nested <ol> has its own counter-reset (either on ol ol or on the element directly).
How do I add a period after the counter without it appearing inside the content?
Include the period as part of the content string: content: counter(item) ". ". The space after the period separates it from the item text.
Can I start a counter at 0?
Yes: set counter-reset: item -1 and counter-increment: item (default +1). The first increment goes from −1 to 0, so the first item shows 0.
How do I skip a number in the sequence?
You can’t skip a number with standard counters. However, you can set counter-increment: item 0 on specific items to prevent incrementing them, effectively leaving them un-numbered.
Does counter-reset on a child element restart the parent counter?
No. counter-reset on a child element creates a new nested instance of that counter. The parent instance is unaffected and continues its sequence.