CSS: The Definitive Guide, 3rd Edition (58 page)

Read CSS: The Definitive Guide, 3rd Edition Online

Authors: Eric A. Meyer

Tags: #COMPUTERS / Web / Page Design

Counters

We're all familiar with
counters; for example, the markers of the list items in ordered lists are counters.
In CSS1, there was no way to affect them, largely because there was no need: HTML
defined its own counting behaviors for ordered lists, and that was that. With the
rise of XML, it's now important to provide a method by which counters can be defined.
CSS2 was not content to simply provide for the kind of simple counting found in HTML,
however. Two properties and two
content
values
make it possible to define almost any counting format, including subsection counters
employing multiple styles, such as "VII.2.c."

Resetting and
incrementing

The basis of
creating counters is the ability to set both the starting point for a counter and
to increment it by some amount. The former is handled by the property
counter-reset
.

counter-reset

Values:

[ ? ]+ |
none
|
inherit

Initial value:

User agent-dependent

Applies to:

All elements

Inherited:

No

Computed value:

As specified

A counter identifier is simply a label created by the author. For
example, you might name your subsection counter
subsection
,
subsec
,
ss
, or
bob
. The
simple act of resetting (or incrementing)
an identifier
is sufficient to call it into being. In the following rule, the counter
chapter
is defined as it is
reset:

h1 {counter-reset: chapter;}

By
default, a counter is reset to zero. If you want to reset to a different number,
you can declare the number following the
identifier:

h1#ch4 {counter-reset: Chapter 4;}

You
can also reset multiple identifiers all at once in identifier-integer pairs. If
you leave out an integer, then it defaults to
zero:

h1 {counter-reset: Chapter 4 section -1 subsec figure 1;}
/* 'subsec' is reset to 0 */

As
you can see from the previous example, negative values are permitted. It would be
perfectly legal to set a counter to
-32768
and
count up from there.

Tip

CSS does not define what user agents should do with negative counter values
in nonnumeric counting styles. For example, there is no defined behavior for
what to do if a counter's value is
-5
but
its display style is
upper-alpha
.

To count up, you'll need a property to indicate that an element
increments a counter. Otherwise, the counter would remain at whatever value it was
given with a
counter-reset
declaration. The
property in question is, not surprisingly,
counter-increment
.

counter-increment

Values:

[ ? ]+ |
none
|
inherit

Initial value:

User agent-dependent

Applies to:

All elements

Inherited:

No

Computed value:

As specified

Like
counter-reset
,
counter-increment
accepts identifier-integer pairs,
and the integer portion of these pairs can be zero or negative as well as
positive. The difference is that if an integer is omitted from a pair in
counter-increment
, it defaults to 1, not
0.

As an example, here's how a user agent might define counters to
recreate the traditional 1, 2, 3 counting of ordered
lists:

ol {counter-reset: ordered;}  /* defaults to 0 */
ol li {counter-increment: ordered;} /* defaults to 1 */

On
the other hand, an author might want to count backward from zero so that the list
items use a rising negative system. This would require only a small
edit:

ol {counter-reset: ordered;}  /* defaults to 0 */
ol li {counter-increment: ordered -1;}

The
counting of lists would then be -1, -2, -3 and so on. If you replaced the integer
-1
with
-2
, then lists would count -2, -4, -6 and so on.

Using
counters

To actually display the counters, though, you need
to use the
content
property in conjunction with
one of the counter-related values. To see how this works, let's use an XML-based
ordered list like
this:


First item
Item two
The third item

By
applying the following rules to XML employing this structure, you would get the
result shown in
Figure
12-22
:

list[type="ordered"] {counter-reset: ordered;}  /* defaults to 0 */
list[type="ordered"] item {display: block;}
list[type="ordered"] item:before {counter-increment: ordered;
content: counter(ordered) ". "; margin: 0.25em 0;}

Figure 12-22. Counting the items

Note that the generated content is, as usual, placed as inline content
at the beginning of the associated element. Thus, the effect is similar to an HTML
list with
list-style-position
:
inside;
declared.

Note also that the
item
elements are ordinary elements
generating block-level boxes, which means that counters are not restricted only to
elements with a
display
of
list-item
. In fact, any element can make use of a
counter. Consider the following
rules:

h1:before {counter-reset: section subsec;
counter-increment: chapter;
content: counter(chapter) ". ";}
h2:before {counter-reset: subsec;
counter-increment: section;
content: counter(chapter )"." counter(section) ". ";}
h3:before {counter-increment: subsec;
content: counter(chapter) "." counter(section) "." counter(subsec) ". ";}

These
rules would have the effect shown in
Figure
12-23
.

Figure 12-23. Adding counters to headings

Figure 12-23
illustrates some
important points about counter resetting
and
incrementing.
Notice how
the
h1
element uses the counter
chapter
, which defaults to zero and has a "1." before
the element's text. When a counter is incremented and used by the same element,
the incrementation happens
before
the counter is displayed.
In a similar way, if a counter is reset and shown in the same element, the reset
happens before the counter is displayed.
Consider:

h1:before, h2:before, h3:before {
content: counter(chapter) "." counter(section) "." counter(subsec) ". ";}
h1 {counter-reset: section subsec;
counter-increment: chapter;}

The
first
h1
element in the document would be
preceded by the text "1.0.0. " because the counters
section
and
subsec
were reset,
but not incremented. This means that if you want the first displayed instance of
an incremented counter to be 0, then you need to reset that counter to
-1
, as
follows:

body {counter-reset: chapter -1;}
h1:before {counter-increment: chapter; content: counter(chapter) ". ";}

You
can do some interesting things with counters. Consider the following
XML:


PRINT "Hello world!"
REM This is what the kids are calling a "comment"
GOTO 10

You
can recreate the traditional format of a BASIC program listing with the following
rules:

code[type="BASIC"] {counter-reset: linenum; font-family: monospace;}
code[type="BASIC"] line {display: block;}
code[type="BASIC"] line:before {counter-increment: linenum;
content: counter(linenum 10) ": ";}

It's
also possible to define a list style for each counter as part of the
counter( )
format. You can do this by adding a
comma-separated
list-style-type
keyword after
the counter's identifier. The following modification of the heading-counter
example is illustrated in
Figure
12-24
:

h1:before {counter-reset: section subsec;
counter-increment: chapter;
content: counter(chapter,upper-alpha) ". ";}
h2:before {counter-reset: subsec;
counter-increment: section;
content: counter(chapter,upper-alpha)"." counter(section) ". ";}
h3:before {counter-increment: subsec;
content: counter(chapter,upper-alpha) "." counter(section) "."
counter(subsec,lower-roman) ". ";}

Figure 12-24. Changing counter styles

Notice that the counter
section
was
not given a style keyword, so it defaulted to the decimal counting style. You can
even set counters to use the styles
disc
,
circle
,
square
, and
none
if you so
desire.

One interesting point to note is that elements with a
display
of
none
do
not increment counters, even if the rule seems to indicate otherwise. In contrast,
elements with a
visibility
of
hidden
do increment
counters:

.suppress {counter-increment: cntr; display: none;}
/* 'cntr' is NOT incremented */
.invisible {counter-increment: cntr; visibility: hidden;}
/* 'cntr' IS incremented */
Counters and
scope

So far, we've seen how to string
multiple counters together to create section-and-subsection counting. Often, this
is something authors desire for nested ordered lists as well, but it would quickly
become clumsy to try to create enough counters to cover deep nesting levels. Just
to get it working for five-level-deep nested lists would require a bunch of rules
like
this:

ol ol ol ol ol li:before {counter-increment: ord1 ord2 ord3 ord4 ord5;
content: counter(ord1) "." counter(ord2) "." counter(ord3) "."
counter(ord4) "." counter(ord5) ".";}

Imagine
writing enough rules to cover nesting up to 50 levels! (I'm not saying you should
nest ordered lists 50 deep. Just follow along for the
moment.)

Fortunately, CSS2.x described the concept of
scope
when it comes to counters. Stated simply, every level
of nesting creates a new scope for any given counter. Scope is what makes it
possible for the following rules to cover nested-list counting in the usual HTML
way:

ol {counter-reset: ordered;}
ol li:before {counter-increment: ordered;
content: counter(ordered) ". ";}

These
rules will all make ordered lists, even those nested inside others, start counting
from 1 and increment each item by one—exactly how it's been done in HTML from the
beginning.

This works because a new instance of the counter
ordered
is created at each level of nesting. So, for
the first ordered list, an instance of
ordered
is created. Then, for every list nested inside the first one, another new instance
is created, and the counting starts anew with each list.

However, you
want ordered lists to count so that each level of nesting creates a new counter
appended to the old: 1, 1.1, 1.2, 1.2.1, 1.2.2, 1.3, 2, 2.1, and so on. This can't
be done with
counter( )
, but it
can
be done with
counters(
)
. What a difference an "s" makes.

To create the
nested-counter style shown in
Figure
12-25
, you need these
rules:

ol {counter-reset: ordered;}
ol li:before {counter-increment: ordered;
content: counters(ordered,".") " - ";}

Figure 12-25. Nested counters

Basically, the keyword
counters(ordered,".")
displays the
ordered
counter from each scope with a period appended, and strings
together all of the scoped counters for a given element. Thus, an item in a
third-level-nested list would be prefaced with the
ordered
value for the outermost list's scope, the scope of the list
between the outer and current list, and the current list's scope, with each of
those followed by a period. The rest of the
content
value causes a space, hyphen, and space to be added after all
of those counters.

As with
counter(
)
, you can define a list style for nested counters, but the same style
applies to all of the counters. Thus, if you changed your previous CSS to read as
follows, the list items in
Figure
12-25
would all use lowercase letters for the counters instead of
numbers:

ol li:before {counter-increment: ordered;
content: counters(ordered,".",lower-alpha) ": ";}

Other books

Unspoken 3 by A Lexy Beck
A Blink of the Screen by Terry Pratchett
Those Across the River by Christopher Buehlman
Take a dip by Wallace, Lacey
Alice-Miranda on Vacation by Jacqueline Harvey
Black Hole Sun by David Macinnis Gill
Two Loves by Sian James