CSS: lists with counters, pseudo-elements and generated content

0
236

Assigning a style to the numerators of the ordered lists is not an immediate operation since the CSS standard does not currently offer a selector that allows to isolate the numbers that mark the elements of the lists ( markers ).

Yet, as we have already seen in another article, it is possible to redesign the numerators thanks to a feature that is present in the CSS 2.1 specification: the counters.

CSS counters

These are variables whose values can be increased thanks to CSS declarations that allow to keep in memory the number of times that the meter itself is used inside the DOM. To manage the counters, the specification provides the properties:

  • counter-reset declares the name of the counter;
  • counter-increment increases the value of one unit.

The two properties can be assigned to any element of the DOM, and therefore the values of the counters can be changed arbitrarily.

The value of the counter is sent to the video thanks to the functions counter() and counters() the property content.

The counters and ordered lists

Let’s take a simple ordered list as an example:

<pre class=”brush: php; html-script: true”>
<ol class = “number”>
<li>Lorem ipsum dolor sit amet, … </li>
<li>Lorem ipsum dolor sit amet, … </li>
</ol>
</pre>

So we assign the following element to the element ol:

<pre class=”brush: php; html-script: true”>
ol.number {
width: 32em;
margin: 0;
padding: 0;
margin-left: 4em;
list-style-type: none;
counter-reset: simple-counter;
}
</pre>

With the property list-style-typethe style of the list is canceled, while counter-resetthe counter is set simple-counter. The default value of the counter is 0, but counter-resetalso allows you to assign a different initial value:

<pre class=”brush: php; html-script: true”>
ol.number {

list-style-type: none;
counter-reset: simple-counter 4;
}
</pre>

In this way, the first element of the list will be marked by the number 5. Once the counter has been set, it is necessary to display the values that it assumes as it is updated.

To achieve the goal, use the content property of the pseudo-element :: before, as in the following statement:

<pre class=”brush: php; html-script: true”>
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;
}
</pre>

The function counter()returns the value of the counter, which can then be sent to the video with the property content.

The complete CSS code and the live example.

The counter () and counters () functions

Thanks to the content property, assigned to the pseudo-elements ::before and ::after, it is possible to generate content within an element of the DOM. Among the possible values of the content property, the counter() and counters() functions provide the current values of the counters.

Counter

The counter () function accepts one or two arguments:

Topic Description
name the name of the counter
style the formatting style. The default is decimal, but any of the values of the list-style-type property can be set

 

Thus, in the previous example, the declaration could also have been the following:

<pre class=”brush: php; html-script: true”>
ol.number li::before {

content: counter(simple-counter, upper-roman);
counter-increment: simple-counter;
}
</pre>

The name of the counter does not necessarily have to be unique. In fact, there may be several counters with the same name, each valid within a specific element ( scope ). The function counters()allows to display the values of all the counters having the same name, from the most external to the most internal, separating them with a specific string. This allows, for example, to generate an alternative numbering for the nested lists. Consider, for example, the following structure:

<pre class=”brush: php; html-script: true”>
<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>
</pre>

The above markup generates three levels of ordered lists. The first level identifies the parts, the second identifies the chapters, the third and finally the paragraphs of a book. So let’s see the CSS declarations. As in the previous example, set the counter and cancel the default list styles:

<pre class=”brush: php; html-script: true”>
.toc,
.toc ol {
counter-reset: listitem;
list-style-type: none;
}
</pre>

Since there are more nested lists, we have selected both the main list and all the lists ordered by it descendants ( .toc ol). Immediately afterwards, we display the values of the meters:

<pre class=”brush: php; html-script: true”>
.toc li:before {
counter-increment: listitem;
content: counters(listitem, “.”)” “;
}
</pre>

Before each list element ::before, the pseudo-element is added , at which the counter is incremented listitem; then, within it, the content generated by the value of the property is inserted content. In this case, the function counters()supplies the values of all the counters whose name is listitem, starting from the counter of the outermost list, separated by a point; after the counter, a space is inserted.

It may be appropriate to give more emphasis to the first list of the hierarchy, perhaps highlighting the elements that identify a main part of the book. To do this, you must overwrite the previous block with a specific selector for the list elements directly descending from the main list:

<pre class=”brush: php; html-script: true”>
.toc > li::before {
content: “Part ” counter(listitem)”. “;
}
</pre>

You will immediately notice that we used the function counter()and not the function counters(). This is because at the first level it will not be necessary to separate the values of the three counters. The string is “Part “added to the progressive number and the string is added “. “.

In this example, we must be careful about the specificity of the selectors, which is exactly the same. For this reason, it is necessary that the blocks of declarations are inserted in the same order as our example.

Short time left. To better distinguish the elements of the nested lists, we assign different colors and sizes to the list elements, based on the hierarchical level:

<pre class=”brush: php; html-script: true”>
.toc li {
line-height: 1.4em;
color: #000;
font-size: .82em;
}

.toc > li {
color: red;
font-size: 2em;
}

.toc> li> ol> li {
color: blue;
}
</pre>

Also in this case, the specificity of the selectors is the same, therefore the order of the blocks of declarations is decisive for the correct visualization of the elements.

Three ordered lists nested with their respective meters
Three ordered lists nested with their respective meters

The complete CSS code and the live example.

Graphic solutions

Once you know the tools available, you can experiment with some graphic solutions. Let’s start from the simple ordered list of the first examples:

<pre class=”brush: php; html-script: true”>
<ol class = “transformed”>
<li> Lorem ipsum dolor sit amet, … </ li>
<li> Lorem ipsum dolor sit amet, … </ li>
</ol>
</pre>

The first blocks of statements should now be well understood:

<pre class=”brush: php; html-script: true”>
ol.transformed {
width: 32em;
margin: 0;
padding: 0;
margin-left: 4em;
list-style-type: none;
counter-reset: my-counter 8;
}
<font></font>
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);
}
</pre>

The code ends with the assignment of styles to the pseudo-element ::before:

<pre class=”brush: php; html-script: true”>
ol.transformed li :: before {
position: absolute;
top: 0;
right: 10.8em;
min-width: 1.2em;
min-height: 1em;
padding: .1em 0;
<font></font>
font-size: 3em;
line-height: 1em;
text-align: center;
<font></font>
content: counter (my-counter);
counter-increment: my-counter;
<font></font>
transform: rotate (-30deg) translate (.2em, .3em);
overflow: hidden;
z-index: -999;
<font></font>
color: # 999;
background-color: # 000;
}
</pre>

We set the marker distance to 10.8em from the right edge of the top element, which is the list element. The measure is derived from the following calculation:

<pre class=”brush: php; html-script: true”>
32em (width of the list ol)
0.4em (padding-left of the list items)
3em (font size of the pseudo-element li :: before)
Distance from the right margin: (32 + 0.4) ÷ 3 = 10.8em
</pre>

Thanks to the property transform, the markers are rotated and translated on the two axes; finally, a black background is assigned. The result is shown in the following figure.

The CSS code and the live example.

Looking

With a few changes it becomes possible to give the markers a completely new look. Let’s go back to the previous example and modify only the declarations related to the pseudo-element ::before:

<pre class=”brush: php; html-script: true”>
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;
<font></font>
content: counter (my-counter);
counter-increment: my-counter;
color: # 999;
background-color: # 000;
border: 4px solid # 999;
border-radius: 50%;
}
</pre>

With respect to the previous example, we have modified the minimum height of the element, set the padding to zero and raise the height of the text lines to 1.2. We have eliminated the properties transform, overflowand z-indexadded rounded edges to 50% of the side dimensions, 4px in thickness.

The effect in Safari is illustrated in the following figure.

The CSS file and the live example.

Animations

More over we assigned the property transformto the pseudo-element li::before. In the same way, you can assign the transition property to generate an animation on the element’s marker.

Let’s leave the list and element styles unchanged li, and modify marker styles:

<pre class=”brush: php; html-script: true”>
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);
}
</pre>

When the mouse hovers over the element li, a 30 degree rotation will be applied clockwise. The property transition, assigned to the pseudo-element in its initial state, will animate the rotation so that the marker will appear to turn on itself.

When the mouse hovers over the list item, the marker is animated by rotating on itself 60 °
When the mouse hovers over the list item, the marker is animated by rotating on itself 60 °

The complete CSS code and the live example.

Conclusions and references

Generated content, counters and pseudo-elements are the ingredients that allow you to circumvent a gap in the Cascading Style Sheet, which currently does not allow you to directly assign styles to the list counters.

Yet in anticipation, there is the introduction of the pseudo-element Generated Content – Automatic Counters and Numbering.

LEAVE A REPLY

Please enter your comment!
Please enter your name here