Title: CSS Lists and Counters Module Level 3 Shortname: css-lists Level: 3 Group: CSSWG Status: ED Work Status: Refining ED: https://drafts.csswg.org/css-lists-3/ TR: https://www.w3.org/TR/css-lists-3/ Editor: Elika J. Etemad / fantasai, Apple, http://fantasai.inkedblade.net/contact, w3cid 35400 Editor: Tab Atkins, Google, http://xanthir.com/contact/, w3cid 42199 Former Editor: Ian Hickson, Google, ian@hixie.ch Former Editor: Tantek Çelik, Formerly of Microsoft, tantekc@microsoft.com Previous Version: https://www.w3.org/TR/2020/WD-css-lists-3-20200709/ Previous Version: https://www.w3.org/TR/2019/WD-css-lists-3-20190817/ Previous Version: https://www.w3.org/TR/2019/WD-css-lists-3-20190425/ Previous Version: https://www.w3.org/TR/2014/WD-css-lists-3-20140320/ Previous Version: https://www.w3.org/TR/2011/WD-css3-lists-20110524/ !Contributors: Simon Montagu, AOL-TW/Netscape, smontagu@netscape.com !Contributors: Daniel Yacob, yacob@geez.org !Contributors: Christopher Hoess, choess@stwing.upenn.edu !Contributors: Daniel Glazman, AOL-TW/Netscape, glazman@netscape.com Abstract: This module contains CSS features related to list counters: styling them, positioning them, and manipulating their value.
spec:css-pseudo-4; type:selector; text:::before spec:selectors-4; type:dfn; text:selector spec:infra; type:dfn; text:list text:string
(i) This is the first item. (ii) This is the second item. (iii) This is the third item.Note: Note that this example is far more verbose than is usually needed in HTML, as the UA default style sheet takes care of most of the necessary styling.
This is the first paragraph in this document.
This is a very short document.
This is the end.
This is the first paragraph in this document. Note 1: This is a very short document. This is the end.
This is a long preceding paragraph ...
This is a long following paragraph ...
This is a long preceding paragraph ... i. This is the first item. ii. This is the second item. iii. This is the third item. This is a long following paragraph ...Previously the only way to style a marker was through inheritance; one had to put the desired marker styling on the list item, and then revert that on a wrapper element around the list item's actual contents.
::marker, ::before::marker, ::after::marker { unicode-bidi: isolate; font-variant-numeric: tabular-nums; white-space: pre; text-transform: none; }ISSUE: ''white-space: pre'' doesn't have quite the right behavior; ''text-space-collapse: preserve-spaces'' + ''text-space-trim: discard-after'' might be closer to what's needed here. See discussion in Issue 4448 and Issue 4891. Note: Although the ''::marker'' pseudo-element can represent the [=marker box=] of a ''::before'' or ''::after'' pseudo-element, the [=compound selector=] ''::marker'', which expands to ''*::marker'' [[SELECTORS-4]], will not select these markers-- an [=originating element=] that is a [=pseudo-element=] needs to be explicitly specified in the [=selector=], e.g. ''::before::marker''.
Name: list-style-image Value: <Specifies the marker image, which is used to fill the [=list item’s=] [=marker=] when its 'content' is ''content/normal''. The values are as follows:> | none Initial: none Applies to: list items Inherited: yes Percentages: n/a Computed value: the keyword ''list-style-image/none''or the computed < > Animation type: discrete
li { list-style-image: url("http://www.example.com/ellipse.png") }
Name: list-style-type Value: <Specifies the marker string, which is used to fill the list item’s marker when its 'content' value is ''content/normal'' and there is no [=marker image=]. The values are as follows:> | < > | none Initial: disc Applies to: list items Inherited: yes Percentages: n/a Computed value: specified value Animation type: discrete
ul { list-style-type: "★"; } /* Sets the marker to a "star" character */ p.note { display: list-item; list-style-type: "Note: "; list-style-position: inside; } /* Gives note paragraphs a marker consisting of the string "Note: " */ ol { list-style-type: upper-roman; } /* Sets all ordered lists to use the upper-roman counter-style (defined in the Counter Styles specification [[CSS-COUNTER-STYLES]]) */ ul { list-style-type: symbols(cyclic '○' '●'); } /* Sets all unordered list items to alternate between empty and filled circles for their markers. */ ul { list-style-type: none; } /* Suppresses the marker entirely, unless list-style-image is specified with a valid image. */
Name: list-style-position Value: inside | outside Initial: outside Applies to: list items Inherited: yes Percentages: n/a Computed value: keyword, but see prose Animation type: discreteThis property dictates whether the ''::marker'' is rendered inline, or positioned just outside of the list item. The values are as follows:
<style> ul.compact { list-style: inside; } ul { list-style: outside; } </style> <ul class=compact> <li>first "inside" list item comes first</li> <li>second "inside" list item comes first</li> </ul> <hr> <ul> <li>first "outside" list item comes first</li> <li>second "outside" list item comes first</li> </ul>The above example may be formatted as:
* first "inside" list item comes first * second "inside" list item comes second ======================== * first "outside" list item comes first * second "outside" list item comes second
Name: list-style Value: <<'list-style-position'>> || <<'list-style-image'>> || <<'list-style-type'>> Applies to: list itemsThe 'list-style' property is a shorthand notation for setting the three properties 'list-style-type', 'list-style-image', and 'list-style-position' at the same place in the style sheet.
ul { list-style: upper-roman inside } /* Any UL */ ul ul { list-style: circle outside } /* Any UL child of a UL */
list-style: none disc; /* Sets the image to "none" and the type to "disc". */ list-style: none url(bullet.png); /* Sets the image to "url(bullet.png)" and the type to "none". */ list-style: none; /* Sets both image and type to "none". */ list-style: none disc url(bullet.png); /* Syntax error */
ol.alpha li { list-style: lower-alpha; } ul li { list-style: disc; }The above won't work as expected. If you nest a <{ul}> into an ol class=alpha, the first rule's specificity will make the <{ul}>’s list items use the lower-alpha style.
ol.alpha > li { list-style: lower-alpha; } ul > li { list-style: disc; }These work as intended.
ol.alpha { list-style: lower-alpha; } ul { list-style: disc; }These are even better, since inheritance will transfer the 'list-style' value to the list items.
Name: marker-side Value: match-self | match-parent Initial: match-self Applies to: list items Inherited: yes Percentages: n/a Computed value: specified keyword Animation type: discreteThe 'marker-side' property specifies whether an ''list-style-position/outside'' [=marker box=] is positioned based on the directionality of the list item itself (i.e. its [=originating element=]) or the directionality of the list container (i.e. the [=originating element=]’s parent). In the first case, the position of the marker can vary across items in the same list, based on the directionality assigned to each list item individually; in the second case they will all align on the same side, as determined by the directionality assigned to the list as a whole.
<ul> <li>english one <li dir=rtl>OWT WERBEH <li>english three <li dir=rtl>RUOF WERBEH </ul>
''match-self'' | ''match-parent'' |
---|---|
* english one OWT WERBEH * * english three RUOF WERBEH * |
* english one * OWT WERBEH * english three * RUOF WERBEH |
<Resolving [=counter=] values on a given element is a multi-step process: 1. Existing counters are [=inherit counters|inherited=] from previous elements. 2. New counters are [=instantiated=] ('counter-reset'). 3. Counter values are incremented ('counter-increment'). 4. Counter values are explicitly set ('counter-set'). 5. Counter values are used (''counter()''/''counters()''). UAs may have implementation-specific limits on the maximum or minimum value of a counter. If a counter reset, set, or increment would push the value outside of that range, the value must be clamped to that range.> = reversed( <> )
Name: counter-reset Value: [ <> < >? | < > < >? ]+ | none Initial: none Applies to: all elements Inherited: no Percentages: n/a Computed value: the keyword ''counter-reset/none'' or a list, each item an identifier or a ''reversed()'' function paired with an integer Animation type: by computed value type
User agents are expected to support this property on all media, including non-visual ones. The 'counter-reset' property [=instantiates=] new [=counters=] on an element and sets them to the specified integer values. Its values are defined as follows:
h1 { counter-reset: section -1 } h1 { counter-reset: imagenum 99 }will only reset ''imagenum''. To reset both counters, they have to be specified together:
H1 { counter-reset: section -1 imagenum 99 }The same principles apply to the 'counter-set' and 'counter-increment' properties. See [[css-cascade-4]].
Name: counter-increment Value: [ <> < >? ]+ | none Initial: none Applies to: all elements Inherited: no Percentages: n/a Computed value: the keyword ''counter-reset/none'' or a list, each item an identifier paired with an integer Animation type: by computed value type
User agents are expected to support this property on all media, including non-visual ones.
Name: counter-set Value: [ <> < >? ]+ | none Initial: none Applies to: all elements Inherited: no Percentages: n/a Computed value: the keyword ''counter-reset/none'' or a list, each item an identifier paired with an integer Animation type: by computed value type
User agents are expected to support this property on all media, including non-visual ones. The 'counter-increment' and 'counter-set' properties manipulate the value of existing [=counters=]. They only [=instantiate=] new counters if there is no counter of the given name on the element yet. Their values are defined as follows:
This example shows a way to number chapters and sections with "Chapter 1", "1.1", "1.2", etc.
h1::before { content: "Chapter " counter(chapter) ". "; counter-increment: chapter; /* Add 1 to chapter */ counter-reset: section; /* Set section to 0 */ } h2::before { content: counter(chapter) "." counter(section) " "; counter-increment: section; }
ol { counter-reset: item } li { display: block } li::before { content: counter(item) ". "; counter-increment: item }In this example, an ol will create a counter, and all children of the ol will refer to that counter. If we denote the nth instance of the ''item'' counter by itemn, then the following HTML fragment will use the indicated counters.
<ol>
item0 is created, set to 0
<li>
item0 is incremented to 1<li>
item0 is incremented to 2
<ol>
item1 is created, set to 0, nested in item0
<li>
item1 is incremented to 1<li>
item1 is incremented to 2<li>
item1 is incremented to 3
<ol>
item2 is created, set to 0, nested in item1
<li>
item2 is incremented to 1</ol>
<li>
item1 is incremented to 4
<ol>
item3 is created, set to 0, nested in item1
<li>
item3 is incremented to 1</ol>
<li>
item1 is incremented to 5</ol>
<li>
item0 is incremented to 3<li>
item0 is incremented to 4</ol>
<ol>
item4 is created, set to 0
<li>
item4 is incremented to 1<li>
item4 is incremented to 2</ol>
<ul style='counter-reset: example 0;'> <li id='foo' style='counter-increment: example;'> foo <div id='bar' style='counter-increment: example;'>bar</div> </li> <li id='baz'> baz </li> </ul>Recall that [=tree order=] turns a document tree into an ordered list, where an element comes before its children, and its children come before its next sibling. In other words, for a language like HTML, it's the order in which the parser encounters start tags as it reads the document. In here, the <{ul}> element establishes a new counter named ''example'', and sets its value to ''0''. The #foo element, being the first child of the <{ul}>, inherits this counter. Its parent is also its immediately preceding element in [=tree order=], so it inherits the value ''0'' with it, and then immediately increments the value to ''1''. The same happens with the #bar element. It inherits the ''example'' counter from #foo, and inherits the value ''1'' from it as well and increments it to ''2''. However, the #baz element is a bit different. It inherits the ''example'' counter from the #foo element, its previous sibling. However, rather than inheriting the value ''1'' from #foo along with the counter, in inherits the value ''2'' from #bar, the previous element in [=tree order=]. This behavior allows a single counter to be used throughout a document, continuously incrementing, without the author having to worry about the nested structure of their document.
h2 { counter-increment: count2; } h2.secret { display: none; }
ol.evens > li { counter-increment: list-item 2; }A three-item list would be rendered as
2. First Item 4. Second Item 6. Third Item
ol > li::marker { content: counters(list-item,'.') '.'; }Nested lists using this rule would be rendered like
1. First top-level item 5. Second top-level item, value=5 5.3. First second-level item, list start=3 5.4. Second second-level item, list start=3 5.4.4. First third-level item in reversed list 5.4.3. Second third-level item in reversed list 5.4.2. Third third-level item in reversed list 5.4.1. Fourth third-level item in reversed list 5.5. Third second-level item, list start=3 6. Third top-level itemgiven markup such as
<where <> = <> | < > < > = counter( < >, < >? ) < > = counters( < >, < >, < >? )
H1::before { content: counter(chno, upper-latin) ". " } /* Generates headings like "A. A History of Discontent" */ H2::before { content: counter(section, upper-roman) " - " } /* Generates headings like "II - The Discontent Part" */ BLOCKQUOTE::after { content: " [" counter(bq, decimal) "]" } /* Generates blockquotes that end like "... [3]" */ DIV.note::before { content: counter(notecntr, disc) " " } /* Simply generates a bullet before every div.note */ P::before { content: counter(p, none) } /* inserts nothing */
<ul> <li>one</li> <li>two <ul> <li>nested one</li> <li>nested two</li> </ul> </li> <li>three</li> </ul> <style> li::marker { content: '(' counters(list-item,'.') ') '; } </style>The preceding document should render something like this:
(1) one (2) two (2.1) nested one (2.2) nested two (3) three
<h1>First H1</h1> ... <h2>First H2 in H1</h2> ... <h2>Second H2 in H1</h2> ... <h3>First H3 in H2</h3> ... <h1>Second H1</h1> ... <h2>First H2 in H1</h2> ... <style> body { counter-reset: h1 h2 h3; } h1 { counter-increment: h1; counter-reset: h2 h3;} h2 { counter-increment: h2; counter-reset: h3; } h3 { counter-increment: h3; } h1::before { content: counter(h1,upper-alpha) ' '; } h2::before { content: counter(h1,upper-alpha) '.' counter(h2,decimal) ' '; } h3::before { content: counter(h1,upper-alpha) '.' counter(h2,decimal) '.' counter(h3,lower-roman) ' '; } </style>The preceding document should render something like this:
A First H1 ... A.1 First H2 in H1 ... A.2 Second H2 in H1 ... A.2.i First H3 in H2 ... B Second H1 ... B.1 First H2 in H1 ...
/* Set up list items */ li { display: list-item; /* implies 'counter-increment: list-item' */ } /* Set up ol and ul so that they scope the list-item counter */ ol, ul { counter-reset: list-item; } /* Default list style types for lists */ ol { list-style-type: decimal; } ul { list-style-type: toggle(disc, circle, square); } /* The type attribute on ol and ul elements */ ul[type="disc"] { list-style-type: disc; } ul[type="circle"] { list-style-type: circle; } ul[type="square"] { list-style-type: square; } ol[type="1"] { list-style-type: decimal; } ol[type="a"] { list-style-type: lower-alpha; } ol[type="A"] { list-style-type: upper-alpha; } ol[type="i"] { list-style-type: lower-roman; } ol[type="I"] { list-style-type: upper-roman; } /* The start attribute on ol elements */ ol[start] { counter-reset: list-item calc(attr(start integer, 1) - 1); } /* The value attribute on li elements */ li[value] { counter-set: list-item attr(value integer, 1); } /* Handling reversed lists */ ol[reversed] { counter-reset: reversed(list-item); } ol[reversed][start] { counter-reset: reversed(list-item) calc(attr(start integer) + 1); } /* Box Model Rules */ ol, ul { display: block; margin-block: 1em; marker-side: match-parent; padding-inline-start: 40px; } ol ol, ol ul, ul ul, ul ol { margin-block: 0; } li { text-align: match-parent; }
This specification is made possible by input from Aharon Lanin, Arron Eicholz, Brad Kemper, David Baron, Emilio Cobos Álvarez, Mats Palmgren, Oriol Brufau, Simon Sapin, Xidorn Quan
As described in the introduction section, there are significant changes in this module when compared to CSS2.1.