CSS counters — 5 lines of CSS replace 20 lines of row-numbering JS
Table of Contents
Today I learned CSS counters, and deleted about 20 lines of JavaScript row-renumbering in favour of 5 lines of CSS.
I had a table where rows are added and removed client-side, and every mutation called a renumber function that walked the rows and wrote 1, 2, 3... into a cell. The CSS version:
tbody { counter-reset: item; } /* create the counter, at 0 */
tbody > tr { counter-increment: item; } /* bump it once per row */
td.item-number::before { content: counter(item); } /* render it */
The number is never stored anywhere — it’s derived from document order. counter-reset instantiates the counter, counter-increment bumps it, and counter() is only readable from content, so only inside ::before / ::after.
For comparison, this is the shape of the JS it replaced — a renumber pass after every mutation:
function renumber() {
[...tbody.children]
.filter((tr) => !tr.classList.contains("hidden"))
.forEach((tr, i) => (tr.querySelector(".item-number").textContent = i + 1));
}
The bigger win behind those 5 lines: the responsibility for row numbering has moved out of our application logic and into the browser. No renumber function to write, call after every mutation, or keep in sync — the numbers are derived from the document itself, so they can never drift from the actual order. Less code to maintain, and a whole class of “the numbers are wrong” bugs that no longer exist.
Side effects & edge cases
Hidden rows drop out of the count automatically
An element that generates no box (e.g. display: none) can’t increment a counter, so the rest stay contiguous. Great for soft-delete UIs. (visibility: hidden does not do this.)
Numbers aren’t in the DOM
They’re generated content — there’s nothing to assert or go stale in tests.
Browser support
Counters are CSS 2.1 — supported since IE 8, so no compatibility floor to worry about.
Try it
Play with it below: the red column is pure CSS, the green column is the old JS. Add, remove, or hide rows and they stay in sync.