v154 · css · counter styles

CSS symbols()

Marking a list with your own symbols meant declaring a named @counter-style at the top level and referring to it by name — a global name, for something used in one place. symbols() builds an anonymous counter style inline, wherever a counter style is accepted.

concepts

  1. The five systems

    cyclic, numeric, alphabetic, symbolic and fixed applied to the same symbols and the same list, so the difference between them is a comparison instead of a paragraph. The interesting one is what each does when the list outruns the symbols.

  2. Inline against @counter-style

    The same result written both ways, with the boilerplate counted, plus the case that motivates the change: two components that each want a private counter style and would otherwise be fighting over one global namespace.

  3. In counter() and content

    symbols() is accepted by counter() and counters() too, so a nested outline can be numbered with your own symbols per level — built here as a live document tree.

why it shipped

@counter-style is a top-level at-rule with a global name, which makes it awkward for exactly the case it is most used for: one component, one list, one set of markers. The name has to be invented, kept unique across the whole stylesheet, and shipped whether or not anything still uses it — the same problems that made anonymous functions preferable to named ones for one-off callbacks.

symbols() is the inline form. It takes a list of strings and an optional system, and it can appear anywhere a counter style is accepted: list-style-type, the list-style shorthand, and inside counter() and counters().

the API

/* Cycles through the symbols; the default system. */
.steps { list-style-type: symbols("◆" "◇"); }

/* Counts in a positional system, so it keeps working past the list. */
.figures { list-style-type: symbols(numeric "0" "1"); }

/* Anywhere a counter style is accepted. */
.outline li::before {
  content: counters(item, ".", symbols(alphabetic "a" "b" "c")) " ";
}

references