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

Should LI under OL respect style containment. #4808

Open
vmpstr opened this issue Jul 29, 2019 · 21 comments
Open

Should LI under OL respect style containment. #4808

vmpstr opened this issue Jul 29, 2019 · 21 comments

Comments

@vmpstr
Copy link
Member

vmpstr commented Jul 29, 2019

Currently, with CSS counters, elements that are underneath a "contain: style" element do not consider counter values above such elements. In other words the counters are contained to the subtree of the "contain: style" element.

This, however, currently does not apply to LIs under OL, since they are not using CSS counters and are defined (as far as I can tell) in terms of DOM structure.

Should those also respect style containment and not consider LI counts outside of a style contained elements?

For instance,

<ol>
  <li></li>
  <li></li>
  <div style="contain: style;">
     <li></li>
     <li></li>
  </div>
  <li></li>
  <li></li>
</ol>

This is currently

1.
2.
3.
4.
5.
6.

If style containment is respected, then presumably these counts would be

1.
2.
1.
2.
3.
4.

One issue is that the LIs under a style contained elements should still be aware that they are descendants of an OL element, as opposed a UL element, which peeks outside of the style contained element, but I think that's acceptable.

Without containment considerations, it may be difficult for UAs to skip some optimizations that would otherwise not be user visible. As an example, the layout or maybe even DOM manipulations of an offscreen element, with "sufficient" containment, can normally be skipped if the UA can prove that this work will not affect the visual output of the page. If LIs are present though under an OL element, we can't satisfy this guarantee since the counts outside of the contained elements consider counts inside the contained element, so work can't be skipped.

/cc @annevk @chrishtr @domenic

@Yay295
Copy link
Contributor

Yay295 commented Jul 30, 2019

I mean, that's technically not even valid code. List elements can only have list items as direct descendants.

@tabatkins
Copy link
Collaborator

That's not relevant here. HTML validity aside, it still produces a document tree as one would expect, and CSS applies to that regardless.

@chrishtr
Copy link
Contributor

chrishtr commented Jul 31, 2019

That's not relevant here. HTML validity aside, it still produces a document tree as one would expect, and CSS applies to that regardless.

I think the most important fact is that in fact the HTML @vmpstr provided does currently generate a list that looks like:

1.
2.
3.
4.
5.
6.

in Chrome and Firefox. Changing the HTML parser to not do that would break content. If the HTML spec says otherwise, then it would need to be adjusted.

@chrishtr
Copy link
Contributor

chrishtr commented Jul 31, 2019

Proposal: change the algorithm for determining the list owner [1] of a list item as follows:

Add a new sentence:

The ordinal value producing ancestor of an element is the closest ancestor of the element which is an ol, ul, menu, or has a computed value of 'contain' which contains 'style'.

Change step 3 to:

"If the element has an ol, ul, or menu ancestor, set ancestor to the ordinal value producing ancestor."

[1] https://html.spec.whatwg.org/#list-owner

@vmpstr
Copy link
Member Author

vmpstr commented Aug 1, 2019

I think that's generally what I was thinking too. One problem is that under
https://html.spec.whatwg.org/#attr-li-value it specifically calls out ol as the li's owner for the purposes of having a value attribute. Presumably, we still want the value attribute even if ol is an ancestor, but not the parent?

On the other hand (and to the first comment's point), the first paragraph under https://html.spec.whatwg.org/#the-li-element says

If its parent element is an ol, ul, or menu element, then the element is an item of the parent
element's list, as defined for those elements. Otherwise, the list item has no defined list-related
relationship to any other li element.

If it has no defined list-related relationship to anything, then strictly speaking any behavior is spec compliant.

@tabatkins
Copy link
Collaborator

Changing the HTML parser to not do that would break content.

Only if there is currently content depending on style containment of ol ancestors not working on the default list-item counter. That's... very much not something I'd assume is true ab initio.

@chrishtr
Copy link
Contributor

chrishtr commented Aug 1, 2019

Only if there is currently content depending on style containment of ol ancestors not working on the default list-item counter. That's... very much not something I'd assume is true ab initio.

Oh I was just referring to the point about

  1. being not valid HTML, not about style containment.

@domenic
Copy link
Member

domenic commented Aug 1, 2019

I don't have too much expertise here, but in general I support tweaking the definition of how ordinal values are determined in weird cases in order to support better performance possibilities and harmonize with CSS concepts like contain: style.

I'll CC @emilio and @matspalmgreen because I believe they were involved in Gecko's recent work on list counters, and I still find the spec/implementation situation confusing: 1, 2, 3. Given that confusion, I'm not sure how to approach this from a spec editor perspective, but I welcome suggestions.

@tabatkins
Copy link
Collaborator

Ah, you were agreeing with me, I see. ^_^

@emilio
Copy link
Contributor

emilio commented Aug 2, 2019

Is the list-owner concept (and all the ordinal value stuff) necessary at all if you use CSS counters?

If html deferred to the CSS lists in this sense, then style containment and co would automatically apply.

That way the "list owner" concept is implemented is just via ol, ul, menu { counter-reset: list-item }.

I think spec-wise we should make HTML point to CSS lists, and move https://drafts.csswg.org/css-lists/#ua-stylesheet to HTML, fixed to account for menu.

@emilio
Copy link
Contributor

emilio commented Aug 2, 2019

cc @MatsPalmgren

@domenic
Copy link
Member

domenic commented Aug 2, 2019

That's the confusion I refer to. It seems some people (unsure if Gecko in particular, or CSSWG in general) want to make such a change. So I'm not sure how to proceed with updating the HTML spec in light of such conflict.

Note that the example appendix UA stylesheet does not cover browsers' behaviors, e.g. as noted in https://groups.google.com/d/msg/mozilla.dev.platform/5V37Xg997Mg/Ei-2XJNJAwAJ by @MatsPalmgren, or the explicit **magic** annotations in the spec, or the return values of getComputedStyle(). (Although from what I understand Gecko might be giving getComputedStyle() values that match the informative CSS Lists example appendix instead of the normative HTML UA stylesheet; I'm not sure where that landed.)

@vmpstr
Copy link
Member Author

vmpstr commented Aug 2, 2019

Small hiccup with implementing this as counters is that because of counter scoping, I think each ol would have to have its own counter. (It also needs counter-set to mimic li's value attribute, but that's a working draft right now so presumably we can use it). Consider:

<!doctype html>
<style>
.li:before {
  content: counter(c) ".";
  counter-increment: c;
}
.ol {
  counter-reset: c;
}
.value5:before {
  counter-set: c 5;
}
.value10:before {
  counter-set: c 10;
}
</style>

<p>OL and LI</p>
<ol>
  <li></li>
  <li value=5></li>
  <ol>
    <li></li>
    <li value=10></li>
    <li></li>
  </ol>
  <li></li>
  <li></li>
</ol>

<p>DIVs</p>
<div class=ol>
  <div class=li></div>
  <div class="li value5"></div>
  <div class=ol>
    <div class=li></div>
    <div class="li value10"></div>
    <div class=li></div>
  </div>
  <div class=li></div>
  <div class=li></div>
</div>

In Chrome (which doesn't have counter-set implemented), OL and LIs produces

1. 
5. 
  1.
  10.
  11.
6.
7.

which I think is correct.

DIVs produces (indentation added by me)

1.
2.
  1.
  2.
  3.
4.
5.

First, obviously it ignores the counter-set, but also "4." is referencing a counter "c" which is defined by the outer .ol, but was reset by the inner .ol.

In Firefox, LIs and DIVs produce the same result, but I don't think the LI version is correct:

1.
5.
  1.
  10.
  11.
12.
13.

The 12 and 13 are being affected by the inner OL it seems, for likely the same reason as counters

I might be missing some trick, but I couldn't figure this out with just a single counter. Overall, I think the spirit of using the counters is correct, but we need to be careful with the details

@tabatkins
Copy link
Collaborator

First, obviously it ignores the counter-set, but also "4." is referencing a counter "c" which is defined by the outer .ol, but was reset by the inner .ol.

That's correct behavior, due to your weird markup.

Note that the "inner" ol is a direct child of the outer ol; it's not wrapped in an li like it would need to be for valid markup. Thus, it's a sibling of all the lis; in particular, the following lis are siblings, which mean they'll see the counter the inner ol created (which shadows the outer one). So both Chrome and Firefox have correct behavior for the div case, and Firefox's ol behavior is a direct consequence of it actually using counters, so it matches the div case.

If you correctly nest the inner ol into an li (and similarly nest your div.ol into a div.li), you'll see both cases render identically in both browsers.

@tabatkins
Copy link
Collaborator

That's the confusion I refer to. It seems some people (unsure if Gecko in particular, or CSSWG in general) want to make such a change. So I'm not sure how to proceed with updating the HTML spec in light of such conflict.

I'm open to whatever. To the extent that HTML's lists need magic, I'm happy to spec magic, or allow HTML to spec magic. I'd prefer that we get as far as possible with CSS functionality, just because it makes things simpler.

Firefox has been using actual CSS for its counters for a while, and apparently hasn't seen any complaints. That suggests we can do away with a lot of magic; currently we're only left with the starting value of a reversed list, and probably gCS() serialization.

@vmpstr
Copy link
Member Author

vmpstr commented Aug 2, 2019

Hmm, it's a bit disappointing that counter and non-counter implementations of ol/li would differ on the output of the markup that I have.

TBH, I still fail to see how "12." is a correct value in the current li's spec. That being said, since it's incorrect markup, I'm guessing all bets are off.

@tabatkins
Copy link
Collaborator

Hmm, it's a bit disappointing that counter and non-counter implementations of ol/li would differ on the output of the markup that I have.

Is it that surprising that an impl using totally different tech would produce different results?

TBH, I still fail to see how "12." is a correct value in the current li's spec. That being said, since it's incorrect markup, I'm guessing all bets are off.

No, again, it being incorrect markup is irrelevant. It's a perfectly reasonable DOM tree; the only thing that it being invalid HTML does it mean that some of your naive (but normally reasonable!) assumptions about how counters work don't apply.

In particular:

  1. The first ol instantiates a counter named "c", and gives it the value "0".

  2. The first li increments it to 1.

  3. The second li increments it to 2.

  4. The second li's ::before then sets it to 5. (We would have the same result if you'd done the 'counter-set' on the li itself, as it turns out; 'counter-set' is specified to happen after 'counter-increment' precisely to make this behavior more intuitive.)

  5. The second ol now instantiates a new counter named "c". There is already a "c" counter, but it comes from an ancestor, not a previous sibling, so it's left alone and the new counter appends to the counter list after it. We now have a "nested" counter.

  6. The second ol’s child lis do some incrementing and setting, all working on the second "c" counter, as expected.

  7. After the second ol closes,it has a following sibling li. Per the spec, it grabs its set of counters from its preceding sibling (the second ol), the fills in those counter's values from the matching ones on the preceding element in tree order (the last li child of the second ol). So it has "c 5, c11". It then increments "c", which continues to work on the second "c", giving it "c 5, c 12", and then counter(c) displays 12.


If you'd written valid HTML and nested the second ol inside of an li like HTML expects, then the second "c" counter that it created wouldn't be visible to the following li; the li would grab its counters from its preceding sibling (the new li that wraps the second ol), then populate its values from the preceding element in tree order (still the last child li of the second ol), but since it only has the first "c" counter, it'll only grab the value "5". Then it'll increment that and display a 6, as you probably expect (and as Chrome does for ol/li, which are implemented fully with non-CSS magic).

@tabatkins
Copy link
Collaborator

Counter behavior is a bit complex, but it follows from attempting to make two use-cases work:

  1. Things like whole-document "figure 1" counters: you can just 'counter-reset: figure' at the root element, then increment as necessary deep in the tree. Because counter values are taken from the preceding element in tree order, that increment will get carried forward and back up and down as necessary until you hit the next figure, etc.

  2. Things like "Section 1.2" in <hX> tags, which are siblings of each other if you're using flat document markup rather than nested sections/etc. With the following markup:

    body { counter-reset: h1 h2; }
    h1 { counter-reset: h2; counter-increment: h1; }
    h2 { counter-increment: h2; }

    The first h1 increments its "h1" counter to 1 and resets its "h2" counter. The following h2s then increment the "h2" counter. The second h1 resets the "h2" counter again; because the preceding version of "h2" came from a previous sibling, it throws it away before it instantiates a new one.

    This behavior could be more easily achieved today with 'counter-set' instead, but for some reason we didn't think of creating that property back in the day, so we got this sorta hacky counter-reset behavior instead.

@MatsPalmgren
Copy link

In a CSS rendering engine, the ordinal value is the built-in list-item CSS counter, which does not depend on anything in the HTML spec per se. (I've submitted a pull request to clarify that.)

CSS Containment defines how the contain property affects CSS counters in general, which means it's also fully defined for HTML lists.

The one HTML thing I'm aware of that has an "action-at-a-distance" effect is <ol reversed>. It makes the default value of counter-increment: list-item for descendant list items -1 (instead of 1). It seems reasonable to also terminate that effect at a contain:style boundary, but that's currently not in any spec AFAIK.

(FYI, contain:style is not yet implemented in Gecko.)

@emilio
Copy link
Contributor

emilio commented Aug 3, 2019

(FYI, contain:style is not yet implemented in Gecko.)

(And in general I'm skeptic about implementing it allowing any sound optimization, see w3c/csswg-drafts#3280, so should we implement it it'd be purely for the rendering effect / isolation, I think, unlike the other kinds of containment which do add optimization opportunities)

annevk pushed a commit that referenced this issue Apr 28, 2021
Map `<ol start reversed>` and `<li value>` to `counter-reset` and `counter-set` properties as presentational hints. For reversed lists, use the CSS `reversed()` function added in w3c/csswg-drafts#6096.

Tests: web-platform-tests/wpt#28040 & web-platform-tests/wpt#28453.

Helps with #4808.

Co-authored-by: Simon Pieters <zcorpan@gmail.com>
@zcorpan
Copy link
Member

zcorpan commented Sep 9, 2021

With this change #4816 -- this issue should be fully dealt with by CSS Containment and CSS Counters. Including reversed lists. Correct, @vmpstr @MatsPalmgren ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

8 participants