Introduction
Auto numbering headings using Word, OpenOffice, LibreOffice… is easy and well known by users since decades.
How to achieve it when publishing HTML pages ?
CSS is powerful, CSS can do the job. Always investigate CSS features before starting a development (Javascript…), often CSS is able to cover very easily a need.
To autonumber elements :
- 3 CSS properties applied mainly on the pseudo class
::before
:content
,counter-increment
,counter-reset
. - 2 CSS functions :
counter()
,counters()
.
That’s all.
<tag>::before {
content: counter(…);
counter-increment: …;
counter-reset: …;
}
Numbering headings
How autonumbering headings is performed in this paper ?
Autonumbering headings is applied in this paper using only CSS.
All headings tags to be numbered are encapsulated in
the main
tag here, obviously it may be another parent tag (body
, div
…):
<main>
<h2>Introduction</h2>
<h2>Numbering headings</h2>
<h3>How autonumbering headings is performed in this paper ?</h3>
<h3>Disabling autonumbering</h3>
<h3>Changing format</h3>
<h2>Numbering lists</h2>
<h2>Pagination</h2>
<h2>Conclusion</h2>
</main>
<h1>
tag is not numbered. Usually there is only one <h1>
tag in a page and it is the title.
A counter is defined for each <hx>
tag and numbering is applied until <h4>
(it is recommended to set a reasonable depth level for headings tags).
<h2> | h2counter |
<h3> | h3counter |
<h4> | h4counter |
Sub headings counters are reset in the counter-reset
property of the parent heading tag:
main { counter-reset: h2counter; }
main h2 { counter-reset: h3counter; }
main h3 { counter-reset: h4counter; }
In the ::before
pseudo class for <hx>
tags, counter is incremented and numbering content is defined :
main { counter-reset: h2counter; }
main h2 { counter-reset: h3counter; }
main h3 { counter-reset: h4counter; }
main h2::before {
counter-increment: h2counter;
content: counter(h2counter) ".";
}
main h3::before {
counter-increment: h3counter;
content: counter(h2counter) "." counter(h3counter) ".";
}
main h4::before {
counter-increment: h4counter;
content: counter(h2counter) "." counter(h3counter) "." counter(h4counter) ".";
}
That’s all.
Disabling autonumbering
For some headings (introduction, conclusion, appendix…), disabling autonumbering is preferred.
Define a custom attribute, for example data-nocount
:
<h2 data-nocount>Introduction</h2>
and apply numbering only if the tag does not contain this attribute :
main { counter-reset: h2counter; }
main h2:not([data-nocount]) { counter-reset: h3counter; }
main h3:not([data-nocount]) { counter-reset: h4counter; }
main h2:not([data-nocount])::before {
counter-increment: h2counter;
content: counter(h2counter) ".";
}
main h3:not([data-nocount])::before {
counter-increment: h3counter;
content: counter(h2counter) "." counter(h3counter) ".";
}
main h4:not([data-nocount])::before {
counter-increment: h4counter;
content: counter(h2counter) "." counter(h3counter) "." counter(h4counter) ".";
}
Changing format
Numbering format may be different in pages, examples : 1.1.1.
, 1-1-1-
.
A separator is then defined using a variable, more customizable :
:root {
--sep-num : '-';
}
main { counter-reset: h2counter; }
main h2:not([data-nocount]) { counter-reset: h3counter; }
main h3:not([data-nocount]) { counter-reset: h4counter; }
main h2:not([data-nocount])::before {
counter-increment: h2counter;
content: counter(h2counter) var(--sep-num);
}
main h3:not([data-nocount])::before {
counter-increment: h3counter;
content: counter(h2counter) var(--sep-num) counter(h3counter) var(--sep-num);
}
main h4:not([data-nocount])::before {
counter-increment: h4counter;
content: counter(h2counter) var(--sep-num) counter(h3counter) var(--sep-num) counter(h4counter) var(--sep-num);
}
To change then the numbering separator for a page, just need to redefine the variable in this page after CSS containing numbering rules are loaded :
<link href="./css/style.css" rel="stylesheet"> <style>
:root { --sep-num : '.'; }
</style>
A variable is also very useful if the numbering format is language dependent. In the below example, the default separator is modified if the document is in French :
:root {
--sep-num : '.';
}
:lang(fr) { --sep-num : '-'; }
Numbering can be set to other types than numeric : alphabetic, roman, greek, katakana…
(Mozilla - list-style-type).
The type is the second optional argument of the counter
function :
content: counter(h2counter, upper-roman) var(--sep-num);
Numbering lists
<ol>
tag is useful for numbering items in a list, unfortunately the numbering is not the expected one
when <ol>
tags are nested :
|
Another issue, we may want to apply autonumbering on <ul>
items built by a third party library (javascript…) : TocBot for example,
a javascript library for Tables of contents dynamic generation.
CSS counters come to the rescue !
In the following use case, the table of contents is generated in a div
element (class js-toc
):
<div class="js-toc">
<ul>
<li><a href="#…">Introduction</a></li>
<li><a href="#…">Numbering headings
<ul>
<li><a href="#…">How autonumbering headings is performed in this paper ?</a></li>
<li><a href="#…">Disabling autonumbering</a></li>
<li><a href="#…">Changing format</a></li>
</ul>
</li>
<li><a href="#…">Numbering lists</a></li>
<li><a href="#…">Pagination</a></li>
<li><a href="#…">Conclusion</a></li>
</ul>
</div>
To achieve numbering, a counter licounter
is reinitialized each time a block ul
is created
in the table of contents container (<div class="js-toc">
) :
div[class*="js-toc"] ul {
list-style-type: none;
counter-reset: licounter 0;
}
For each li
block, the counter is incremented and the counters
function is used to fill the content
of the ::before
pseudo-class :
:root {
--sep-num : '.';
}
:lang(fr) { --sep-num : '-'; }
div[class*="js-toc"] ul {
list-style-type: none;
counter-reset: licounter 0;
}
div[class*="js-toc"] ul li::before {
counter-increment: licounter;
content: counters(licounter, var(--sep-num)) var(--sep-num);
padding-right: 8px;
}
The CSS counters
function enables nested counters and returns
a concatenated string representing the current values of the named counters.
To disable autonumbering, like headings, a user attribute data-nocount
is defined :
<li data-nocount><a href="#…">Introduction</a></li>
and the numbering is applied only if the li
tag does not contain the attribute data-nocount
:
div[class*="js-toc"] ul li:not([data-nocount])::before {
counter-increment: licounter;
content: counters(licounter, var(--sep-num)) var(--sep-num);
padding-right: 8px;
}
The third optional argument of the counters
function changes the numbering type :
content: counters(licounter, var(--sep-num), upper-roman) var(--sep-num);
Pagination
Knowing this powerful feature, pagination toolbars are easily built without having to compute and print the counters :
ul[class*="paging"] {
list-style-type: none;
counter-reset: pgcounter var(--start-toolbar-paging);
}
ul[class*="paging"] li a:not([data-nocount])::before {
counter-increment: pgcounter;
content: counter(pgcounter);
}
Unfortunately, the --start-toolbar-paging
variable must be defined before in the page.
It is currently forbidden to use attribute values which are string
data types
to define integer
counters, the new CSS version levels (CSS 4 ?) will probably cover this long awaited feature.
ul[class*="paging"] {
list-style-type: none;
counter-reset: pgcounter attr(data-count-start);
}
Conclusion
A conclusion ? Consult the CSS documentations and stay up to date : powerful, CSS can save hours of programming and headhaches.