CSS @counter-style: Custom List Bullet Markers
The CSS @counter-style at-rule allows developers to
define custom counter styles for ordered and unordered lists, moving
beyond the default browser markers like numbers, discs, and roman
numerals. This article provides a straightforward guide to understanding
how @counter-style works, its core descriptor properties,
and how to implement it to create clean, reusable custom bullets and
numbering systems.
What Is the
@counter-style At-Rule?
Traditionally, styling list markers with custom characters, emojis,
or specialized counting systems required replacing the marker entirely
using ::before pseudo-elements. The
@counter-style at-rule solves this by allowing you to
define a named counter algorithm directly in your stylesheet and apply
it anywhere using the standard list-style-type property or
CSS counter functions.
Key Descriptors in
@counter-style
To build a custom marker system, @counter-style uses
several core descriptors:
system: Defines the logic used to generate markers from the counter value. Common values include:cyclic: Cycles through the provided symbols repeatedly (ideal for bullet markers).numeric: Treats symbols as digits in an positional numbering system.alphabetic: Interprets symbols like an alphabet (e.g., A-Z, AA-ZZ).fixed: Applies symbols only to a limited, specified range before stopping.symbolic: Repeatedly prints a symbol corresponding to the counter integer (e.g., *, **, ***).
symbols: Specifies the space-separated list of characters, strings, or emojis used as the marker symbols.suffix: Defines the content appended directly after the marker symbol (defaults to". "in most cases).prefix: Defines content prepended directly before the marker symbol.pad: Defines minimum character lengths and zero-padding (e.g.,pad: 2 "0"creates01,02,03).fallback: Designates a backup system (such asdecimalordisc) if the custom counter exceeds its defined range or fails to render.speak-as: Informs screen readers how to pronounce the custom markers (e.g.,bullets,numbers, orwords).
Practical Examples
1. Custom Emoji Bullet List
Using the cyclic system, you can cycle through a custom
set of bullet markers for an unordered list:
@counter-style task-markers {
system: cyclic;
symbols: "👉" "⭐" "✅";
suffix: " ";
}
ul.custom-bullets {
list-style-type: task-markers;
}2. Padded Numbered List
Using the numeric system alongside the pad
descriptor, you can create zero-padded ordered lists:
@counter-style zero-padded {
system: numeric;
symbols: "0" "1" "2" "3" "4" "5" "6" "7" "8" "9";
pad: 2 "0";
suffix: ") ";
}
ol.padded-list {
list-style-type: zero-padded;
}This generates items formatted as 01), 02),
03), etc.
Advantages of Using
@counter-style
- Semantic Cleanliness: Keeps list formatting inside
native list properties (
list-style-type) rather than relying on pseudo-element workarounds. - Reusability: Once declared, the counter style can
be reused across any element, including CSS
counters()and::markerpseudo-elements. - Accessibility: The
speak-asdescriptor ensures that visual embellishments do not degrade the experience for users relying on screen readers.