Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(css): clarify counter-set vs counter-reset #35258

Merged
merged 4 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 45 additions & 3 deletions files/en-us/web/css/counter-reset/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ counter-reset: unset;
The `counter-reset` property accepts a list of one or more space-separated counter names or the keyword `none`. For counter names, regular counters use the format `<counter-name>`, and reversed counters use `reversed(<counter-name>)`, where `<counter-name>` is a {{cssxref("custom-ident", "&lt;custom-ident&gt;")}} or `list-item` for the built-in {{HTMLElement("ol")}} counter. Optionally, each counter name can be followed by an `<integer>` to set its initial value.

- {{cssxref("custom-ident", "&lt;custom-ident&gt;")}}
- : Specifies the counter name to create and initialize using the {{cssxref("custom-ident", "&lt;custom-ident&gt;")}} format.
- : Specifies the counter name to create and initialize using the {{cssxref("custom-ident", "&lt;custom-ident&gt;")}} format. The `reversed()` functional notation can be used to mark the counter reversed.
- {{cssxref("&lt;integer&gt;")}}
- : The value to reset the counter to on each occurrence of the element.
- : The initial value to set on the newly created counter.
Defaults to `0` if not specified.
- `none`
- : Specifies that no counter initialization should occur.
Expand All @@ -57,7 +57,8 @@ The `counter-reset` property accepts a list of one or more space-separated count

The `counter-reset` property can create both regular and, in browsers that support it, reversed counters. You can create multiple regular and reversed counters, each separated by a space. Counters can be a standalone name or a space-separated name-value pair.

After creating a counter using `counter-reset`, you can adjust its value by using the {{cssxref("counter-set")}} property. This is counterintuitive because, despite its name, the `counter-reset` property is used for creating and initializing counters, while the `counter-set` property is used for resetting the value of an existing counter.
> [!WARNING]
> There is [a difference between `counter-reset` and `counter-set` properties](/en-US/docs/Web/CSS/CSS_counter_styles/Using_CSS_counters#difference_between_counter-set_and_counter-reset). After creating a counter using `counter-reset`, you can adjust its value by using the {{cssxref("counter-set")}} property. This is counterintuitive because, despite its name, the `counter-reset` property is used for creating and initializing counters, while the `counter-set` property is used for resetting the value of an existing counter.
Comment on lines +60 to +61
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about this warning. You can reset a counter using:

h1::before {
    content: "Chapter " counter(chapter) ". ";
    counter-increment: chapter;  /* Add 1 to chapter */
    counter-reset: section;      /* Set section to 0 */
}

See https://drafts.csswg.org/css-lists/#example-838dca3e

So it might be better to say counter-set is good for directly setting values (like skipping increments), and counter reset is for initializing and resetting.

I would keep "After creating a counter using counter-reset, you can adjust its value by using the {{cssxref("counter-set")}} property." but I wouldn't mention the others, especially "counter-set property is used for resetting".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the spec example has a typo, and it should use counter-set instead.

counter reset is for initializing and resetting.

The spec doesn't say counter-reset updates the existing counter if a counter with the same name exists. So counter-reset can't actually reset the same counter.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, it's in https://drafts.csswg.org/css-lists/#increment-set

Manipulating Counter Values: the counter-increment and counter-set properties

dear oh dear 😅


Setting `counter-increment: none` on a selector with greater specificity overrides the creation of the named counter set on selectors with lower specificity.

Expand Down Expand Up @@ -134,6 +135,47 @@ ol {

Using `counter-reset`, we set the implicit `list-item` counter to start counting at `3` for every `ol`. Then, the first item would be numbered 4, second would be numbered 5, etc., similar to the effect of writing [`<ol start="4">`](/en-US/docs/Web/HTML/Element/ol#start) in HTML.

### Using a reverse counter

In the following example, we've declared a reversed counter named 'priority'. The counter is being used to number five tasks.

```html
<ul class="stack">
<li>Task A</li>
<li>Task B</li>
<li>Task C</li>
<li>Task D</li>
<li>Task E</li>
</ul>
```

```css hidden
@supports not (counter-reset: reversed(priority)) {
.stack {
display: none;
}
body::after {
content: "Your browser doesn't support the reversed counters yet.";
}
}
```

```css
li::before {
content: counter(priority) ". ";
counter-increment: priority -1;
}

.stack {
counter-reset: reversed(priority);
list-style: none;
}
```

{{EmbedLiveSample("Using a reverse counter", 140, 150)}}

In the output, the items are numbered in reversed order from 5 to 1. Notice in the code we haven't specified the counter's initial value. The browser automatically calculates the initial value at layout-time using the counter increment value.

## Specifications

{{Specifications}}
Expand Down
6 changes: 4 additions & 2 deletions files/en-us/web/css/counter-set/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ browser-compat: css.properties.counter-set

{{CSSRef}}

The **`counter-set`** [CSS](/en-US/docs/Web/CSS) property sets [CSS counters](/en-US/docs/Web/CSS/CSS_counter_styles/Using_CSS_counters) to the given values.
The **`counter-set`** [CSS](/en-US/docs/Web/CSS) property sets [CSS counters](/en-US/docs/Web/CSS/CSS_counter_styles/Using_CSS_counters) on the element to the given values.

The `counter-set` property will create a new counter for each named counter in the list of space-separated counter and value pairs that doesn't already exist. If a named counter in the list is missing a value, the value of the counter will be set to `0`.
If the counters don't exist the `counter-set` property creates a new counter for each named counter in the list of space-separated counter and value pairs. However, to create a new counter it is recommended to use the {{cssxref("counter-reset")}} CSS property.

If a named counter in the list is missing a value, the value of the counter will be set to `0`.

{{EmbedInteractiveExample("pages/css/counter-set.html")}}

Expand Down
168 changes: 164 additions & 4 deletions files/en-us/web/css/css_counter_styles/using_css_counters/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,21 @@ spec-urls: https://drafts.csswg.org/css-lists/#auto-numbering
{{CSSRef}}

**CSS counters** let you adjust the appearance of content based on its location in a document.
For example, you can use counters to automatically number the headings in a webpage, or to change the numbering on ordered lists.
For example, you can use counters to automatically number the headings on a webpage or to change the numbering on ordered lists.

Counters are, in essence, variables maintained by CSS whose values may be incremented or decremented by CSS rules that track how many times they're used. The following things affect the counter values on an element:

1. Counters are [inherited](#counter_inheritance_and_propagation) from the parent element or received from a previous sibling.
2. New counters are instantiated using {{cssxref("counter-reset")}} property.
3. Counters are incremented using {{cssxref("counter-increment")}} property.
4. Counters are directly set to a value using the {{cssxref("counter-set")}} property.

Counters are, in essence, variables maintained by CSS whose values may be incremented or decremented by CSS rules that track how many times they're used.
You can define your own named counters, and you can also manipulate the `list-item` counter that is created by default for all ordered lists.

## Using counters

To use a counter it must first be initialized to a value with the {{cssxref("counter-reset")}} property.
The counter's value can then be increased or decreased using {{cssxref("counter-increment")}} property.
The counter's value can be increased or decreased using the {{cssxref("counter-increment")}} property and can be directly set to a specific value using the {{cssxref("counter-set")}} property.
The current value of a counter is displayed using the {{cssxref("counter", "counter()")}} or {{cssxref("counters", "counters()")}} function, typically within a [pseudo-element](/en-US/docs/Web/CSS/Pseudo-elements) {{CSSxRef("content")}} property.

Counters can only be set, reset, or incremented in elements that generate boxes.
Expand Down Expand Up @@ -51,7 +57,15 @@ h3::before {
}
```

You can specify the value to increment or decrement the counter after the counter name, using a positive or negative number.
You can specify the increment or decrement amount after the counter name. It can be a positive or negative number, but defaults to `1` if no integer is provided.

Apart from increment or decrement, counters can also be explicitly set to a value using {{cssxref("counter-increment")}} property.

```css
.done::before {
counter-set: section 20;
}
```

The counter's name must not be `none`, `inherit`, or `initial`; otherwise the declaration is ignored.

Expand Down Expand Up @@ -129,6 +143,152 @@ The counter value is decreased by specifying a negative value for {{cssxref("cou
> You can also use {{cssxref("counter-increment")}} to decrement a non-reversed counter.
> The main benefit of using a reversed counter is the default initial value, and that the `list-item` counter automatically decrements reversed counters.

### Counter inheritance and propagation

Each element or pseudo-element has a set of counters in the scope of that element. Initial counters in the set are received from the element's parent and the preceding sibling. The counter values are received from the last descendent of the previous sibling, the last sibling, or the parent.

When an element declares a counter, the counter is nested inside the counter with the same name received from the parent. If the parent doesn't have a counter with the same name then the counter is added to the element's counters set as it is. A counter with the same name received from the previous sibling is removed from the counters set.

The {{cssxref("counter", "counter()")}} function retrieves the innermost counter with the provided name. And the {{cssxref("counters", "counters()")}} function retrieves the entire counter tree with the given name.

In the following example, we are demoing an inherited counter named `primary` and a sibling counter named `secondary`. All the `<div>` elements display their counters using the `counters()` function. Note that all the counters have been created using `counter-reset` property, and none of the counters have been incremented.

```html
<section>
counter-reset: primary 3
<div>A</div>
<div>B</div>
<div>C</div>
<div class="same-primary-name">D</div>
<span> counter-reset: primary 6</span>
<div>E</div>
<div class="new-secondary-name">F</div>
<span> counter-reset: secondary 5</span>
<div>G</div>
<div>H</div>
<div class="same-secondary-name">I&nbsp;</div>
<span> counter-reset: secondary 10</span>
<div>J&nbsp;</div>
<div>K</div>
<section></section>
</section>
```

```css hidden
.same-primary-name,
.new-secondary-name,
.same-secondary-name {
display: inline-block;
}

@counter-style style {
system: numeric;
symbols: "" "1" "2" "3" "4" "5" "6" "7" "8" "9" "10";
}
```

```css
/* create 'primary' counter on divs' parent */
section {
counter-reset: primary 3;
}

div::after {
content: " ('primary' counters: " counters(primary, "-", style)
", 'secondary' counters: " counters(secondary, "-", style) ")";
color: blue;
}

/* create new 'primary' counter */
.same-primary-name {
counter-reset: primary 6;
}

/* create 'secondary' counter on div 'F' */
.new-secondary-name {
counter-reset: secondary 5;
}

/* override the sibling 'secondary' counter */
.same-secondary-name {
counter-reset: secondary 10;
}
```

{{EmbedLiveSample("Counter inheritance and propagation", "100%", 250)}}

The section element initializes a counter named `primary` with value `3`, and all the child `<div>`s receive the inherited `primary` counter. The element 'D' creates a new `primary`(value `6`) counter which gets nested in the counter received from the parent, so the element has two counters named `primary` with values `3` and `6`.

The element 'F' creates the `secondary`(value `5`) counter for the first time, and it passes the counter to the next sibling 'G'. The element 'G' passes the counter to the next element 'H' and so on. Next, the element 'I' creates a new counter with the same name `secondary`(value `10`), but it drops the `secondary`(value `5`) counter received from the previous sibling 'H' and passes its own counter to 'J'.

### Difference between counter-set and counter-reset

The {{cssxref("counter-set")}} property updates an existing counter and if no counter with the name exists then a new counter is instantiated. The {{cssxref("counter-reset")}} property _always_ creates a new counter.

In the following example, we have two sub-lists inside a parent list. Each list item has been numbered using a counter named 'item'. The first sub-list uses {{cssxref("counter-set")}} property and the second sub-list uses {{cssxref("counter-reset")}} property to change the 'item' counter.

```html
<ul class="parent">
<li>A</li>
<li>B</li>
<li>
C (the counter updated using `counter-set`)
<ul class="sub-list-one">
<li>sub-A</li>
<li>sub-B</li>
</ul>
</li>
<li>D</li>
<li>
E (a new counter created using `counter-reset`)
<ul class="sub-list-two">
<li>sub-A</li>
<li>sub-B</li>
<li>sub-C</li>
</ul>
</li>
<li>F</li>
<li>G</li>
</ul>
```

```css hidden
ul {
list-style: none;
}
```

```css
/* create a new counter for the first time */
.parent {
counter-reset: item 0;
}

/* increment the counter on each list item */
li {
counter-increment: item;
}

/* show numbers on list items */
li::before {
content: counter(item) " ";
}

/* change the existing counter value */
.sub-list-one {
counter-set: item 10;
}

/* change the counter value */
.sub-list-two {
counter-reset: item 0;
}
```

{{EmbedLiveSample("Difference between counter-set and counter-reset", "100%", 300)}}

Notice how the first sub-list items start receiving numbers from `11`, and the numbering is continued in the parent list. This is because the `counter-set` property updates the same 'item' counter declared on the `.parent` element. Then notice how the second sub-list items receive new numbering starting from '1' and the parent list items after it don't carry forward the numbering. This is because the `counter-reset` property created a new counter with the same name so the parent list items kept using the old counter.

### List item counters

Ordered lists, as created using {{HTMLElement("ol")}} elements, implicitly have a counter named `list-item`.
Expand Down