Assegnare uno stile ai numeratori delle liste ordinate non è un'operazione immediata, in quanto lo standerd CSS non offre al momento un selettore che permetta di isolare i numeri che contrassegnano gli elementi delle liste (markers).
Eppure come abbiamo già visto in un altro articolo è possibile ridisegnare i numeratori grazie ad una funzonalità presente nella specifica di CSS 2.1: i contatori.
I contatori CSS
Si tratta di variabili i cui valori possono essere incrementati grazie a dichiarazioni CSS che permettono di tenere in memoria il numero di volte in cui il contatore stesso è utilizzato all'interno del DOM. Per gestire i contatori, la specifica fornisce le proprietà:
- counter-reset
- counter-increment
Le due proprietà possono essere assegnate a qualunque elemento del DOM, e quindi i valori dei contatori possono essere modificati arbitrariamente.
Il valore del contatore viene mandato a video grazie alle funzioni counter()
e counters()
della proprietà content
.
I contatori e le liste ordinate
Prendiamo come esempio una semplice lista ordinata:
<ol class="number">
<li>Lorem ipsum dolor sit amet, ... </li>
<li>Lorem ipsum dolor sit amet, ... </li>
</ol>
Assegniamo, quindi, all'elemento ol il seguente stile:
ol.number {
width: 32em;
margin: 0;
padding: 0;
margin-left: 4em;
list-style-type: none;
counter-reset: simple-counter;
}
Con la proprietà list-style-type
viene annullato lo stile della lista, mentre con counter-reset
viene impostato il contatore simple-counter
. Il valore predefinito del contatore è 0, ma counter-reset
permette anche di assegnare un valore iniziale diverso:
ol.number {
...
list-style-type: none;
counter-reset: simple-counter 4;
}
In questo modo, il primo elemento della lista sarà contrassegnato dal numero 5. Una volta impostato il contatore, bisogna mandare a video i valori che questo assume man mano che viene aggiornato.
Per raggiungere lo scopo, si ricorre alla proprietà content dello pseudo-elemento ::before, come nella dichiarazione che segue:
ol.number li::before {
position: absolute;
top: 0;
left: -0.8em;
font-size: 2.8em;
line-height: 1em;
content: counter(simple-counter);
counter-increment: simple-counter;
}
La funzione counter()
restituisce il valore del contatore, che quindi può essere mandato a video con la proprietà content
.
Il codice CSS completo e l'esempio dal vivo.
Le funzioni counter() e counters()
Grazie alla proprietà content, assegnata agli pseudo-elementi ::before
e ::after
, è possibile generare un contenuto all'interno di un elemento del DOM. Tra i possibili valori della proprietà content, le funzioni counter() e counters() forniscono i valori correnti dei contatori.
Counter
La funzione counter() accetta uno o due argomenti:
Così, nell'esempio precedente, la dichiarazione avrebbe potuto essere anche la seguente:
ol.number li::before {
...
content: counter(simple-counter, upper-roman);
counter-increment: simple-counter;
}
Il nome del contatore non deve necessariamente essere univoco. Possono esistere, infatti, più contatori con lo stesso nome, ognuno valido nell'ambito di uno specifico elemento (scope
counters()
<ol class="toc">
<li>Core JavaScript <!-- Part 1 -->
<ol>
<li>Lexical Structure <!-- Chapter -->
<ol>
<li>Character Set</li> <!-- Paragraph -->
<li>Comments</li>
<li>Literals</li>
<li>Identifiers and Reserved Words</li>
<li>Optionals Semicolons</li>
</ol>
</li>
...
</ol>
Il markup qui sopra genera tre livelli di liste ordinate. Il primo livello individua le parti, il secondo individua i capitoli, il terzo, infine, i paragrafi di un libro. Vediamo, quindi, le dichiarazioni CSS. Come nell'esempio precedente, va impostato il contatore e annullati gli stili predefiniti delle liste:
.toc,
.toc ol {
counter-reset: listitem;
list-style-type: none;
}
Trattandosi di più liste annidate, abbiamo selezionato sia la lista principale, sia tutte le liste ordinate da essa discendenti (.toc ol
.toc li:before {
counter-increment: listitem;
content: counters(listitem, ".")" ";
}
Prima di ogni elemento di lista viene aggiunto lo pseudo-elemento ::before
listitem
content
counters()
listitem
Potrebbe essere opportuno dare maggiore enfasi alla prima lista della gerarchia, magari evidenziando gli elementi che individuano una parte principale del libro. Per far questo, bisogna sovrascrivere il blocco precedente con un selettore specifico per gli elementi di lista direttamente discendenti dalla lista principale:
.toc > li::before {
content: "Part " counter(listitem)". ";
}
Si noterà subito che abbiamo utilizzato la funzione counter()
counters()
"Part "
". "
In questo esempio bisogna stare attenti alla specificità dei selettori, che è esattamente la stessa. Per questo motivo è necessario che i blocchi di dichiarazioni vengano inseriti nello stesso ordine del nostro esempio.
Manca pochissimo. Per distinguere meglio gli elementi delle liste annidate, assegniamo colori e dimensioni diverse agli elementi di lista, in base al livello gerarchico:
.toc li {
line-height: 1.4em;
color: #000;
font-size: .82em;
}
.toc > li {
color: red;
font-size: 2em;
}
.toc > li > ol > li {
color: blue;
}
Anche in questo caso, la specificità dei selettori è la stessa, quindi l'ordine dei blocchi di dichiarazioni è determinante per la corretta visualizzazione degli elementi.
Tre liste ordinate annidate con i rispettivi contatori
Il codice CSS completo e l'esempio dal vivo.
Soluzioni grafiche
Una volta conosciuti gli strumenti a disposizione, è possibile sperimentare alcune soluzioni grafiche. Partiamo dalla semplice lista ordinata dei primi esempi:
<ol class="transformed">
<li>Lorem ipsum dolor sit amet, ... </li>
<li>Lorem ipsum dolor sit amet, ... </li>
</ol>
I primi blocchi di dichiarazioni dovrebbero essere ora ben comprensibili:
ol.transformed {
width: 32em;
margin: 0;
padding: 0;
margin-left: 4em;
list-style-type: none;
counter-reset: my-counter 8;
}
ol.transformed li {
position: relative;
margin-bottom: 2em;
padding-left: .4em;
line-height: 1.2em;
min-height: 3em;
border-left: 2px solid #ccc;
background-color: rgba(255, 255, 255, .8);
}
Il codice si conclude con l'assegnazione degli stili allo pseudo-elemento ::before:
ol.transformed li::before {
position: absolute;
top: 0;
right: 10.8em;
min-width: 1.2em;
min-height: 1em;
padding: .1em 0;
font-size: 3em;
line-height: 1em;
text-align: center;
content: counter(my-counter);
counter-increment: my-counter;
transform: rotate(-30deg) translate(.2em, .3em);
overflow: hidden;
z-index: -999;
color: #999;
background-color: #000;
}
Abbiamo impostato la distanza del marker a 10.8em dal bordo destro dell'elemento superiore, ossia l'elemento di lista. La misura è derivata dal seguente calcolo:
32em (larghezza della lista ol)
0.4em (padding-left degli elementi della lista)
3em (dimensioni del font dello pseudo-elemento li::before)
Distanza dal margine destro: ( 32 + 0.4 ) ÷ 3 = 10.8em
Grazie alla proprietà transform
, i marker vengono ruotati e traslati sui due assi; infine viene assegnato uno sfondo nero. Il risultato è quello mostrato nella figura che segue.

Il codice CSS e l'esempio dal vivo
Cerchi
Con poche modifiche diventa possibile dare ai markers un aspetto del tutto inedito. Riprendiamo l'esempio precedente e modifichiamo solo le dichiarazioni relative allo pseudo-elemento ::before
:
ol.transformed li::before {
position: absolute;
top: 0;
right: 10.8em;
min-width: 1.2em;
min-height: 1.2em;
padding: 0;
font-size: 3em;
line-height: 1.2em;
text-align: center;
content: counter(my-counter);
counter-increment: my-counter;
color: #999;
background-color: #000;
border: 4px solid #999;
border-radius: 50%;
}
Rispetto all'esempio precedente abbiamo modificato l'altezza minima dell'elemento, azzerato il padding e alzato a 1.2 l'altezza delle linea del testo. Abbiamo eliminato le proprietà transform
, overflow
e z-index
e aggiunto bordi arrotondati al 50% delle dimensioni dei lati, di 4px di spessore.
L'effetto in Safari è illustrato nella figura che segue.

Il file CSS e l'esempio dal vivo
Animazioni
Più sopra abbiamo assegnato la proprietà transform
allo pseudo-elemento li::before
. Allo stesso modo è possibile assegnare la proprietà transition per generare un'animazione sul marker dell'elemento.
Lasciamo ancora una volta invariati gli stili della lista e degli elementi li
, e modifichiamo gli stili dei marker:
ol.transformed li::before {
position: absolute;
top: 0;
right: 10.8em;
min-width: 1.2em;
min-height: 1em;
padding: .1em 0;
font-size: 3em;
line-height: 1em;
text-align: center;
content: counter(my-counter);
counter-increment: my-counter;
transform: rotate(-30deg) translate(.2em, .3em);
overflow: hidden;
z-index: -999;
color: #999;
background-color: #000;
transition: all .2s ease-out;
}
ol.transformed li:hover::before {
transform: rotate(30deg);
}
Al passaggio del mouse sull'elemento li
transition
Al passaggio del mouse sul list item, il marker si anima ruotando su se stesso di 60°
Il codice CSS completo e l'esempio dal vivo
Conclusioni e riferimenti
Contenuto generato, contatori e pseudo-elementi sono gli ingredienti che permettono di aggirare una lacuna del Cascading Style Sheet, che al momento non permette di assegnare direttamente stili ai contatori delle liste.
Eppure in previsione c'è l'introduzione dello pseudo-elemento ::marker
, che dovrebbe in futuro risolvere il problema. Purtroppo, al momento nessun browser ne offre il supporto.
Gli esempi presentati sono stati testati sulle versioni correnti di Firefox, Chrome, Opera e Safari.
Nella stesura di questo articolo si è fatto riferimento alle seguenti risorse:
- W3C Recommendation: Generated Content - Automatic Counters and Numbering
- W3C Draft: CSS Lists and Counters Module Level 3
- MDN: content
- MDN: Using CSS counters
- MDN: counter-reset
- MDN: counter-increment
- Caniuse: CSS counters
Ti consigliamo anche
Argomento | Descrizione |
---|---|
name | il nome del contatore |
style | lo stile della formattazione. Il valore predefinito è decimal
list-style-type
|