Styling Counters in CSS | CSS-Tricks


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: " ";
}
List of four items, the first two items are prefixed with an orange diamond marker and a blue diamond marker, respectively.

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: " ";
}
List of four items, the last item has a marker formatted as a snowflake emoji.

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: ". ";
}
  1. bread
  2. butter
  3. milk
  4. 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: ". ";
}
  1. bread
  2. butter
  3. milk
  4. apples
  5. 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: ". ";
}
  1. bread
  2. butter
  3. milk
  4. apples
  5. 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: " ";
}
List of five grocery items with the markers formatted as emoji numbers.

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: ") ";
}
  1. bread
  2. butter
  3. milk
  4. 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: "(" ")";
}
  1. bread
  2. butter
  3. milk
  4. 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 the system default.
  • bullets reads an unordered list. By default, cyclic systems are read as bullets
  • numbers reads the counter’s numeric value in the content language. By default, additivefixednumeric, and, symbolic are read as numbers.
  • words reads the counter representation as words.
  • spell-out reads the counter representation letter by letter. By default, alphabetic is read as spell-out.
  • <counter-style-name> It will use that counter’s speak-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.

Recent Articles

Related Stories

Leave A Reply

Please enter your comment!
Please enter your name here