Skip to content

[css-flexbox-2] Add flex-wrap: dense #3071

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

Open
tabatkins opened this issue Aug 30, 2018 · 10 comments
Open

[css-flexbox-2] Add flex-wrap: dense #3071

tabatkins opened this issue Aug 30, 2018 · 10 comments

Comments

@tabatkins
Copy link
Member

If items don't fit on one line, try to pack them again with their min-content size instead, causing line wraps inside the item before wrapping the flex line (good for toolbars).

(copied from the wiki)

@yisibl
Copy link
Contributor

yisibl commented Apr 22, 2019

Is there any progress? We need this feature.

@tabatkins
Copy link
Member Author

(Moving some comments from #11480 to the dedicated issue issue.)

The Grid 3 proposal contains a masonry-slack property, which relaxes the constraints on what tracks are "the least filled", so a small difference in track heights doesn't force the tracks to fill in a surprising order.

#11480 proposes, as part of generalizing a few other layout properties, a new item-slack property with the same value space (and if accepted, replacing the masonry-slack property), and as part of that, proposes adding a similar "slack" behavior to Flexbox, to be controlled by this property. It also proposes an item-pack property that controls the "dense" behavior from Grid and Masonry, and proposes it be applied to Flexbox as well.

I had some feedback on the details of the slack and dense proposals for Flexbox, but it was requested that feedback be moved to a new issue. I'm copying the comments here, then I'll delete them from the original issue.


Original Comment from fantasai

Defining dense packing for Flexbox would mean that instead of breaking lines as soon as an item can’t fit, we try to cram in one more, triggering flex-shrink behavior instead of flex-grow on that line. See #3071>.

For Flexbox, slack could say at what point you switch from loose packing to cramming:

  • In normal mode, 10px slack would mean “if there’s only 10px overflow on this line when adding the next item, cram it in anyway, as squeezing in an extra 10px is no big deal”.
  • In dense mode, 10px slack would mean “if there’s only 10px empty space left on this line, don’t try to cram in the next item, that’s too much cramming”.

Reply 1 from me

[re: the proposal for "dense" Flexbox behavior]

What Grid does with dense is always start its search from the top, rather than maintaining a cursor and always proceeding forward. This way, a large item that doesn't fit on one line and gets moved to the next doesn't forever leave an empty space on the preceding line. Translating this to Flexbox, I'd assume it places each item in the first flex line that still has enough space for the item's base size, rather than always putting the item in the last line. It wouldn't change the wrapping behavior or force shrinking in new cases; items would still only shrink if they were the sole item on the line and still too large. I think this would be a useful behavior, as it helps avoid the "one line gets REALLY STRETCHY items because a large item had to wrap to the next line" that today's Flexbox gives.

[re: the proposal for "slack" Flexbox behavior]

I really like that the initial value of 0 is just today's Flexbox behavior, and higher values start enabling some slack in line-breaking. However, I don't like that in the proposal for the dense behavior, higher values make it stricter rather than looser. Given that this proposed definition of dense still only ever puts one additional item on a line, I think that *-slack is enough to define it all on its own; item-slack: infinite would give the proposed dense behavior of "always cram one extra item on the line", and smaller values make it stricter. I recognize this is a slightly different constraint -- it's checking how much the next item would overflow by, rather than checking how much space is left in the line -- but I think in practice the two constraints are reasonably interchangable. This then frees up dense to be the behavior suggested in †¹, which is closer to Grid's dense behavior.


Reply 2 from fantasai

I think you're misreading the proposal. Higher values are looser adherence to dense packing in the proposal (i.e. the larger the slack, the looser packing we allow).


Reply 3 from me

Ah, I'm not misreading, we're just thinking of the condition in opposite ways.

My objection is partly semantic (this is using slack as a negative condition, while the other usages are a positive condition), but the more important one is practical. The point of the slack is to give you some control over the decision of whether to keep the overflowing item on the line (causing shrinkage) or push it to the next line (causing flexing). What's more important for that decision: how much of the item does fit on the line, or how much of the item doesn't fit on the line?

As a practical example, say your flexbox is 1000px wide, and the line is currently 800px filled, so 200px left. The next item is one of two possible elements: either a 250px item, or a 2000px item (lots of text, guaranteed to shrink and internall wrap). What's more important for deciding whether to squeeze either item in or push it to the next line: the fact that there's 200px left on the line, or the fact that one item overflows by 50px (requiring shrinking from 1050 to 1000) and the other overflows by 1800px (requiring shrinking from 2800 to 1000)?

I think it's pretty clear that it's the latter. The 200px is irrelevant for the decision, all that matters is how much shrink the item is going to force everything on the line to do if you cram it in. You're probably okay with the 50px overflow (only 5% over!), but definitely not okay with the 1800px overflow (180% over!).

If you can roughly predict the size of the items, the two numbers are interchangeable: if one is X, the other is size - X, so you can author against either number just fine. But if you can't predict the item sizes, the amount of overflow is, I think, definitely what you want to author against.

So, I think this sort of "dense" behavior is better achieved by just using the proposed "normal flexbox" behavior and setting item-slack: infinite. You can use lower numbers to control how densely you pack.

This then frees up dense to instead act more similar to Grid and Masonry, where it causes the item to try and find an unfilled spot that it'll fit in. I think this would be useful any time someone has an unordered set of data in a multiline flexbox, so one large item wrapping to the next line doesn't leave a ton of free space on the preceding line. And then dense will still work with item-slack - the item finds a line that it can fit in or overflow by the slack amount or less, allowing a little bit of squish.


Reply 4 from me

Ah, here's the positive-condition version of your dense proposal (I rewrote the above comment three times, and of course didn't think of this until after I hit Comment): dense forces the flexbox lines to be completely filled if possible -- aka 0 free space -- by cramming in one more item when needed. item-slack loosens that; flex lines are allowed to have up to the slack amount of free space, but no more.

That said, my practical objection still stands. If you can predict item sizes, then "amount of leftover space on the line" and "amount the item overflows by" are interchangeable and we can use either; if you can't predict item sizes, then it's still more important to control how much overflow is happening than how much free space is left. And dense can then be employed to allow some light rearranging to reduce the free space.

@tabatkins
Copy link
Member Author

So the simple version of my alternate proposal:

  • item-pack: dense (or flex-pack, if we don't end up accepting the item-* proposal) triggers dense flexbox packing - every item, rather than being placed at the end of the last line, is instead placed at the end of the first line the item can fit in. This way, if a large item gets wrapped to the next line, leaving a decent amount of space behind, a small following item can scootch up and fill that space, rather than just having the remaining items get a ton of free space and flex really big.

  • item-slack: <length-percentage> (or flex-slack) adds some "slack" to the flex linebreaking algorithm: items don't break to the next line unless they'd overflow by more than the slack amount, or if the flex line has no free space left. (So, if the slack amount is 50px, and the last item overflowed by 10px, a folllowing 30px item would still wrap to the next line, as the line is now "full", even tho it could theoretically fit in the slack amount.) This way, flex items will slightly shrink if necessary, to ensure a fuller packing.

  • The two work together in the obvious way - if you're dense packing, it'll choose the first line that the item fits in or overflows by less than the slack in.

I think these better reflect the current behaviors of "dense" grid packing, and "slack" masonry calculation.

@Loirooriol
Copy link
Contributor

item-slack

My concern is that this assumes that items can shrink the desired amount. But maybe they can't, e.g.

  • They have flex-shrink: 0
  • Their min sizing properties prevent further shrinking
  • Their content size reaches 0 (they are instead filling the line using padding, border, margin, or gaps)

So I tend to think that items should still go to the next line if keeping them in the former one would cause overflow.

@dholbert
Copy link
Member

dholbert commented Jan 31, 2025

dense flexbox packing - every item, rather than being placed at the end of the last line, is instead placed at the end of the first line the item can fit in. This way, if a large item gets wrapped to the next line, leaving a decent amount of space behind, a small following item can scootch up and fill that space, rather than just having the remaining items get a ton of free space and flex really big.

I'm a bit concerned about this formulation of "dense" flexbox packing, and to-what-extent it's useful vs. weird.

A few weird behaviors that I'm imagining:

  1. The start of each line would tend to contain medium-to-large-items, vs. the end of each line would tend to have 1-2 very small items. This would make lines look "lopsided".

  2. The small items that get densely-placed at the end of each flex-line would tend to feel very "unstable". If you resize your browser window, or if a font/image loads and sizes change slightly, then those small items would suddenly snap to their in-flow position which would potentially be much later.

  3. The interaction with order might be weird. E.g. if you're using this for a toolbar and you have one button that you want to always be at the end (e.g. maybe an "Add more tools..." button) you might expect that setting a large order would force it to be last, but that would fail if this item happens to be chosen to fit on an earlier row.

  4. Related to order, we need to be sure painting order is well-defined when items get shifted around like this.

Item 1 here is specific to "dense" packing in flexbox, I think.

Items 2,3,4 are arguably things that already happen (or similarly-need-to-and-probably-already-are-defined) with "dense" packing in grid, though they might manifest slightly differently or more-noticeably in flexbox.

@tabatkins
Copy link
Member Author

My concern is that this assumes that items can shrink the desired amount. But maybe they can't, e.g.

Then they overflow. You need to make sure your specified slack actually works. You get the same issue today if you have an item that's bigger than the available space. That's almost certainly less common, of course.

So I tend to think that items should still go to the next line if keeping them in the former one would cause overflow.

The slack behavior is an explicit opt-in from the author saying it's okay to overflow a bit and shrink the items. I'm not sure why we're concerned about items overflowing a bit and shrinking, then.

@Loirooriol
Copy link
Contributor

My point is that authors may want prefer placing an item into the current line even if it doesn't completely fit, as long as items can be squeezed together to handle it. But if not, prefer to place it on the next line. Ending up with overflow is unlikely desirable.

concerned about items overflowing a bit and shrinking

My concern is that they may not be able to shrink.

You need to make sure your specified slack actually works

They may not know how much the items are able to shrink in advance.

You get the same issue today if you have an item that's bigger than the available space

Sure, but this is unavoidable. Here it can be avoided by only forcing into the current line if the items can handle it.

@tabatkins
Copy link
Member Author

Going further requires understanding what is meant by "able to shrink". There's a lot of possibilities! Is it min-width? min-content size? I don't think we want to cause size recomputation during line-breaking; currently we purposely just compute the base size and use that. I'd like to retain that simplicity if possible.

Even if you knew how much space was "available" (say it's based on min-content sizes), the 'slack' value is still important; you probably dont' want to squeeze items in completely until the minimum size doesn't fit anymore.

So the concern is, afaict, just the possibility that within your (likely relatively small) slack value, the items might be so inflexible that they can't even shrink that much. I suspect that's rarely going to be much of a problem?

@Loirooriol
Copy link
Contributor

There's a lot of possibilities!

Well I think it's straightforward to define: for items with flex-shrink: 0 you take the outer hypothetical main size, otherwise the outer minimum main size. Then you add these things together with the gaps.

The automatic minimum sizes will typically cover min-content sizes, but that may vary.

I don't know other authors, but if I can only use an extremely tiny value or otherwise I risk a high likelihood of overflow, then I won't bother using this feature, it seems useless.

@fantasai
Copy link
Collaborator

I agree with @Loirooriol, fwiw, and that was my original intention when sketching out this idea: you only cram in items if it's possible to shrink enough to fit them.

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

No branches or pull requests

5 participants