Revamp how fieldset and legend rendering is defined#3934
Conversation
|
cc @whatwg/fieldset |
| positioned in the inline direction as follows:</p> | ||
|
|
||
| <ul> | ||
| <li><p>If the computed value of 'text-align' is 'left', then position the element on the |
There was a problem hiding this comment.
I don't understand the rationale behind letting the text-align property affect the LEGEND block itself.
In my book, that should be determined by inline margins, and possibly also the ALIGN attribute (but that one's deprecated, right?).
Text-align should only affect the position of text within lines. The position of the lines (or their container) should not be affected.
There was a problem hiding this comment.
The rationale is just that WebKit and Chromium do this, but we can change it to be more like EdgeHTML (align attribute on ancestor div affects the legend, but text-align does not affect the position).
There was a problem hiding this comment.
Yeah, I think we should be more like Edge here.
| </li> | ||
|
|
||
| <li>The child is generating a box (e.g. it is not 'display:none' or 'display:contents').</li> | ||
| <li><p>If the element has a <span>rendered legend</span>, then there border on the block-start |
There was a problem hiding this comment.
"then there border" -> "then the border"
| legend</span>'s static position with the same inline size as the <span>rendered legend</span>'s | ||
| border box.</p></li> | ||
|
|
||
| <li><p>The element is expected to have a child anonymous box, the <dfn>anonymous fieldset |
There was a problem hiding this comment.
I'm not so sure about the anonymous box idea anymore. First of all, we'd then have to distribute the CSS properties on the FIELDSET node between the wrapper box and the contents container. Things like 'width', 'border', 'background' 'float', 'z-index', 'position' and 'transform' would intuitively belong in the wrapper, while 'display' and 'overflow' would have to be set on the contents container anonymous box. Do we want to go there?
WebKit creates just one box for a FIELDSET (box type determined by 'display'). The legend is simply encapsulated into the border block-start area. Any chance we could just spec it like that? Having the legend encapsulated into the border makes a lot of sense, e.g. when it comes to determining the containing block size for out-of-flow positioned descendants (in the cases where the FIELDSET isn't statically positioned). Scrolling would also work well then, since borders are outside the scrollport.
One thing that's still not obvious with this approach, though, is paint order. Should the legend be painted in the same phase as the borders (unless the legend establishes a stacking context entry (position:relative, etc.))?
Another thing would be that the used border-width may diverge from the computed border-width. Is that a problem?
There was a problem hiding this comment.
If this approach works for implementation, it should work for the spec. 🙂
I think paint order should be such that the legend is painted after the fieldset's borders, but before other content of the fieldset. Does that match WebKit?
There was a problem hiding this comment.
Looks like it. Would be interesting to know what others think about this "legend is border" approach, though.
There was a problem hiding this comment.
OK, please take a look at the latest commit.
| <li>The child is not <span>out-of-flow</span> (e.g. not absolutely positioned or floated).</li> | ||
| <li><p>If the computed value of 'display' is one of 'block', 'table', 'table-row-group', | ||
| 'table-header-group', 'table-footer-group', 'table-row', 'table-cell', 'table-column-group', | ||
| 'table-column', 'table-caption', 'list-item', 'flow', 'flow-root', or 'run-in', then the used |
There was a problem hiding this comment.
Promoting table-column / table-column-group (something essentially invisible and display:none-ish) to a block seems a bit weird.
Might also consider setting the used value to "flow-root" (rather than "block") for the other values in this list, and get a new formatting context for free. :)
There was a problem hiding this comment.
I guess it's a bit weird but it's what browsers do, AFAICT.
There was a problem hiding this comment.
Still need to say to establish the BFC for flex/grid, so it doesn't help much.
| <ul> | ||
| <li><p>If the computed value of 'display' is one of 'inline', 'inline-block', 'inline-table', | ||
| 'ruby', 'ruby-base', 'ruby-text', 'ruby-base-container', 'ruby-text-container', then the | ||
| used value is 'inline-block'.</p></li> |
There was a problem hiding this comment.
The "used value" thing here bothers me a bit here - first and foremost as an implementor. :)
There are specs that do blockification, for instance, but that's about changing the computed value, not the used value. WebKit does actually change the used value for fieldsets, but is there any reason why we shouldn't change the computed value instead?
There was a problem hiding this comment.
I suspect this was to match existing implementations. If we can clean this up that seems nice though.
There was a problem hiding this comment.
I guess we can change this if it's easier to implement. @MatsPalmgren any opinion?
There was a problem hiding this comment.
Setting used value of display does nothing; it's not a meaningful operation, since 'display' is only ever consulted during the computed->used transformation.
You can say that if [...] holds, then 'display' behaves as "inline-block", with a note that it doesn't change the computed value for backwards-compatibility reasons.
There was a problem hiding this comment.
I think we should try to avoid having the computed value of one property (-webkit-appearance) affect the computed value of another property (display) in general, because it makes style engines more complicated and might degrade performance. So Tab's "behaves as" suggestion sounds good to me.
There was a problem hiding this comment.
Please use the phrasing "behaves as", it's becoming a term of art in CSS. (Not quite actually defined yet, but I've been thinking about how to formalize it lately.)
| <ul class="brief"> | ||
| <li>The child's used value of '-webkit-appearance' is 'fieldset-legend'.</li> | ||
| <li>The child's used value of 'float' is 'none'.</li> | ||
| <li>The child's used value of 'position' is not 'absolute' or 'fixed'.</li> |
There was a problem hiding this comment.
I really think 'sticky' should also disqualify it from becoming a rendered legend. It's just gonna look silly anyway (right?), and probably not straight-forward to implement.
There was a problem hiding this comment.
It is the rendered legend in WebKit/Chromium/Gecko/EdgeHTML. It is sticky in Chromium/Gecko/EdgeHTML but not in WebKit.
There was a problem hiding this comment.
Should legends be allowed to be multicol containers, BTW? Yes, because they are allowed to be blocks?
| <p>The used value of 'display' is determined as follows:</p> | ||
|
|
||
| <ul> | ||
| <li><p>If the computed value of 'display' is 'inline-flex', then the used value is |
There was a problem hiding this comment.
Why allow only flex, grid and block?
I think we should either allow it to only be a block container, or allow practically any display-inside values.
There was a problem hiding this comment.
Browsers today blockify legends, but @MatsPalmgren said they should support grid and flex. We could potentially support more if there are use cases and it doesn't break web compat.
https://gist.github.com/zcorpan/2f590536cf64c1aaabbc70f63dbfe2b5
| positioned in the inline direction as follows:</p> | ||
|
|
||
| <ul> | ||
| <li><p>If the computed value of 'text-align' is 'left', then position the element on the |
There was a problem hiding this comment.
Yeah, I think we should be more like Edge here.
|
Containing block size calculation (for e.g. percentage height resolution) for legends needs to be specced. I imagine it should contain the logical top border and padding areas (since legends are placed above the padding area, and are part of the border), although no browsers do that currently: http://software.hixie.ch/utilities/js/live-dom-viewer/?saved=6133 (only testing borders, no padding) |
|
Edge/Firefox/Chrome renders the percentage height testcase the same though, so I think we should spec that rather than risk regressing existing web content. Also, I don't consider a rendered legend to be "part of the border" because then its extent should be part of the border-box area and thus the fieldset background should render behind it (fully), a box-shadow should wrap it etc, and that's not what any UAs do as far as I know. Perhaps the spec would benefit from an example figure that visualizes the rendering? Something like this table figure. In Gecko we implement the |
|
Yes, all browsers render the same with my test, and I do agree that it's risky to spec it otherwise then. However, since legends are placed at the very top of the fieldset (and thus ignores top border and padding), it's kind of strange that top border and padding are included in the containing block size when resolving percentages. Anyway, my post about containing block size calculation concerns was written under the assumption that we really let the legend be part of the border area (that's what the current spec changes in this pull request say), but I believe this is something we still need to discuss. I was quite sold on the "two boxes" approach myself, but then I noticed that WebKit just puts it in the border area (and expands the border area if necessary), which seems simpler, and also easy to reason about. Blink doesn't use two boxes for tables, like the spec says (that implementation detail isn't leaked via any web-exposed API that I'm aware of, though), but what I find tricky with this approach is to determine which properties should be set on the outer box and which to set on the inner one. https://www.w3.org/TR/CSS22/tables.html#model mentions 'position', 'float', 'margin-*', 'top', 'right', 'bottom', and 'left' as properties to set on the wrapper box. That list is far from complete, though. First of all, it only covers CSS 2 (and even then it seems to forget about things like z-index). How do you determine which property goes where? EDIT: Looks like the answer to that lies in https://dxr.mozilla.org/mozilla-central/source/layout/style/res/forms.css |
|
Related bug re percentage height. https://bugs.chromium.org/p/chromium/issues/detail?id=178782 |
|
I find 20 pages in httparchive that style legend with a percentage height. https://gist.github.com/zcorpan/186444076d75b735e99439bcddf92e40 |
| in the block-flow direction, whichever is greater.</p></li> | ||
|
|
||
| <li><p>For the purposes of painting order, the <span>rendered legend</span> is expected to | ||
| be painted after the element's own borders and before the element's other content.</p></li> |
There was a problem hiding this comment.
This is fine in the general case, but I'm realizing that it won't do if the legend establishes a stacking order entry (using relative position, opacity != 1.0, transform, etc.). Then we won't be able to paint it in the background phase. One special thing with the background phase is that it's not affected by scroll offset. But if we don't paint the legend in the background phase, it will be affected by the scroll offset if the fieldset has scrollable overflow.
This is something that won't be a problem if we wrap everything but the legend inside an anonymous fieldset child (like Firefox), so that e.g. overflow:scroll will be set on that anonymous child (which obviously won't affect the legend when it's on the outside).
There was a problem hiding this comment.
Fails to paint the legend when it's relatively positioned and the fieldset is scrollable.
http://software.hixie.ch/utilities/js/live-dom-viewer/?saved=6141
|
Here's a demo with percentage heights: http://software.hixie.ch/utilities/js/live-dom-viewer/saved/6143 A is 150px in Safari/Chrome/Firefox/Edge. B is 5px in Safari/Firefox, 150px in Chrome/Edge. |
|
The B result in Firefox seems like a bug to me. I think it should be 150px. I filed bug 1485646. |
|
In that case, I think what https://drafts.csswg.org/css2/visudet.html#containing-block-details says should just apply to fieldset, without HTML having to say anything in particular. Right? |
|
I'd like us to settle on a FIELDSET/LEGEND layout model. In this example we have a scrollable fieldset. The legend is not expected to scroll along with the fieldset contents, according to Firefox, Safari, and common sense. This does suggest that it makes sense to create an anonymous child for the actual contents of the fieldset. So I'd like to explore that approach a bit further (rather than my former favorite: slap the legend in the border area, expand the border as necessary, paint it as part of border painting - like WebKit). The question then is, which properties should the anonymous child inherit, and which ones should the parent fieldset ignore (if any). And how much magic do we have to apply in addition to that? Intuitively, 'overflow' should go on the anoymous child, so that the legend isn't scrolled along with the rest. Padding probably needs to be ignored on the parent FIELDSET and be applied on the anonymous child, though, so that the scrollport includes the padding. However, percentages need to be resolved on the FIELDSET and NOT on the anonymous child. @MatsPalmgren Does Firefox really get away with no special magic here? Firefox has a nice selector for the anonymous child here: https://dxr.mozilla.org/mozilla-central/source/layout/style/res/forms.css |
|
Why doesn't the WebKit approach work when there is a scrollport? What does WebKit do in that case? |
| <li>The child is a <code>legend</code> element.</li> | ||
| <li><p>The space allocated for the element's border on the block-start side is expected to be | ||
| the element's <span>'border-block-start-width'</span> or the <span>rendered legend</span>'s | ||
| border box size in the block-flow direction, whichever is greater.</p></li> |
There was a problem hiding this comment.
Should be margin box now (since we started to allow block margins again), not border box.
| border box size in the block-flow direction, whichever is greater.</p></li> | ||
|
|
||
| <li>The child is not <span>out-of-flow</span> (e.g. not absolutely positioned or floated).</li> | ||
| <li><p>For the purpose of computing the block size, if it is not 'auto', the space allocated |
There was a problem hiding this comment.
Regarding "space allocated", maybe clarify that this too is about the margin box.
| property).</p></li> | ||
|
|
||
| <li><p>The element is expected to be constrained in the inline direction by the inline content | ||
| size of the <code>fieldset</code> as if it had used its computed inline paddings.</p></li> |
|
|
||
| <li><p>If the computed value of <span>'display'</span> is one of 'inline', 'inline-block', | ||
| 'inline-table', 'ruby', 'ruby-base', 'ruby-text', 'ruby-base-container', | ||
| 'ruby-text-container', 'inline-flex', or 'inline-grid', then behave as |
There was a problem hiding this comment.
Do we want "behave as" here, or modify the "used value"? Not sure which the CSSWG prefers nowadays.
| </li> | ||
|
|
||
| <li> | ||
| <p>If the element has a child box that matches the conditions in the list below, then the |
There was a problem hiding this comment.
This is sort of mixing elements and boxes up a bit. An element can have child elements; a box can have child boxes.
It's pretty important to be clear here, because it affects cases like this:
<fieldset>
<div style="display: contents">
<legend>aaa</legend>
bbb
</div>
</fieldset>
Amazingly enough, there is good interop here: the child determination happens on boxes, not elements, in all of Firefox, Chrome, Safari. Edge does not seem to support display:contents.
| border box size in the block-flow direction, whichever is greater.</p></li> | ||
|
|
||
| <li>The child is not <span>out-of-flow</span> (e.g. not absolutely positioned or floated).</li> | ||
| <li><p>For the purpose of computing the block size, if it is not 'auto', the space allocated |
There was a problem hiding this comment.
Computing the fieldset's block size? Should that be block-size with a link to the CSS prop?
Also, does this affect computed block-size or used block-size? I would assume just the latter, so "computing" may not be the best word here. Maybe "For the purpose of calculating used block-size, if the computed block-size is not auto then ....."
| </ul> | ||
| </li> | ||
|
|
||
| <li><p>If the element has a <span>rendered legend</span>, then the border is expected to not be |
There was a problem hiding this comment.
This sentence is ending up a bit run-on and ends up underdefining exactly what the rectangle is (because it talks about it being at the legend's position, but that's not quite right in the block direction when the border is bigger than the legend). It might be clearer broken up like this:
... to not be painted behind the rectangle defined as follows, using the writing mode of the fieldset:
- The block-start edge of the rectangle is the smaller of the block-start edge of the legend's border rect and the block-start outer edge of the fieldset's border.
- The block-end edge of the rectangle is the larger of ....
- The inline-start edge of the rertangle is ....
- The inline-end edge of the rectangle is ...
| <li>The child is a <code>legend</code> element.</li> | ||
| <li><p>The space allocated for the element's border on the block-start side is expected to be | ||
| the element's <span>'border-block-start-width'</span> or the <span>rendered legend</span>'s | ||
| border box size in the block-flow direction, whichever is greater.</p></li> |
There was a problem hiding this comment.
"fieldset's block-flow direction", maybe? The legend might have a different writing mode.
| blocks (e.g., taking into account margins and the <span>'justify-self'</span> | ||
| property).</p></li> | ||
|
|
||
| <li><p>The element is expected to be constrained in the inline direction by the inline content |
There was a problem hiding this comment.
This part is not at all clear. Could use a rephrasing, an example, or both.
There was a problem hiding this comment.
I've added an example. I'm not sure how to rephrase it, do you have a suggestion?
| blocks (e.g., taking into account margins and the <span>'justify-self'</span> | ||
| property).</p></li> | ||
|
|
||
| <li><p>The element is expected to be constrained in the inline direction by the inline content |
| box is centered over the border on the block-start side of the <code>fieldset</code> | ||
| element.</p></li> | ||
|
|
||
| <li><p>For the purpose of calculating the min-content inline size, use the greater of the |
There was a problem hiding this comment.
This should be in the fieldset section, not the rendered legend section, right?
| min-content inline size of the <span>rendered legend</span> and the min-content inline size of | ||
| the <span>anonymous fieldset content box</span>.</p></li> | ||
|
|
||
| <li><p>For the purpose of calculating the max-content inline size, use the greater of the |
There was a problem hiding this comment.
Again, that's for fieldset, not legend.
|
|
||
| <ul> | ||
| <li> | ||
| <p>The <span>'display'</span> property is expected to act as follows:</p> |
There was a problem hiding this comment.
Again, not sure whether this should just explicitly set the used value.
There was a problem hiding this comment.
There is no observable difference, is there?
There was a problem hiding this comment.
I guess it's not really clear to me what "act as" means if the used value is not being changed is all.
| painted behind the rectangle defined as follows, using the writing mode of the fieldset:</p> | ||
|
|
||
| <ol> | ||
| <li><p>The block-start edge of the rectangle is the smaller of the block-start edge of the |
There was a problem hiding this comment.
Should use the margin box of the legend, not the border box, as I've pointed out in another review issue (which seems to have disappeared now). But I guess that can be dealt with later.
…eft margin masks the border, a=testonly Automatic update from web-platform-testsHTML: test that a legend with negative left margin masks the border See whatwg/html#3934 -- wpt-commits: e241e4de1c07c47f004199fc12d031559208b922 wpt-pr: 13004
|
@zcorpan I'm happy to land this, but it'd help if you could leave a suggested commit message as a comment. It should probably also mention the location all of the tests are in. I know that browser bugs have been filed and multiple implementers are on board, so this is good to go otherwise. |
|
…stonly Automatic update from web-platform-testsHTML: Test block margins on legend See whatwg/html#3934 -- wpt-commits: e40235366bbd17f42e23e0177352c5f52c655909 wpt-pr: 13040
…', a=testonly Automatic update from web-platform-testsHTML: legend align maps to 'justify-self' See whatwg/html#3934 whatwg/html#4013 -- wpt-commits: 51ceab16ec539fd421322727f93a847f7855e317 wpt-pr: 12965
…erent display values, a=testonly Automatic update from web-platform-testsHTML: test rendering of legend with different display values See whatwg/html#3934 -- wpt-commits: a0d4be661b5c006fe64b42fe08aeb992668052ac wpt-pr: 12932
…stonly Automatic update from web-platform-testsHTML: Test block margins on legend See whatwg/html#3934 -- wpt-commits: e40235366bbd17f42e23e0177352c5f52c655909 wpt-pr: 13040
…', a=testonly Automatic update from web-platform-testsHTML: legend align maps to 'justify-self' See whatwg/html#3934 whatwg/html#4013 -- wpt-commits: 51ceab16ec539fd421322727f93a847f7855e317 wpt-pr: 12965
…erent display values, a=testonly Automatic update from web-platform-testsHTML: test rendering of legend with different display values See whatwg/html#3934 -- wpt-commits: a0d4be661b5c006fe64b42fe08aeb992668052ac wpt-pr: 12932
…e, a=testonly Automatic update from web-platform-testsHTML: test fieldset percentage block size See whatwg/html#3934 -- wpt-commits: c4998afb119a14386c5645e0d1a7c90954849dab wpt-pr: 12930
…e, a=testonly Automatic update from web-platform-testsHTML: test fieldset percentage block size See whatwg/html#3934 -- wpt-commits: c4998afb119a14386c5645e0d1a7c90954849dab wpt-pr: 12930
This implements the basics for a layout algorithm for the FIELDSET
element. It doesn't yet contain min/max calculation, legend inline
alignment, or block fragmentation support. Nor do we attempt to
paint/hit-test/etc anything correctly. Painting is likely to require
some rather persuasive code on the legacy side, especially if we want to
get paint layers right (for scrolling or e.g. relatively positioned
legends).
This new implementation is not enabled by default, but can be enabled
via the (new) LayoutNGFieldset runtime flag.
There has been some recent spec work for FIELDSET / LEGEND [1], and we
aim to accommodate for all of that. Among other things it's been decided
that a fieldset may be a flex or grid container. We are also expected to
support multicol and scrollable containers. None of these things are
supported by legacy layout.
The NG implementation is designed to handle all of that (although, with
this CL, it handles none of them). This is the reason why we need an
anonymous fieldset content wrapper child, which contains all the
fieldset contents. The rendered legend is placed on the outside of that.
This is both for convenience reasons and pure necessity.
Necessity: The legend is expected NOT to scroll together with the
contents if the fieldset is scrollable. That'd obviously look silly.
Conveniece: Taking the legend out from the fieldset contents means that
none of the other layout algorithms (block layout, multicol, flex, grid)
need to support legends in their own peculiar ways.
A fieldset element generates a fragment with up to two child fragments;
first the rendered legend (if any), then the fieldset content (if any).
Given this markup:
<fieldset>
<legend>leder</legend>
<div>hosen</div>
</fieldset
The fragment tree will look like this:
+--------------------+
| Fieldset container |
| (FIELDSET DOM node)|
+--------------------+
/ \
/ \
+--------------------------+ +-------------------+
| Fieldset content wrapper | | Rendered legend |
| (anonymous) | | (LEGEND DOM node) |
+--------------------------+ +-------------------+
| |
| |
+-----+ +-------+
| DIV | | leder |
+++++++ +-------+
|
|
+-------+
| hosen |
+-------+
The fieldset content wrapper can be a regular block container, or,
eventually, a scrollable container, a multicol container, a flex
container, or a grid container. Fieldset padding is applied on the
anonymous wrapper, NOT on the fieldset container. The reason for this is
that padding is on the inside of the scrollport (if any), so since the
wrapper establishes the scrollport (in order to exclude the legend; we
don't want it to scroll with the rest), the padding needs to go there as
well. At the same time, the legend needs to take fieldset padding into
consideration, so some extra code is required for this.
[1] whatwg/html#3934
Bug: 875235
Cq-Include-Trybots: luci.chromium.try:linux_layout_tests_layout_ng
Change-Id: If952215459016e8528b2d94b74bca1c76e2fb4d6
Reviewed-on: https://chromium-review.googlesource.com/1236221
Reviewed-by: Ian Kilpatrick <ikilpatrick@chromium.org>
Reviewed-by: Emil A Eklund <eae@chromium.org>
Reviewed-by: Christian Biesinger <cbiesinger@chromium.org>
Commit-Queue: Morten Stenshorne <mstensho@chromium.org>
Cr-Commit-Position: refs/heads/master@{#594032}
Properly define the rendering of the fieldset and legend elements.
The layout model used is most similar to Gecko, which uses an anonymous box to hold the fieldset's contents.
Fixes #3955, fixes #3930, fixes #3929, fixes #3928, fixes #3927, fixes #3915, fixes #3913, fixes #3660, fixes #3331, fixes #2756, fixes #4013.
/infrastructure.html ( diff )
/interaction.html ( diff )
/interactive-elements.html ( diff )
/references.html ( diff )
/rendering.html ( diff )