Клавиша / esc

Счётчики в CSS

Подробно разбираемся как браузер считает элементы и как этим управлять через CSS.

Время чтения: больше 15 мин

Кратко

Скопировано

CSS-счётчики — это мощный инструмент для нумерации любых элементов страницы, не только списков.

Открыть демо в новой вкладке

Основные свойства для работы со счётчиками:

  • counter-reset — создаёт счётчик;
  • counter-increment — увеличивает числовое значение счётчика;
  • counter-set — устанавливает точное значение счётчика.

Помимо свойств, с помощью которых можно создавать счётчики и управлять ими, есть две основные функции для подставления значения счётчика: counter() и counters().

counter-reset

Скопировано

Перед тем как воспользоваться значением счётчика, этот счётчик нужно создать. Счётчики создаются при помощи свойства counter-reset.

        
          
          ul {  counter-reset: example 0;}
          ul {
  counter-reset: example 0;
}

        
        
          
        
      

В качестве значения сначала указывается имя счётчика, а затем его начальное целочисленное значение.

Имена счётчиков чувствительны к регистру. Например, значения example и EXAMPLE — это два разных, не связанных между собой счётчика.

Нельзя использовать ключевые слова: none, initial, inherit, unset, default в качестве названий счётчиков.

Также счётчики подчиняются принципу каскада, например:

        
          
          h1 {  counter-reset: counter1 1;}h1 {  counter-reset: counter2 99;}
          h1 {
  counter-reset: counter1 1;
}

h1 {
  counter-reset: counter2 99;
}

        
        
          
        
      

В таком случае применится только последнее CSS-правило для элемента <h1>.

Чтобы свойство counter-reset сработало для обоих счётчиков из примера выше необходимо указать их вместе через пробел:

        
          
          h1 {  counter-reset: counter1 1 counter2 99;}
          h1 {
  counter-reset: counter1 1 counter2 99;
}

        
        
          
        
      

Или вот такой пример:

        
          
          h1 {  counter-reset: counter 1 counter 2;}
          h1 {
  counter-reset: counter 1 counter 2;
}

        
        
          
        
      

В значении свойства counter-reset указаны два одинаковых счётчика, в таком случае будет учитываться только последний.

counter-increment

Скопировано

Чтобы значение счётчика начало увеличиваться на определённое значение от элемента к элементу необходимо указать свойство counter-increment.

        
          
          li {  counter-increment: example 2;}
          li {
  counter-increment: example 2;
}

        
        
          
        
      

Теперь каждый элемент <li> в документе будет увеличивать значение счётчика example на 2.

Рассмотрим пример:

        
          
          <ul>  <li class="first">Значение счётчика example равно 1</li>  <li class="second">Значение счётчика example равно 10</li>  <li class="third">Значение счётчика example равно 15</li></ul>
          <ul>
  <li class="first">Значение счётчика example равно 1</li>
  <li class="second">Значение счётчика example равно 10</li>
  <li class="third">Значение счётчика example равно 15</li>
</ul>

        
        
          
        
      

На элементе <ul> создадим счётчик example с начальным значением 0:

        
          
          ul {  counter-reset: example 0;}
          ul {
  counter-reset: example 0;
}

        
        
          
        
      

Укажем разные значения увеличения счётчика для каждого элемента <li>:

        
          
          .first {  counter-increment: example 1;}.second {  counter-increment: example 9;}.third {  counter-increment: example 5;}
          .first {
  counter-increment: example 1;
}

.second {
  counter-increment: example 9;
}

.third {
  counter-increment: example 5;
}

        
        
          
        
      

Как это работает будет более подробно расписано дальше. Но в конечном итоге пример выше выглядит так:

Открыть демо в новой вкладке
        
          
          li {  /* Значение счётчика example будет равно 15 на этом элементе */  counter-increment: example 1 example 9 example 5;}
          li {
  /* Значение счётчика example будет равно 15 на этом элементе */
  counter-increment: example 1 example 9 example 5;
}

        
        
          
        
      

counter-set

Скопировано

Вам неожиданно понадобилось изменить порядок нумерации элементов в списке, чтобы после 1 пункта шёл сразу 9? counter-set отлично подойдёт для этой задачи:

Рассмотрим пример:

        
          
          <ul>  <li class="first">Значение счётчика example равно 1</li>  <li class="second">Значение счётчика example равно 9</li>  <li class="third">Значение счётчика example равно 15</li></ul>
          <ul>
  <li class="first">Значение счётчика example равно 1</li>
  <li class="second">Значение счётчика example равно 9</li>
  <li class="third">Значение счётчика example равно 15</li>
</ul>

        
        
          
        
      

На элементе <ul> создадим счётчик example с начальным значением 0:

        
          
          ul {  counter-reset: example 0;}
          ul {
  counter-reset: example 0;
}

        
        
          
        
      

Укажем разные значения увеличения счётчика для каждого элемента <li>, а для элемента с классом second добавим ещё свойство counter-set:

        
          
          .first {  counter-increment: example 1;}.second {  counter-increment: example 3;  counter-set: example 9;}.third {  counter-increment: example 6;}
          .first {
  counter-increment: example 1;
}

.second {
  counter-increment: example 3;
  counter-set: example 9;
}

.third {
  counter-increment: example 6;
}

        
        
          
        
      
Открыть демо в новой вкладке

Может показаться, что второй элемент с классом second должен иметь значение 12, а не 9, потому что на него применяются сразу два свойства counter-increment и counter-set, однако при вычислении значения используется определённый порядок.

Сначала выполняется свойство counter-increment и значение счётчика увеличивается на 3, но затем сразу же переписывается свойством counter-set устанавливая точное значение 9.

        
          
          li {  /* Значение счётчика example будет равно 9 на этом элементе */  counter-set: example 1 example 6 example 9;}
          li {
  /* Значение счётчика example будет равно 9 на этом элементе */
  counter-set: example 1 example 6 example 9;
}

        
        
          
        
      

counter() и counters()

Скопировано

Свойства counter-increment и counter-set не отображают фактическое значение счётчиков. Эти свойства только управляют ими.

Но когда речь заходит о том, чтобы отобразить значения того или иного счётчика на помощь приходят функции counter() и counters().

Разница между ними будет описана подробнее дальше.

Возьмём один из примеров который уже был представлен выше:

        
          
          <ul>  <li class="first">Значение счётчика example равно 1</li>  <li class="second">Значение счётчика example равно 10</li>  <li class="third">Значение счётчика example равно 15</li></ul>
          <ul>
  <li class="first">Значение счётчика example равно 1</li>
  <li class="second">Значение счётчика example равно 10</li>
  <li class="third">Значение счётчика example равно 15</li>
</ul>

        
        
          
        
      

На элементе <ul> создадим счётчик example с начальным значением 0:

        
          
          ul {  counter-reset: example 0;}
          ul {
  counter-reset: example 0;
}

        
        
          
        
      

Укажем разные значения увеличения счётчика для каждого элемента <li>:

        
          
          .first {  counter-increment: example 1;}.second {  counter-increment: example 9;}.third {  counter-increment: example 5;}
          .first {
  counter-increment: example 1;
}

.second {
  counter-increment: example 9;
}

.third {
  counter-increment: example 5;
}

        
        
          
        
      

Теперь, чтобы значения счётчика начали отображаться в документе воспользуемся функцией counter().

Подставляем значение счётчика example на каждый псевдоэлемент ::marker элемента <li>:

        
          
          li::marker {  content: counter(example);}
          li::marker {
  content: counter(example);
}

        
        
          
        
      
Открыть демо в новой вкладке

В обоих функциях есть необязательный аргумент, который указывает стиль счётчика, например, вместо использования десятичной системы счисления — данное значение устанавливается по умолчанию, можно указать строчную римскую нумерацию, то есть тип lower-roman:

        
          
          li::marker {  content: counter(example, lower-roman);}
          li::marker {
  content: counter(example, lower-roman);
}

        
        
          
        
      
Открыть демо в новой вкладке

О других стилях счётчика можно посмотреть в доке по свойству list-style-type.

Основные термины

Скопировано
  1. Набор счётчиков — это коллекция неповторяющихся между собой счётчиков, которые имеет элемент. Эта коллекция пополняется наследуемыми счётчиками от другого элемента и счётчиками которые элемент создал сам.

Изначально каждый элемент или псевдоэлемент в документе имеет пустой набор счётчиков.

Каждый счётчик, который попадает в эту коллекцию, имеет внутри себя следующие данные:

  • имя — идентификатор, который указывается при создании счётчика;
  • создатель — элемент, который его создал;
  • значение — целочисленное значение счётчика.

Представить это можно так:

Набор счётчиков элемента
  1. Явный счётчик — это счётчик который создали вы.
  2. Неявный счётчик — это счётчик который автоматически создаётся браузером.
  3. Последний счётчик — это самый последний счётчик с указанным именем из набора счётчиков.

Порядок вычисления значения

Скопировано

Значения счётчика из набора на каждом отдельном элементе могут быть разными. Значение счётчика на текущем элементе вычисляется поэтапно:

  1. Сначала наследуется набор счётчиков.
  2. Если на элементе указано свойство counter-reset — создаётся новый счётчик.
  3. Если на элементе указано свойство counter-increment — значение счётчика увеличивается.
  4. Если на элементе указано свойство counter-set — устанавливается точное значение счётчика.

И только теперь, после всех вычислений, значением счётчика можно воспользоваться через функции counter() и counters().

Создание и наследование счётчиков

Скопировано

Наследование

Скопировано

Элемент наследует свой начальный набор счётчиков от своего родителя и предыдущего одноуровневого элемента.

Затем он берёт значения этих счётчиков из набора счётчиков предыдущего элемента в порядке дерева. Предыдущий элемент может быть:

  • родителем;
  • одноуровневым элементом;
  • дочерним элементом предыдущего одноуровневого элемента.

Создадим счётчик на элементе <ul> и проследим как счётчик будет наследоваться элементами, которые находятся внутри.

        
          
          <ul>  <li class="first">Первый элемент    <p class="paragraph">Параграф</p>  </li>  <li class="second">Второй элемент</li></ul>
          <ul>
  <li class="first">Первый элемент
    <p class="paragraph">Параграф</p>
  </li>
  <li class="second">Второй элемент</li>
</ul>

        
        
          
        
      

На элементе <ul> создаём счётчик c именем new и начальным значением 0:

        
          
          ul {  counter-reset: new 0;}
          ul {
  counter-reset: new 0;
}

        
        
          
        
      

Теперь в наборе у элемента <ul> есть один, созданный им же счётчик с именем new.

Укажем, чтобы на элементах <li> с классами first и second, и на элементе <p> с классом paragraph значение счётчика new увеличивалось на 1:

        
          
          .first,.second,.paragraph {  counter-increment: new 1;}
          .first,
.second,
.paragraph {
  counter-increment: new 1;
}

        
        
          
        
      

Первый дочерний элемент с классом first наследует свой начальный набор счётчиков от родительского элемента <ul>.

Также <ul> является предшествующим элементом в порядке дерева, поэтому элемент с классом first наследует также значение счётчика new — 0, а затем сразу же увеличивает его на 1.

Теперь значение счётчика new на элементе <li> с классом first равно 1.

Тоже самое происходит с элементом у которого класс paragraph. Он наследует счётчик new от своего родительского элемента с классом first, а также его значение — 1, а затем сразу же увеличивает его на 1.

Теперь значение счётчика new на элементе <p> с классом paragraph равно 2.

Однако элемент с классом second немного отличается. Он наследует счётчик new от своего предыдущего одноуровневого элемента <li> с классом first, но вместо того чтобы наследовать значение 1, он наследует значение 2 от элемента с классом paragraph, предыдущего элемента в порядке дерева, а затем сразу же увеличивает его на 1.

Теперь значение счётчика new на элементе <li> с классом second равно 3.

Подставим значение счётчика new в псевдоэлемент ::before для элемента <p>, и в псевдоэлемент ::marker для элементов с классами first и second чтобы пронумеровать их:

        
          
          li::marker,p::before {  content: counter(new);}
          li::marker,
p::before {
  content: counter(new);
}

        
        
          
        
      
Открыть демо в новой вкладке

Создание счётчиков

Скопировано

После наследования набора счётчиков от предыдущего одноуровневого и родительского элементов происходит создание счётчиков.

Создавать новые счётчики может не только свойство counter-reset.

Если вы применили свойство counter-increment или counter-set, или воспользовались функцией counter() или counters() и указали имя несуществующего счётчика:

        
          
          li {  /* Сначала создаст счётчик с именем new, а затем увеличит его значение на 2 */  counter-increment: new 2;}li {  /* Сначала создаст счётчик с именем new, а затем установит его значение на 2 */  counter-set: new 2;}li::marker {  /* Сначала создаст счётчик с именем new, а затем подставит его значение */  content: counter(new);}li::marker {  /* Аналогично */  content: counters(new, '.');}
          li {
  /* Сначала создаст счётчик с именем new, а затем увеличит его значение на 2 */
  counter-increment: new 2;
}

li {
  /* Сначала создаст счётчик с именем new, а затем установит его значение на 2 */
  counter-set: new 2;
}

li::marker {
  /* Сначала создаст счётчик с именем new, а затем подставит его значение */
  content: counter(new);
}

li::marker {
  /* Аналогично */
  content: counters(new, '.');
}

        
        
          
        
      

В таком случае на элементе сначала создастся новый счётчик с именем new и начальным значением 0. После создания счётчика свойства counter-increment и counter-set, и функции counter() и counters() начнут действовать как обычно.

Если элемент уже содержит унаследованный счётчик с именем n, который был создан одноуровневым предыдущим элементом, и вы создаёте на элементе счётчик с точно таким же именем n, то этот счётчик заменит унаследованный.

Рассмотрим пример. Создадим элемент <div> с 3 вложенными элементами <p> и проследим как счётчик будет наследоваться элементами.

        
          
          <div>  <p class="first">Первый абзац</p>  <p class="second">Второй абзац</p>  <p class="third">Третий абзац</p></div>
          <div>
  <p class="first">Первый абзац</p>
  <p class="second">Второй абзац</p>
  <p class="third">Третий абзац</p>
</div>

        
        
          
        
      

На элементе <div> создаём счётчик c именем new и начальным значением 0:

        
          
          div {  counter-reset: new 0;}
          div {
  counter-reset: new 0;
}

        
        
          
        
      

Теперь у элемента <div> в наборе имеется один, созданный им же счётчик с именем new.

Создаём ещё один счётчик, но уже на элементе <p>. Назовём его paragraph:

        
          
          p {  counter-reset: paragraph 1;}
          p {
  counter-reset: paragraph 1;
}

        
        
          
        
      

Первый дочерний элемент с классом first наследует свой начальный набор счётчиков от родительского элемента <div> и также создаёт собственный счётчик с именем paragraph.

Теперь у элемента <p> с классом first в наборе два счётчика: один унаследованный от родительского элемента <div>, второй собственно созданный счётчик.

Унаследованный и собственно созданный счётчик в наборе

Так как <div> является предшествующим элементом в порядке дерева, элемент с классом first наследует также значение счётчика new — 0.

Следующий элемент <p> с классом second унаследует точно такой же набор счётчиков от предыдущего одноуровневого элемента <p> с классом first, но как только это произойдёт он перезапишет счётчик paragraph элемента <p> c классом first на собственно созданный счётчик.

Унаследованный и собственно созданный счётчик в наборе

Последний элемент <p> с классом third сделает абсолютно тоже самое.

Увеличим элементу с классом second значение счётчика paragraph:

        
          
          .second {  counter-increment: paragraph 1;}
          .second {
  counter-increment: paragraph 1;
}

        
        
          
        
      

Подставим значение счётчика paragraph в псевдоэлемент ::before элементов с классами first, second и third чтобы пронумеровать их:

        
          
          p::before {  content: counter(paragraph);}
          p::before {
  content: counter(paragraph);
}

        
        
          
        
      
Открыть демо в новой вкладке

Так как счётчик paragraph увеличивался при помощи свойства counter-increment только на элементе с классом second, значение счётчика на следующих элементах <p> будет 1.

Вложенные счётчики и область применения

Скопировано

Представим ещё одну ситуацию. Если элемент уже содержит унаследованный счётчик с именем n, который был создан родительским элементом (любым, необязательно родительским элементом текущего элемента), и вы создаёте на элементе счётчик с точно таким же именем n, в таком случае этот счётчик вложится в уже существующий счётчик.

Представить это можно так:

Вложенные счётчики в наборе

Рассмотрим пример. Создадим многоуровневый список создав счётчик на элементе <ul> и проследим как счётчик будет наследоваться элементами, которые находятся внутри.

        
          
          <ul class="first-list">  <li class="one">Первый элемент первого списка    <ul class="second-list">      <li class="one-one">Первый элемент второго списка</li>      <li class="one-two">Второй элемент второго списка</li>    </ul>  </li>  <li class="two">Второй элемент первого списка</li></ul>
          <ul class="first-list">
  <li class="one">Первый элемент первого списка
    <ul class="second-list">
      <li class="one-one">Первый элемент второго списка</li>
      <li class="one-two">Второй элемент второго списка</li>
    </ul>
  </li>
  <li class="two">Второй элемент первого списка</li>
</ul>

        
        
          
        
      

На элементе <ul> создаём счётчик c именем new и начальным значением 0:

        
          
          ul {  counter-reset: new 0;}
          ul {
  counter-reset: new 0;
}

        
        
          
        
      

Теперь у элемента <ul> с классом first-list в наборе имеется один счётчик созданный им же.

Укажем, чтобы на элементе <li> значение счётчика new увеличивалось на 1:

        
          
          li {  counter-increment: new 1;}
          li {
  counter-increment: new 1;
}

        
        
          
        
      

Первый дочерний элемент с классом one наследует свой начальный набор счётчиков от родительского элемента <ul> c классом first-list.

Также <ul> с классом first-list является предшествующим элементом в порядке дерева, поэтому элемент с классом one наследует также значение счётчика new — 0, а затем сразу же увеличивает его на 1.

Теперь значение счётчика new на элементе <li> с классом one равно 1.

В элементе <li> с классом one появляется ещё один элемент <ul> с классом second-list.

Элемент <ul> с классом second-list наследует счётчик new от своего родительского элемента с классом one, после этого он создаёт собственный счётчик с именем new, но так как счётчик с таким же именем, созданный родительским элементом <ul> с классом first-list, уже есть в наборе, то этот счётчик не будет удалён, а вложится в уже существующий.

У элемента <ul> с классом second-list получается примерно следующий набор счётчиков:

Вложенные счётчики в наборе

На этом моменте область действия счётчика new элемента <ul> с классом first-list заканчивается. И если применить свойства counter-increment или counter-set чтобы повлиять на значение счётчика new, меняться будет только последний счётчик с именем new в наборе.

Первый дочерний элемент с классом one-one, элемента <ul> с классом second-list, наследует свой начальный набор счётчиков от родительского элемента.

Также <ul> c классом second-list является предшествующим элементом в порядке дерева, поэтому элемент с классом one-one наследует также значение счётчика new — 0, а затем сразу же увеличивает его на 1. Но увеличивает не счётчик созданный элементом <ul> с классом first-list, а счётчик, который создал его родительский элемент <ul> с классом second-list.

Вложенные счётчики в наборе

Элемент <li> с классом one-two наследует счётчик new от своего предшествующего одноуровневого элемента с классом one-one, а также его значение — 1, а затем сразу же увеличивает его на 1. Но опять же, увеличивает не счётчик созданный элементом <ul> с классом first-list, а счётчик, который создал его родительский элемент <ul> с классом second-list.

Вложенные счётчики в наборе

Самый последний элемент <li> с классом two наследует набор счётчиков от предыдущего одноуровневого элемента — элемента <li> с классом one.

Так как предыдущий элемент в порядке дерева — это элемент <ul> с классом second-list, элемент с классом two наследует значение счётчика new, но только не того счётчика new, который создал элемент <ul> с классом second-list, а счётчика который создал элемент <ul> с классом first-list и затем сразу же увеличивает его на 1.

Теперь значение счётчика new на элементе <li> с классом two равно 2.

Открыть демо в новой вкладке

Разница между counter() и counters()

Скопировано

В итоговой демке выше нумерация (значение счётчиков) была выведена при помощи функции counters().

Функция counters() выводит значения всех счётчиков с указанным именем в наборе:

Пример работы функции

Вторым аргументом функции counters() важно указать разделитель в виде строки. Эта строка будет разделять значения всех счётчиков с указанным именем.

Функция counter() выводит значение только последнего счётчика с указанным именем.

Выведем ту же самую демку, но используем функцию counter():

Открыть демо в новой вкладке
Пример работы функции

Свойства display и content

Скопировано

Свойства counter-reset, counter-increment и counter-set не будут действовать на элементах которые не создают блок. Например на элементах с display: none или псевдоэлементах с content: none.

Рассмотрим пример:

        
          
          <div>  <p class="display">Абзац с display: none</p>  <p class="content">Абзац с content: none</p>  <p class="first">Нормальный абзац</p>  <p class="second">Последний нормальный абзац</p></div>
          <div>
  <p class="display">Абзац с display: none</p>
  <p class="content">Абзац с content: none</p>
  <p class="first">Нормальный абзац</p>
  <p class="second">Последний нормальный абзац</p>
</div>

        
        
          
        
      

На элементе <div> создаём счётчик c именем new и начальным значением 0:

        
          
          div {  counter-reset: new 0;}
          div {
  counter-reset: new 0;
}

        
        
          
        
      

Укажем, чтобы на элементах <p> с классами first и second значение счётчика new увеличивалось на 1:

        
          
          p.first,p.second {  counter-increment: new 1;}
          p.first,
p.second {
  counter-increment: new 1;
}

        
        
          
        
      

Далее укажем основные свойства для элементов с классами display и content, а также укажем увеличение значения счётчика new на 1:

        
          
          p.display {  counter-increment: new 1;  display: none;}p.content::before {  counter-increment: new 1;  content: none;}
          p.display {
  counter-increment: new 1;
  display: none;
}

p.content::before {
  counter-increment: new 1;
  content: none;
}

        
        
          
        
      

Пронумеруем элементы с классами first и second через функцию counter():

        
          
          p.first::before,p.second::before {  content: counter(new);}
          p.first::before,
p.second::before {
  content: counter(new);
}

        
        
          
        
      
Открыть демо в новой вкладке

Как видно элемент <p> с классом display и псевдоэлемент ::before элемента с классом content не увеличили значение счётчика new.

Неявный счётчик элементов списка

Скопировано

В дополнение к любым явно определённым в CSS счётчикам, любые элементы с display: list-item автоматически имеют в своём наборе специальный неявный счётчик с именем list-item.

Например, элементы <li> по умолчанию имеют свойство display со значением list-item.

Счётчиком list-item можно управлять так же как и обычным явным счётчиком.

Если в значении свойства counter-increment явно не указывается значение на которое счётчик list-item будет увеличиваться от элемента к элементу, то он автоматически увеличивается браузером на 1 для каждого элемента со свойством display: list-item.

Именно поэтому, когда вы объявляете список <ol> элементы <li> в списке автоматически нумеруются.

Даже если вы попытаетесь указать свойство counter-increment со значением none на элементе <li> неявный счётчик list-item всё равно продолжит нумерацию. Эта защита сделана специально.

Однако если вы укажете другое значение, например:

        
          
          li {  counter-increment: list-item 2;}
          li {
  counter-increment: list-item 2;
}

        
        
          
        
      

автоматическое увеличение счётчика list-item будет переопределено и теперь его значение будет увеличиваться на 2.

Вы также можете указать значение 0, тогда счётчик list-item вообще перестанет увеличиваться.

Рассмотрим пример:

        
          
          <ol>  <li>Первый элемент</li>  <li>Второй элемент</li>  <li>Третий элемент</li></ol>
          <ol>
  <li>Первый элемент</li>
  <li>Второй элемент</li>
  <li>Третий элемент</li>
</ol>

        
        
          
        
      

Укажем, чтобы на элементе <li> значение неявного счётчика list-item увеличивалось на 2:

        
          
          li {  counter-increment: list-item 2;}
          li {
  counter-increment: list-item 2;
}

        
        
          
        
      
Открыть демо в новой вкладке

На самом деле это очень удобно, когда уже есть готовый неявный счётчик. Если вам нужно создать многоуровневый нумерованный список, это можно сделать в два счёта:

        
          
          <ol>  <li>Первый элемент первого списка    <ol>      <li>Первый элемент второго списка</li>      <li>Второй элемент второго списка</li>    </ol>  </li>  <li>Второй элемент первого списка</li></ol>
          <ol>
  <li>Первый элемент первого списка
    <ol>
      <li>Первый элемент второго списка</li>
      <li>Второй элемент второго списка</li>
    </ol>
  </li>
  <li>Второй элемент первого списка</li>
</ol>

        
        
          
        
      

Выведем нумерацию при помощи функции counters():

        
          
          li::marker {  content: counters(list-item, '.');}
          li::marker {
  content: counters(list-item, '.');
}

        
        
          
        
      

Всё! Больше ничего делать не нужно.

Открыть демо в новой вкладке

Особенность списка <ol>

Скопировано

Любые вносимые в HTML изменения нумерации списка также будут учитываться счётчиком.

Например, если вы укажете в списке <ol> атрибут start и значение с которого следует начинать нумерацию, или атрибут reversed, чтобы пронумеровать список в обратном порядке, то данные атрибуты будут учитываться при нумерации списка. Также это касается элемента <li> и его атрибута value:

        
          
          <ol>  <li>Первый элемент первого списка</li>  <li value="5">Второй элемент первого списка, value = 5    <ol start="3">        <li>Первый элемент второго списка. Список нумеруется с 3</li>        <li>Второй элемент второго списка          <ol reversed>            <li>Первый элемент третьего списка. Список отображается в обратном порядке</li>            <li>Второй элемент третьего списка</li>            <li>Третий элемент третьего списка</li>            <li>Четвёртый элемент третьего списка</li>          </ol>        </li>        <li>Третий элемент второго списка</li>    </ol>  </li>  <li>Третий элемент второго списка</li></ol>
          <ol>
  <li>Первый элемент первого списка</li>
  <li value="5">Второй элемент первого списка, value = 5
    <ol start="3">
        <li>Первый элемент второго списка. Список нумеруется с 3</li>
        <li>Второй элемент второго списка
          <ol reversed>
            <li>Первый элемент третьего списка. Список отображается в обратном порядке</li>
            <li>Второй элемент третьего списка</li>
            <li>Третий элемент третьего списка</li>
            <li>Четвёртый элемент третьего списка</li>
          </ol>
        </li>
        <li>Третий элемент второго списка</li>
    </ol>
  </li>
  <li>Третий элемент второго списка</li>
</ol>

        
        
          
        
      

Выведем нумерацию при помощи функции counters():

        
          
          li::marker {  content: counters(list-item, '.');}
          li::marker {
  content: counters(list-item, '.');
}

        
        
          
        
      
Открыть демо в новой вкладке

Полная поддержка подобного поведения, пока что, реализована только в Mozilla Firefox.

Пример работы в браузере Mozilla Firefox

На практике

Скопировано

Алексей Степанов советует

Скопировано

🛠 Вы вдохновились выступлением Никиты Дубко и решили написать курсовую оформленную через CSS? Счётчики помогут вам сделать автоматическую нумерацию страниц, рисунков, вашего содержания и списка литературы, а также глав и подглав вашего научного труда.

Или у вас есть интересное дизайнерское решение вроде изображённого в начале статьи?

Также вы можете посмотреть две переведённые статьи на тему счётчиков:

  1. Игры на чистом CSS со свойством counter-increment.
  2. Развлечения с CSS-счётчиками.