- From: Tab Atkins Jr. via GitHub <sysbot+gh@w3.org>
- Date: Wed, 15 Jan 2025 03:17:16 +0000
- To: public-css-archive@w3.org
(tldr: complaints up top, counter-proposal near the bottom) I don't think this proposal works. If we were just talking about Flexbox and Grid, this seems pretty reasonable, to the point that I'd likely have supported it if was suggested a few years ago. But in trying to wedge Masonry into the same framework of property and value names, it makes some pretty serious mistakes. In both Flexbox and Grid, the `row`/`column` keyword means "the meaningful layout grouping" (flex lines, or the meaningful direction of grid tracks) matches the keyword: `row` indicates your flex lines are laid out as rows, or that your grid items are laid out in rows, moving to the next row when one is full and possibly creating new rows if needed. `column` indicates the opposite. `row-reverse` and `column-reverse` both indicate that this layout group is filled in the reverse order, from end to start, rather than start to end as normal. Also, the `wrap`/etc keywords in both Flexbox and Grid indicate the direction you wrap to when the row/column (as chosen above) is full; `wrap` or `wrap-reverse` tells you where to place the *next* flex line, or which next grid track to search and where to create new implicit grid tracks when needed. So far, so good: these two layout modes (Flexbox, and Grid auto-layout) have very similar concepts, used in very similar ways, and the same sorts of terms apply meaningfully across both. There's some tension - `dense` doesn't have a good meaning in Flexbox (note that the cited issue #3071 is talking about a *different* behavior than what's proposed here, and *neither* behavior is really analogous to what Grid is doing †¹), and `nowrap` doesn't have a good meaning in Grid (I don't understand the behavior described here, but the "no wrap" behavior is already doable by giving the auto-placed item an explicit row or column placement), but either some meaning can be defined for them, or we can define them as error cases and give them a default behavior. Unfortunate, but with some precedent. This is probably a worthwhile cost to pay for the benefit of reusing a single set of properties and values. These arguments don't apply to Masonry. @jyasskin brings up the first issue: this proposal defines that `row` indicates a Masonry whose meaningful layout unit (masonry tracks) is *columns*. This means the standard Pinterest-style layout would be indicated with `item-flow: row collapse`, but then pay attention to `grid-template-columns`, use `grid-column` for explicit position, etc. This is confusing! Second, the `row-reverse`/`column-reverse` keywords also don't mean the same thing. In Flexbox and Grid, `row` means each new item will be placed end-ward of the previous item in the same row, while `row-reverse` means each new item will be startward. But in this proposal for Masonry `row-reverse` just indicates how to break ties when deciding which column to flow the item into. When placing the *first* few items in the Masonry this does resemble the usage of `row-reverse` in the other two, but as soon as the column edges get ragged due to differing item heights, it diverges significantly, only occasionally affecting an item's placement. Most of the time, whether the next item is placed startward or endward of the previous has nothing to do with the keyword, being instead determined by which column is currently shortest. (In the degenerate case where all items are the same size the similarity does persist the whole time, but in that case using Masonry doesn't actually do anything; you could have just used a Grid with identical effect.) Third, the entire *concept* of wrapping doesn't apply to Masonry. This proposal defines it as dictating in which direction Masonry fills its tracks, but this exact concept is controlled by `item-direction` for the other two layout modes! Masonry does not "fill" a layout unit in any meaningful way, and then create new layout units to put subsequent items in, needing a control for where to play the new ones, like Flex and Grid do with their `wrap` keywords. Fourth, the additional values still don't have great meanings in Masonry: * `nowrap` doesn't appear to have *any* possible useful behavior. (I guess it would just put a single item in each track, and generates as many tracks as there are items? That doesn't seem worthwhile to define.). * `dense` has a decently analogous behavior to Grid's definition, but it happens much more rarely, only affecting placement when you're mixing items of varying spans, and only working *well* when all your tracks are the same size. (In Grid it helps fill in the ends of rows when an item was too wide to fit in the leftover columns and got moved to the next row instead. In Masonry, the placement rules already handle that situation *by default*. Masonry only gains "holes" to fill if a spanning item covers multiple columns of varying heights. And for perf reasons, it's only allowed to *search* for holes in the same track as where it would *normally* be placed, or other tracks with the exact same size.) * Masonry adds `collapse` to indicate the grid is doing masonry rather than normal grid layout, but that doesn't have any meaningful definition for Flexbox. * `balance` has been discussed for Flexbox, but that doesn't have any meaningful definition for Grid or Masonry. Fifth, while I think applying the 'slack' concept to Flexbox is pretty interesting and worth pursuing †², afaict there's no meaningful definition for Grid. In conclusion, I think this proposal makes some unacceptable design decisions, which are forced on it due to trying to fit Masonry's layout concepts into the "direction + wrap" concept pair that Flexbox and auto-Grid use. I would oppose this. Instead, Masonry needs its own set of properties to handle the "direction + tie-breaking" concept pair that it actually uses, as I proposed in <https://github.com/w3c/csswg-drafts/issues/11243#issuecomment-2563983858>. -------- If we *did* want to define a single collective set of properties, I think it's possible! A good design just has to recognize that some concepts don't apply to some layout modes, rather than trying to cram all of them into the same set of concepts, like how different layout modes use different subsets of the `place-*` properties. We'd instead go with: * `item-direction: row | column | row-reverse | column-reverse`: works for all three layout modes, giving the direction of the primary linear layout unit, and which direction to fill that unit in. (So, the standard Pinterest layout is indeed `column`.) * `item-wrap: no-wrap | [ wrap | wrap-reverse ] || balance?`: only works in Flexbox and Grid. In Grid, `nowrap` is ignored and treated as `wrap`, and `balance` is ignored. * `item-ties: tie-start | tie-end`: only works in Masonry. (*Maybe* it could apply to a dense Grid or a "dense Flexbox" as I defined it below in †¹; `tie-start` gives today's dense behavior that always places as early as possible, while `tie-end` instead places it in the *last* position preceding the cursor that it fits in, if possible; otherwise in its normal position. That way, items tend to stay *close to* source order while still filling in gaps.) * `item-pack: normal | dense`: works in Grid and Masonry. (*Maybe* works in Flexbox, as one of the explored possibilities.) * `item-slack: <length> | infinite`: works in Masonry, and Flexbox as described in the first post of this thread. If we use Grid-masonry, we'll still need a separate way to indicate that a given Grid is using Masonry instead of Grid layout. If using `display:masonry`, that's already covered, and leaves us with only `masonry-template-tracks`, `masonry-template-areas`, and `masonry-track` as masonry-specific properties, plus the `masonry` shorthand that sets some of the `item-*` props as well. ---------- †¹: 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. †²: 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. -- GitHub Notification of comment by tabatkins Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/11480#issuecomment-2591555448 using your GitHub account -- Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config
Received on Wednesday, 15 January 2025 03:17:17 UTC