Html: fieldset and details have inconsistent definitions in the spec

Created on 7 Jul 2018  路  23Comments  路  Source: whatwg/html

Fieldset is defined in terms of the rendering; details is defined in terms of a shadow DOM like thing.

These are different when ::before and ::after are involved, because those are not placed in slots in shadow DOM; they get stuck into the rendering tree at the last minute after flattened tree construction is done.

As a result, per spec as currently written, ::before and ::after on a <details> will _not_ be hidden like the normal non-summary DOM kids are and the ::before will render before the summary. But ::before and ::after on a <fieldset> will behave quite differently, with the ::before going into the fieldset rendering area, not before the legend.

It's not clear to me that this is actually purposeful; I suspect the "just use a shadow tree" thing here was written before shadow DOM had its current shape. It would make a lot more sense to me in terms of resulting behavior to have the ::before/::after of a <details> treated like its other non-summary kids.

fieldset rendering shadow

Most helpful comment

For the record, here are the questions & responses to that poll:

If you use details::before or details::after on a <details>/<summary> widget, would you expect:

1.
A) pseudos hide/show with non-summary content (54% total)
B) pseudos always visible (46% total)

2.
A) ::before is before <summary> (67% total)
B) ::before is after <summary>, before rest of content (33% total)

  • 32% 1A and 2A
  • 22% 1A and 2B (aka, what Firefox currently does)
  • 35% 1B and 2A (aka, what Chrome & Safari currently do)
  • 11% 1B and 2B

From replies to the tweet, it is clear that many are rather confused about the underlying model & how it is supposed to map to CSS concepts in general. We can't go back in time & create a more logical markup model. But I suspect it would be helpful to create a model that we can clearly define using established standards (e.g., shadow DOM) & make all behavior consistent with that model.

All 23 comments

I'm pretty sure this was written this way because at least Apple and Google have voiced interest in implementing these new elements through "internal" shadow tree mechanisms. If instead we described them in terms of CSS boxes (or whatever the latest terminology is there) that would no longer be feasible.

(To me it seems somewhat acceptable to keep fieldset elements special and new elements more grounded in a set of primitives that are also directly exposed to web developers.)

The problem is that the resulting rendering is kinda broken, from my point of view. Maybe that means that the shadow DOM spec needs changes of some sort, but what we basically have here is prioritizing implementors' interests over authors' interests, from my point of view.

cc @whatwg/components

@bzbarsky do I understand correctly that this issue is really about details being defined in a way that is web-developer-unfriendly, and not an issue with fieldset?

A quick test with :before shows different rendering between WebKit/Blink, Gecko, and Edge.

http://software.hixie.ch/utilities/js/live-dom-viewer/saved/6088

Questions I see being raised here:

  1. Do fieldset and details need to follow the same kind of model? (E.g., does details need an appearance value?)
  2. Should ::before and ::after get assigned to slot elements as if they were Text/Element nodes?
  3. What do web developers want the rendering to be for fieldset/details/element with a shadow tree when they use ::before and ::after on them?

To summarize: Browser implementations of <details> mean that details::before and details::after are always available, regardless of whether the widget is expanded or not. More generally, these pseudoelements always go before/after shadow tree content, and can't be re-arranged in the template.

Which is pretty much what I would expect, since these pseudos are attached to the parent <details> box for the entire widget, not to the anonymous box that expands and contracts. If I want the before/after on a <details> to change according to open/closed state, I can always be more specific in my CSS (details[open]::after). If I want ::before/::after on the actual expandable part, I currently can wrap the content in a real element (although having a dedicated pseudo-element for accessing the anonymous box in the shadow tree would be nice).

I certainly wouldn't want to not be able to use generated content on a closed <details> (not sure if that's what you're suggesting, or only suggesting that the order should be re-arrangeable).

So, the confusion seems to be that the <summary> is always pulled up ahead of all its sibling content, but not in front of the ::before? While a <legend>, being pulled completely outside the parent box, ends up in the visible flow _before_ the ::before?

I've always seen the rendering of legend within fieldset as being more like absolute positioning than tree rearrangement. (I just wish I had the ability to do position: legend on other elements, like maybe <summary>!) I haven't examined whether screen readers agree on that.

For me, the only confusing part of the whole system is the tree-rearrangement created when a <summary> isn't the first child. I really wish details/summary had been defined with a model that could be neatly defined & overridden with CSS only, without any shadow required. (But it's a little too late to change that.) Instead, we're left trying to define a CSS pseudo-element model for accessing the functional parts of details/summary:
https://github.com/w3c/csswg-drafts/issues/2084

PS, If it's helpful to anyone else, a pen comparing details vs fieldset: https://codepen.io/AmeliaBR/pen/wEQPMN

@AmeliaBR thank you for writing that down. I think I see the difference between legend and summary in the same way. @bzbarsky or @MatsPalmgren probably have to way in at this point why they see things differently.

(Note that I filed https://github.com/w3c/csswg-drafts/issues/3126 on question 3 above, which also seems to indicate that the way things are today is okay.)

A quick test with :before shows different rendering between WebKit/Blink, Gecko, and Edge.

I'm embarrassed to realize I commented without doing cross-browser testing. Indeed, Gecko (Firefox 63 tested) does display the summary _before_ the details::before, and hides the details::before and details::after when the widget is collapsed.

MS Edge doesn't implement details/summary at all (as of the current stable version, EdgeHTML 17), so they just lay them out as unknown container elements. However, they also handle fieldset/legend different than the other browser rendering engines. Instead of pulling the <legend> out of position to align it over the <fieldset> border, the border is restricted to only start at the legend's normal position. Which means that both the ::before content and any other content in the fieldset before the legend gets rendered outside of the visual box.

_But_, since all the other major browser engines agree on fieldset/legend, maybe that one can just be called a bug on MS Edge. Although to be fair, my example is technically invalid markup, since the spec says that legend must be the first child.

<details>/<summary> is very close to <fieldset>/<legend> semantically. I see no reason why they should behave differently when it comes to pseudos. <details> is by default display:block and its not a replaced element, so it's surprising and inconsistent that the <details> pseudos should go _outside_ the block when all other elements do it per the css-pseudo spec:

... these pseudo-elements generate boxes as if they were immediate children of their _originating element_.

Can we just make <details>/<summary> behave like normal HTML elements please? (i.e. without built-in mandatory shadow DOM magic)

By that argument the resulting rendering of

<details>
a
<summary>b</summary>
c
</details>

is also weird, no? Whereas it makes perfect sense if you think of it in terms of shadow trees.

@rniwa @mstensho @hayatoito any thoughts on changing this in Chromium/WebKit?

I don't see anything weird about that example. Anyway, this issue isn't about if <summary> (and <legend>) rendering is weird in general or not. It's specifically about why this example:

<details>
details::before
<summary>b</summary>
details::after
</details>

doesn't give the same results as your example. That's inconsistent, surprising and in violation of the quoted css-pseudo spec IMO.

details is by default display:block and its not a replaced element, so it's surprising and inconsistent that the details pseudos should go outside the block when all other elements do it per the css-pseudo spec:

Hm? They don't go outside the block. details::before is definitely still inside the block generated by the details element, just like it would be on any other element.

The only thing is that pseudo-elements are inserted into the flat tree, not the pre-flattening DOM tree. So they're not scooped up by slots or anything; a ::before is always the first child of its originating element, regardless of what shadowy trickery the element might be doing with the order of its actual DOM children. This is how author-defined shadow hosts work, and should be how UA-defined ones work too. (And the browser shouldn't, if possible to avoid, be specifying behaviors that aren't achieveable by authors. The only way to rationalize details is with shadow DOM, so it should act the same as an author-defined implementation.)

(It looks like this particular interaction isn't well-specified at the moment. Happy to fix that, apologies for missing it.)

Our complaint is that (quoting from the OP): ":before and ::after on a <details> will not be hidden like the normal non-summary DOM kids ". We want it to be consistent with <fieldset>/<legend> and all other HTML elements. We're _not_ disputing that ::before/::after behaves like this in a Shadow DOM tree. We're saying that Shadow DOM might be a bad model for <details>/<summary> since it leads to inconsistent and surprising results compared to all other HTML elements.

You're completely ignoring our complaint by saying: "well, this is how it works". Sad.

fieldset/legend are freaky magic that can't be explained in terms of author-exposed APIs. Using them as a basis for anything is a bad idea.

::before on details works exactly like on any other HTML element. Nothing else has special behavior for ::before/::after, except for the replaced elements that don't allow them at all. What inconsistent behavior do you think you're seeing?

I agree that it would be useful to allow ::before/::after to be on the "contents" of a details, so they get hidden when closed. I outlined my suggested solution to this in the sister thread to this on the CSS repo: HTML should define a ::details-content pseudo-element that's defined on details, and which matches a shadow-div wrapped around the <slot> that selects all the non-summary children. This is identical to what authors can do with a details::part(content) pseudo. Then you can use ::details-content::before to get a ::before acting like how you want. This solution is consistent with implementing details as a shadow host, rather than as unreproducible magic.

You're completely ignoring our complaint by saying: "well, this is how it works". Sad.

It wasn't my intention to ignore your complaint; you were saying that the behavior was wrong/inconsistent, and I was answering that it's not. The current behavior is indeed the correct Shadow DOM behavior for ::before/::after.

Insofar as you were saying that the current behavior was undesirable, apologies, I thought the CSS repo's thread had already been linked over here and could be assumed as part of the general knowledge. That lack has been corrected by my latest (immediately preceding) comment.

Also:

We want it to be consistent with fieldset/legend

To the best of my knowledge, this is consistent with fieldset/legend too. They have magical rendering that's not author-exposed, equivalent to some sort of display: border-label; value on the first legend child, but aside from that, the rendering of fieldset/legend with ::before behaves exactly the same as for every other element - the ::before is the first child in the DOM. The "rendered legend" is just moved into the border region at layout time, due to bizarre legacy magic, but that doesn't violate the "::before comes first" principle. It's not possible for content to come before the border of the container, after all! (Without negative margins or similar.)

::before on details works exactly like on any other HTML element.

I respectfully disagree. The built-in Shadow DOM on <details> makes it not behave as other HTML elements. Perhaps you meant to say "::before on details works exactly like on any other HTML element _that has a Shadow DOM attached_.". Then it's correct, but other (non-replaced) HTML elements don't have a shadow tree attached by default. (EDIT: added "(non-replaced)" since the internals of replaced elements are opaque and could very well use Shadow DOM internally. However, <details>/<summary> aren't replaced elements.)

What inconsistent behavior do you think you're seeing?

Quoting from the css-pseudo spec (my emphasis):

... these pseudo-elements generate boxes _as if they were immediate children of their originating element_.

Hence, the pseudos should be hidden when the <details> is closed, just _as other immediate children_ are hidden.

I outlined my suggested solution to this...

That's nice, but it's still a workaround in my opinion. I'd prefer details::before to work like on other HTML elements. If UAs want to implement <details>/<summary> using Shadow DOM internally, they should be required to do so in a way that is non-observable to authors.

BTW, would this hypothetical example work in an UA that implements <details>/<summary> using Shadow DOM?

<details style="-webkit-appearance:fieldset">
  <summary style="-webkit-appearance:legend">LEGEND</summary>
  CONTENTS
</details>

("work" as in: clicking the LEGEND hides the CONTENTS, but nothing else.)

FYI, I put together a twitter poll to gather some feedback from web authors about what they expect from pseudo elements on details, focusing just on details/summary without any confusion from trying to draw parallels with fieldset/legend. Retweets for reach, etc.

https://twitter.com/AmeliasBrain/status/1046860654659887104

Early results are fairly divided, though, so I'm not sure it's going to result in a clear preference for one way or the other.

For the record, here are the questions & responses to that poll:

If you use details::before or details::after on a <details>/<summary> widget, would you expect:

1.
A) pseudos hide/show with non-summary content (54% total)
B) pseudos always visible (46% total)

2.
A) ::before is before <summary> (67% total)
B) ::before is after <summary>, before rest of content (33% total)

  • 32% 1A and 2A
  • 22% 1A and 2B (aka, what Firefox currently does)
  • 35% 1B and 2A (aka, what Chrome & Safari currently do)
  • 11% 1B and 2B

From replies to the tweet, it is clear that many are rather confused about the underlying model & how it is supposed to map to CSS concepts in general. We can't go back in time & create a more logical markup model. But I suspect it would be helpful to create a model that we can clearly define using established standards (e.g., shadow DOM) & make all behavior consistent with that model.

TPAC F2F WebPlat: Discussed this topic. We may want a flag to control this since author defined custom elements may also want this behavior. But we've decided that this is probably best discussed in the CSS WG.

FWIW, I just wrote https://bugzilla.mozilla.org/show_bug.cgi?id=1308080 which moves Gecko to use Shadow DOM. I want to discuss with @MatsPalmgren / @bzbarsky and co, in particular, since doing that would inmediately fix problems related with the magic way boxes are suppressed in <details> (https://bugzilla.mozilla.org/show_bug.cgi?id=1558049), and eventually fix issues we have with selection (https://bugzilla.mozilla.org/show_bug.cgi?id=1446675) which we need to address with Shadow DOM anyways.

Since this hasn't really become more (or less) of a problem I'm inclined to close this. Note that #4746 further formalizes the shadow tree behavior of details.

Was this page helpful?
0 / 5 - 0 ratings