[csswg-drafts] Page-based counters not working with forced breaks. (#4760)

faceless2 has just created a new issue for https://github.com/w3c/csswg-drafts:

== Page-based counters not working with forced breaks. ==
(migrated from second half of #3520 to make a single point per issue, and updated)

The description of how page-based counters works does not exactly match current implementations when dealing with forced breaks. Current expectation in a number of user-agents is this:
```css
@page {
  @top-center {
    content: counter(page);
  }
}
section {
  counter-reset: page 9;
  break-before: page;
}
```
```html
<section>This is on page 9</section>
```
But that is not the behaviour currently described by the spec.

We need to start with this paragraph from [css-page-3](https://drafts.csswg.org/css-page-3/#page-based-counters)

> If a counter that has not been reset or incremented within the margin context or the page context is used by counter() or counters() in the margin context, then the resultant value is exactly as if the page-margin box were an element within the document at the start of the page, inside the deepest element in the normal flow that spans the page break. Use of the counter in this way does not affect the calculation of the counter’s value.
 

We're told this applies _if the counter has not been reset or incremented in the page context_, but it is relevant if the counter is incremented too - how else would we know what value we're incrementing? It's really just telling us where in the document to take the initial counter values from.

Lets say I have the following HTML. I've used "section" as a counter to illustrate this could apply to any counter, not just the obvious choice of "page".
```css
@page {
  counter-increment: section 1;
  @top-center {
    content: counter(section);
  }
}
:root {
  counter-reset: section 5
}
div::before {
  content: counter(section)
}
#div2 {
  break-before: page
}
```
```html
<html>
  <body>
    <div id="div1"></div>
    <div id="div2"></div>
  </body>
</html>
``` 
so there is a page break between div1 and div2.

![output-001](https://user-images.githubusercontent.com/989243/51131545-f5e92800-1827-11e9-94bc-f962742d047a.png)

The page-content rules are effectively inserting some boxes into the tree at the position described above: _inside the deepest element in the normal flow that spans the page break_:

![output-002](https://user-images.githubusercontent.com/989243/51131978-06e66900-1829-11e9-87ef-359e334e082b.png)

and the counter values are as shown in the boxes: 5 before the page break, 6 after.

But.

If I modify the style to add `#div2 { counter-reset: section 10}`, then the expectation is that div2 will be on page 10, but it's not.

![output-003](https://user-images.githubusercontent.com/989243/51132497-45305800-182a-11e9-9bd3-cb077d33f132.png)

This expectation seems pretty widespread - some examples:
* https://github.com/web-platform-tests/wpt/blob/master/css/css-page/page-counters-000.xht
* http://css4.pub/2015/malthus/essay.html (Prince)
* https://www.oxygenxml.com/events/2015/DITA_OT_Day_2015/Slides/dita-css.pdf (Oxygen XML)
* https://github.com/Kozea/WeasyPrint/issues/93 - discussion.
and I'm pretty sure it's how AH works as well.

This was also suggested in @fantasai's [original text on this](https://lists.w3.org/Archives/Public/www-style/2009Apr/0227.html):
> Copy counters in scope at all break points, in document order, into the @page counter scope, obscuring any counters of the same name there. (In CSS3, this step is not required.)

This somewhat depends on whether "break points" is the point between elements, or specifically means break-causing elements. Either way, this step has disappeared. The phrase "In CSS3 this step is not required" makes me think this is intentional, but without it the described behaviour does not match existant behaviour from CSS2.

I think the functionality described by this step is necessary. There is a body of existing work that expects it, and while you can achieve the same result without it, the process of resetting a page counter at the start of a chapter becomes very complex. 

```html
<style>
@page :nth(1 of chapter) {
    counter-set: page 1; /* Edited, original had counter-reset by mistake */
    counter-increment: 0;
}
.chapter {
    break-before: page;
    page: chapter;
}
</style>
<section class="chapter">
```

You can't reset in @page:first as this [refers](https://lists.w3.org/Archives/Public/www-style/2013Oct/0567.html) to the first page in the document. The [:nth selector](https://drafts.csswg.org/css-gcpm/#document-sequence-selector) is very new and I don't think is supported by anyone, yet.

To sum up:
* Prince and AH both propagate a `counter-reset: page` on a break-causing element to the page margin.
* There is a long-held presumption that this is how it should work, and a clear requirement that it's possible.
* The alternative is complex and relies on a very new selector.

I'm sure there's a backstory to this I'm unaware of, and no doubt other implementations too. But to my mind this could be addressed by adding a paragraph:

> Copy all counters from any elements on the page that have a forced page break, in document order, to the @page counter scope, obscuring any counters of the same name.

Limiting it to break-causing elements will cover the case where an element has `break-before: page; counter-reset: page 99` while removing a lot of the complexity over determining which element on the page has precedence.

Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/4760 using your GitHub account

Received on Friday, 7 February 2020 18:55:41 UTC