Closed Bug 1312588 Opened 7 years ago Closed 3 years ago

implement fit-content(<length-percentage>) value of width/min-width/max-width property

Categories

(Core :: Layout, enhancement, P3)

enhancement

Tracking

()

RESOLVED FIXED
91 Branch
Webcompat Priority revisit
Tracking Status
firefox52 --- wontfix
firefox79 --- wontfix
firefox91 --- fixed

People

(Reporter: dbaron, Assigned: boris)

References

(Depends on 1 open bug, Blocks 3 open bugs)

Details

(Keywords: dev-doc-complete, DevAdvocacy, Whiteboard: [DevRel:P1][layout:p2], [wptsync upstream])

Attachments

(5 files)

https://drafts.csswg.org/css-sizing-3/#valdef-width-fit-content-length-percentage now introduces a fit-content(<length-percentage>) value, that is a new value similar to fit-content, except taking an argument instead of using the available width.

This seems worth implementing, particularly given how easy it should be to implement.  (It's not a particularly high priority.)
Keywords: DevAdvocacy
Whiteboard: [DevRel:P1]
Did we implement this as part of CSS Grid?
Because this demo is working: https://codepen.io/jensimmons/pen/QpZpWR?editors=1100
I think. And I think it works in Safari / aka, Igalia's implementation.
Oh, maybe this ticket is for implementing `fit-content` outside of a Grid container context. Sorry to clutter it up with confusion. I would appreciate a double-check that it is implemented to be used with Grid, because MDN implies otherwise. https://developer.mozilla.org/en-US/docs/Web/CSS/fit-content
Assignee: nobody → boris.chiou
(In reply to David Baron :dbaron: 🏴󠁵󠁳󠁣󠁡󠁿 ⌚UTC-7 from comment #0)
> https://drafts.csswg.org/css-sizing-3/#valdef-width-fit-content-length-
> percentage now introduces a fit-content(<length-percentage>) value, that is
> a new value similar to fit-content, except taking an argument instead of
> using the available width.

So, looks like Gecko supports -moz-fit-content value. And the spec says it wants to defer this keyword into level 4, so for now, we just add a new fit-content function without prefix (for width related properties), and it exists together with -moz-fit-content. Let me try to do this.
Oh, and fit-content(<length-percentage>) should be animatable.
Priority: P4 → P3
Whiteboard: [DevRel:P1] → [DevRel:P1][layout:p2]
Status: NEW → ASSIGNED
Hi, David,

I'm a little bit confused on the calculation of intrinsic widths while adding fit-content(...). The calculation is pretty complicated, so before reading all the spec and code, I need a help to have more understanding on this:

If this |fit-content(...)| is a variant of |-moz-fit-content| but taking an argument. In [1], we skip |-moz-fit-content| for width and |-moz-available| because we could treat them as <auto> so no effect on intrinsic widths. Should we also skip |fit-content(...)|? It seems |fit-content(...)| may use the argument or max/min-content, depends on the calculation. I'm not sure that should we need to add intrinsic size offset for |fit-content(...)|, or only need to calculate it for min/max-width? Do you have any hint on this? Thanks.

[1] https://searchfox.org/mozilla-central/rev/819cd31a93fd50b7167979607371878c4d6f18e8/layout/base/nsLayoutUtils.cpp#5018-5020
Flags: needinfo?(dbaron)
It's not clear to me what the correct intrinsic sizing behavior is for fit-content().  In theory it should be defined in the css-sizing spec; I suspect it may not be.  (I also suspect that we may not match what is defined there... and I'm not sure even how confident I would be in what's defined there.)

You could still get a useful start on this without solving that issue by:
 * implementing the syntax for fit-content() behind a pref
 * implementing the non-intrinsic-sizing aspects of the behavior
and then we'd at least have it partway implemented and could then experiment with the intrinsic sizing behavior.

Generally the goal of intrinsic sizing behavior should be:
 * for min-isize, produce the smallest size that doesn't cause the element's children to overflow it (without worrying about whether the children have descendants overflowing them)
 * for pref-isize, produce the largest size that the element can occupy without further expansion just wasting space.

Figuring out what those mean here, for all of 'width', 'min-width', and 'max-width' actually might not be trivial.  We can discuss this further next week if you want, but I'm going to mark the needinfo? as answered for now.
Flags: needinfo?(dbaron)
(In reply to David Baron :dbaron: 🏴󠁵󠁳󠁣󠁡󠁿 ⌚UTC-7 from comment #7)
> In theory it should be defined in the css-sizing spec; I
> suspect it may not be.

I cannot find any words about how we handle fit-content() for intrinsic sizing in spec (the draft version). Probably it is a spec issue.

> 
> You could still get a useful start on this without solving that issue by:
>  * implementing the syntax for fit-content() behind a pref
>  * implementing the non-intrinsic-sizing aspects of the behavior
> and then we'd at least have it partway implemented and could then experiment
> with the intrinsic sizing behavior.

Thanks for this suggestion. I will add a pref for fit-content() (e.g. layout.css.sizing-fit-content-func.enabled),
and implement it without calculating intrinsic-sizing to figure out a reasonable solution for this.

> Figuring out what those mean here, for all of 'width', 'min-width', and
> 'max-width' actually might not be trivial.  We can discuss this further next
> week if you want.

All I know is 'min-width' and 'max-width' are the restrictions of 'width'. I should trace more code and read more specs/examples to have a better understanding for min/pref-isize on these properties. Will let you know if I have any further question. Really thanks for this comment.
First, a little bit of background:  Historically layout algorithms (thinking of English text for convenience and ignoring vertical) worked using widths as inputs and heights as output.  You start with the width of a page, or maybe half the page if it's divided in two columns, you lay out the text you want, and then once the text ends you have a height that's a result.  However, in some cases, you actually want widths that are derived from the content.  We commonly use two of these:  the min-content width is generally intended to be the smallest that something can be without causing overlap/overflow (e.g., for a line of text, it's the width of the longest unbreakable word), and the max-content width is generally intended to be the largest that something can be without just wasting the space (e.g., for a line of text, it's the width of the line of text all laid out on one line).

Second, in general, properties like 'width' don't change an element's own intrinsic widths; we do that so that we can have keywords like 'min-content' and 'fit-content()' that depend on the intrinsic widths and are values of 'width'.  Instead they affect the intrinsic width contribution that the element makes towards its parent's intrinsic width.


So I'll try to work through what I think the behavior ought to be, starting in the easier cases and working to the harder ones:

Intrinsic sizing behavior for this function (and for 'min-content', 'max-content', and 'fit-content' keywords) in the block dimension is not particularly relevant here, since they're all treated as the initial values.  while intrinsic sizing in the block dimension isn't particularly well defined until after https://github.com/w3c/csswg-drafts/issues/2890 happens, we don't need to worry about that here.

I think the intrinsic sizing behavior for 'min-content' and 'max-content' in the inline dimension is pretty easy; they compute to a single length, and the intrinsic sizing behavior should be the same as for <length> type values (for whichever of 'inline-size', 'min-inline-size', or 'max-inline-size' is the case -- <length> values have different effects on the three of them).

Intrinsic sizing for 'fit-content(<length>)' is also easy, since it also computes to a single length.

However, intrinsic sizing for 'fit-content(<percentage>)' is harder, since what it resolves to in the end (during layout) depends on things *outside* the box.  All the previous cases were easy because the size specified is a function only of the contents of the element and the properties on it, but now we need to consider what to do for percentages.  Some of this may depend on how we treat inline-size: <percentage>, min-inline-size: <percentage>, and max-inline-size: <percentage>.  However, there are also cases where the way we treat those was a compromise that we made for compatibility, and we might want to do something better here.  (On the flip side, doing something different might also be confusing if developers are accustomed to the current behavior.)  In the past, there have been two ways to approach handling <percentage> in intrinsic widths:
  (1) treat them just like 'auto' (i.e., pretend they're not there) -- this way has basically one at this point
  (2) reverse-compute them on their own, where a box with max-content size S and width: P (a percentage, expressed so 1.0 is 100%) has a max-content contribution to its parent of S/P.  This prevents overlap in block-like layout, but isn't sufficient when there's more than one thing per line.
  (3) reverse-compute them together, so that if a line/row/table-row has boxes with sizes S1...SN and percentages P1...PN and then other non-percentage (fixed) sized boxes F1...FN, the contribution of the entire row to its parent is the larger of sum(F1...FN)/(1 - max(sum(P1...PN), 1.0)) and any of Si/Pi.  The first term ensures that the sizes for the non-percentage parts fit within the percentage remaining after all the percentage parts, and the remaining terms ensure that each percentage part is big enough.  This is roughly what we do for tables.

So (1) is simple for percentages -- but it's not immediately clear to me how it works for 'fit-content(<percentage>)'.  That's the part that I'd need to think about more.  Doing (2) or (3) is probably even more complicated.  But there's probably a simpler way to think about what's needed for fit-content() that I haven't quite thought of yet...

Intrinsic sizing for the 'fit-content' keyword is, I think, basically the same as 'fit-content(<percentage>)', except that it has to deal with padding/border/margin differently.  But there's also a little less justification for it to behave like <percentage> does today.

Sorry, I missed this. So the idea of fit-content was to represent the sizing formula of auto floats and tables so that it can be applied to other things, like regular blocks. And fit-content(100%) and fit-content are conceptually the same thing, assuming zero MBP; it would make sense if their behavior were essentially interchangeable. So the behavior here might need clarification in the spec, but unless there's some compelling reason not to, they should behave like auto does when it is interpreted as shrinkwrapping. Which is to say, contribute as for max-content when calculating max-content intrinsic sizes, and contribute as for min-content when contributing min-content sizes.

Webcompat Priority: --- → ?
Webcompat Priority: ? → revisit
Depends on: 1695374
Attachment #9206807 - Attachment description: Bug 1312588 - Suppoert fit-content() in style. → Bug 1312588 - Support fit-content() in style.
Attachment #9206806 - Attachment description: Bug 1312588 - Add pref for fit-content(). → Bug 1312588 - Part 1: Add pref for fit-content().
Attachment #9206807 - Attachment description: Bug 1312588 - Support fit-content() in style. → Bug 1312588 - Part 2: Support fit-content() in style.
Attachment #9214820 - Attachment description: Bug 1312588 - Implement the layout part of fit-content(). → Bug 1312588 - Part 3: Implement the layout part of fit-content() without the intrinsic contribution.

We simplified to just rely on the behavior of the plain argument. That is,
width: fit-content(50%) behaves the same as width: 50%; in all circumstances,
just clamped by min/max-content.

Note: for block axis, we treat fit-content() as initial value its minimal
and maximal value are identical and equal to the initial value in block axis.

From: https://github.com/w3c/csswg-drafts/issues/3731#issuecomment-661408877

Attachment #9217922 - Attachment description: Bug 1312588 - Part 4: Use enum class for WidthProperty in nsLayoutUtils.cpp. → Bug 1312588 - Part 4: Replace eWidthProperty with the enum class, SizeProperty.
Blocks: 1708310
Blocks: 1715148
Pushed by bchiou@mozilla.com:
https://hg.mozilla.org/integration/autoland/rev/30be78bce9b7
Part 1: Add pref for fit-content(). r=emilio
https://hg.mozilla.org/integration/autoland/rev/160fb67fad9f
Part 2: Support fit-content() in style. r=emilio
https://hg.mozilla.org/integration/autoland/rev/c5389bf6f509
Part 3: Implement the layout part of fit-content() without the intrinsic contribution. r=emilio
https://hg.mozilla.org/integration/autoland/rev/f0de4933e215
Part 4: Replace eWidthProperty with the enum class, SizeProperty. r=emilio
https://hg.mozilla.org/integration/autoland/rev/41792e6152e6
Part 5: Implement the intrinsic size contribution for fit-content(). r=TYLin,emilio
Created web-platform-tests PR https://github.com/web-platform-tests/wpt/pull/29272 for changes under testing/web-platform/tests
Whiteboard: [DevRel:P1][layout:p2] → [DevRel:P1][layout:p2], [wptsync upstream]
Upstream PR merged by moz-wptsync-bot

I have added this to experimental features and to BCD.

You need to log in before you can comment on or make changes to this bug.