CSS counter-set: Assigning Custom Counter Values
The CSS counter-set property provides a direct way to
modify the value of an active CSS counter without altering its nesting
or scoping structure. While CSS counters typically rely on automatic
step-by-step additions, counter-set allows developers to
assign any explicit, arbitrary integer to a counter sequence mid-flow.
This guide explains how counter-set operates, how it
interacts with existing scopes, and how to use it to inject custom
values into your document numbering.
Understanding
counter-set
CSS counters work using three core properties:
counter-reset to initialize a counter,
counter-increment to step through values, and
counter-set to force a counter to a specific integer.
The syntax for the property is straightforward:
counter-set: <counter-name> <integer>?;<counter-name>: The name of the counter you want to alter.<integer>: The target number to assign to the counter. This value can be positive, negative, or zero. If omitted, it defaults to0.
Multiple counters can be manipulated simultaneously within a single declaration by separating pairs with spaces:
counter-set: section-count 10 page-count 1;Modifying Counters Without Breaking Scope
The key distinction of counter-set lies in how it
handles counter scoping compared to counter-reset:
counter-resetcreates a new instance of the counter on the target element. If applied to a child element inside an existing list, it instantiates a nested counter, leaving the parent counter unchanged.counter-setseeks the existing instance of the counter in the current scope and overrides its current value. It does not spawn a new counter scope.
This behavior makes counter-set the appropriate choice
when you need to jump to a specific number or correct an existing
sequence without unintentionally creating hierarchical sub-lists.
Practical Implementation
Consider a scenario where an ordered list requires a non-standard jump in numbering, such as skipping from item 2 directly to item 50:
<ol class="custom-list">
<li>First item</li>
<li>Second item</li>
<li class="skip-ahead">Intermission or special entry</li>
<li>Subsequent item</li>
</ol>.custom-list {
list-style: none;
counter-reset: custom-counter 0;
}
.custom-list li {
counter-increment: custom-counter;
}
.custom-list li::before {
content: counter(custom-counter) ". ";
}
/* Assign an arbitrary number */
.custom-list li.skip-ahead {
counter-set: custom-counter 50;
}In this example:
- The first item increments the counter to
1. - The second item increments it to
2. - The third item matches
.skip-ahead, socounter-setforcescustom-counterto50. (Becausecounter-incrementalso applies here, order of evaluation placescounter-setaftercounter-incrementwhen on the same element, ensuring the rendered output reflects50). - The fourth item increments normally from the new baseline, rendering
as
51.
Order of Operations on the Same Element
When multiple counter properties appear on the same element, the browser applies them in a strict sequence:
counter-reset: Generates new counter scopes.counter-increment: Adjusts existing or newly generated counters by their specified offsets.counter-set: Overwrites the counter's value with the specified integer.
Because counter-set evaluates last, any arbitrary value
assigned via counter-set overrides any increment applied by
counter-increment on that identical element. Subsequent
elements then continue incrementing or calculating based on the newly
assigned number.