The system
descriptor
The symbols
or additive-symbols
descriptors define the characters used for the counter style, while system
says how to use them.
The valid system
values are:
cyclic
alphabetic
symbolic
additive
fixed
extends
cyclic
will go through the characters set on symbols
and repeat them. We can use just one character in the symbols
to mimic a bullet list:
@counter-style cyclic-example {
system: cyclic;
symbols: "⏵";
suffix: " ";
}
Or alternate between two or more characters:
@counter-style cyclic-example {
system: cyclic;
symbols: "🔸" "🔹";
suffix: " ";
}
fixed
will write the characters in symbols
descriptor just one time. In the last example, only the first two items will have a custom counter if set to fixed
, while the others will drop to their fallback
, which is decimal by default.
@counter-style multiple-example {
system: fixed;
symbols: "🔸" "🔹";
suffix: " ";
}

We can set when the custom counters start by appending an <integer>
to the fixed
value. For example, the following custom counter will start at the fourth item:
@counter-style fixed-example {
system: fixed 4;
symbols: "💠";
suffix: " ";
}

numeric
will numerate list items using a custom positional system (base-2, base-8, base-16, etc.). Positional systems start at 0, so the first character at symbols
will be used as 0, the next as 1, and so on. Knowing this, we can make an ordered list using non-decimal numerical systems like hexadecimal:
@counter-style numeric-example {
system: numeric;
symbols: "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "A" "B" "C" "D" "E" "F";
suffix: ". ";
}
- bread
- butter
- milk
- apples
alphabetic
will enumerate the list items using a custom alphabetical system. It’s similar to the numeric system but with the key difference that it doesn’t have a character for 0, so the next digits are just repeated. For example, if our symbols
are "A" "B" "C"
they will wrap to "AA", "AB", "AC"
, then BA, BB, BC
and so on.
Since there is no equivalent for 0 and negative values, they will drop down to their fallback.
@counter-style alphabetic-example {
system: alphabetic;
symbols: "A" "B" "C";
suffix: ". ";
}
- bread
- butter
- milk
- apples
- cinnamon
symbolic
will go through the characters in symbols
repeating them one more time each iteration. So for example, if our symbols
are "A", "B", "C"
, it will go “A”, “B”, and “C”, double them in the next iteration as “AA”, “BB”, and “CC”, then triple them as “AAA”, “BBB”, “CCC” and so on.
Since there is no equivalent for 0 and negative values, they will drop down to their fallback.
@counter-style symbolic-example {
system: symbolic;
symbols: "A" "B" "C";
suffix: ". ";
}
- bread
- butter
- milk
- apples
- cinnamon
additive
will give characters a numerical value and add them together to get the counter representation. You can think of it as the way we usually count bills: if we have only $5, $2, and $1 bills, we will add them together to get the desired quantity, trying to keep the number of bills used at a minimum. So to represent 10, we will use two $5 bills instead of ten $1 bills.
Since there is no equivalent for negative values, they will drop down to their fallback.
@counter-style additive -example {
system: additive;
additive-symbols: 5 "5️⃣", 2 "2️⃣", 1 "1️⃣";
suffix: " ";
}

Notice how we use additive-symbols
when the system is additive
, while we use just symbols
for the previous systems.
extends
will create a custom style from another one but with modifications. To do so, it takes a <counter-style-name>
after the extends
value. For example, we could change the decimal
counter style default’s suffix
to a closing parenthesis (")"
)`:
@counter-style extends-example {
system: extends decimal;
suffix: ") ";
}
- bread
- butter
- milk
- cinnamon
Per spec, “If a @counter-style
uses the extends
system, it must not contain a symbols
or additive-symbols
descriptor, or else the @counter-style
rule is invalid.”
The other descriptors
The negative
descriptor allows us to create a custom representation for a list’s negative values. It can take one or two characters: The first one is prepended to the counter, and by default it’s the hyphen-minus ("-"
). The second one is appended to the symbol. For example, we could enclose negative representations into parenthesis (2), (1), 0, 1, 2
:
@counter-style negative-example {
system: extends decimal;
negative: "(" ")";
}
- bread
- butter
- milk
- apples
The prefix
and suffix
descriptors allow us to prepend and append, respectively, a character to the counter representation. We can use it to add a character at the beginning of each counter using prefix
:
@counter-style prefix-suffix-example {
system: extends decimal;
prefix: "(";
suffix: ") ";
}
The range
descriptor defines an inclusive range in which the counter style is used. We can define a bounded range by writing one <integer>
next to another. For example, a range of 2 4 will affect elements 2, 3, and 4:
@counter-style range-example {
system: cyclic;
symbols: "‣";
suffix: " ";
range: 2 4;
}
- bread
- butter
- milk
- apples
- cinnamon
On the other hand, using the infinite
value we can unbound the range to one side. For example, we could write infinite
3 so all items up to 3 have a counter style:
@counter-style range-example {
system: alphabetic;
symbols: "A" "B" "C";
suffix: ". ";
range: infinite 3;
}
- bread
- butter
- milk
- apples
- cinnamon
The pad
descriptor takes an <integer>
that represents the minimum width for the counter and a character to pad it. For example, a zero-padded counter style would look like the following:
@counter-style pad-example {
system: extends decimal;
pad: 3 "0";
}
The fallback
descriptor allows you to define which counter style should be used as a fallback whenever we can’t represent a specific count. For example, the following counter style is fixed
and will fallback to lower-roman
after the sixth item:
@counter-style fallback-example {
system: fixed;
symbols: "⚀" "⚁" "⚂" "⚃";
fallback: lower-roman;
}
- bread
- butter
- milk
- apples
- cinnamon
Lastly, the speak-as
descriptor hints to speech readers on how the counter style should be read. It can be:
auto
Uses thesystem
default.bullets
reads an unordered list. By default,cyclic
systems are read asbullets
numbers
reads the counter’s numeric value in the content language. By default,additive
,fixed
,numeric
, and,symbolic
are read asnumbers
.words
reads the counter representation as words.spell-out
reads the counter representation letter by letter. By default,alphabetic
is read asspell-out
.<counter-style-name>
It will use that counter’sspeak-as
value.
@counter-style speak-as-example {
system: extends decimal;
prefix: "Item ";
suffix: " is ";
speak-as: words;
}
symbols()
The symbols()
function defines an only-use counter style without the need to do a whole @counter-style
, but at the cost of missing some features. It can be used inside the list-style-type
property and the counter()
and counters()
functions.
ol {
list-style-type: symbols(cyclic "🥬");
}
However, its browser support is appalling since it’s only supported in Firefox.