- From: Tab Atkins Jr. via GitHub <sysbot+gh@w3.org>
- Date: Fri, 27 Dec 2024 19:46:40 +0000
- To: public-css-archive@w3.org
I've been sitting on this for a bit, because of vacation/Christmas/etc., but I think it's ready to post now. tldr: The "reuse when possible" principle is *completely valid* and should be (and is) applied as much as possible, but it shouldn't be *overused*, when merging/reuse is messy or complicated. Grid/Masonry is an example of the latter, with several properties that work well in Grid but are *genuinely bad* when used for Masonry, and several other properties that are at least *clumsy* in Masonry (tho technically usable). This appears to be a *general* issue, since Flexbox can't meaningfully merge into these *either*, nor can future examples that have been brought up. In short, while new layout abilities sometimes merge nicely into an existing layout mode (like Subgrid into Grid), it is *usually* the case that the abilities of one layout mode, and the specialized syntaxes we design to allow authors control over those abilities, are usually a bad fit for even a moderately different layout mode. Layout modes should, in general, get their own set of properties with their own specialized syntaxes, rather than trying to reuse each other's properties. I'll go into detail on exactly *how* the current Grid-masonry design is bad, and at the end, offer a compromise if the WG still disagrees that Masonry should be its own independent layout mode. # Specific examples Apologies for the length of this section. The badness really is in the details; the way that the Grid properties work when applied to Masonry is an *extraordinarily* bad design, and it's worth working thru those details rather than handwaving it as "it's bad, just trust me". If you'd prefer to trust me, feel free to skim. * [`grid-template-areas`](https://drafts.csswg.org/css-grid-2/#grid-template-areas-property): * Syntax allows defining an NxM grid; when used in Grid-masonry, we are forced to ignore all but the first row/column. "Technically valid but ignored" values are an anti-pattern we try to avoid when possible; we often purposely design grammars to avoid this even being possible. * A single-column Grid-masonry and a single-row Grid-masonry use different syntaxes; `"one two three"` vs `"one" "two" "three"`. Requiring authors to remember which syntax to use depending on their masonry direction is bad. In Grid, the syntax works well, and authors have good reason to break the separate row-strings onto separate lines, to align the columns across the strings and make 2D ASCII art showing the grid structure. There is no such benefit in Grid-masonry (there's only one cell per row), so instead you just have the value put on one line regardless, and the weird syntax split based on direction. That is, authors are not going to write: ```css grid-template-areas: "foo bar baz"; // column masonry grid-template-areas: "foo" "bar" "baz"; // row masonry ``` as that's just a frankly silly use of linebreaks/whitespace. They'll just write: ```css grid-template-areas: "foo bar baz"; // column masonry grid-template-areas: "foo" "bar" "baz"; // row masonry ``` * [`grid-auto-flow`](https://drafts.csswg.org/css-grid-2/#grid-auto-flow-property): * Grid 2 syntax is `[ row | column ] || dense`. In Grid, this dictates how auto-placed items search for empty spaces - by scanning each row before moving to the next, or scanning each column. Both `row` and `column` *do absolutely nothing* in Grid-masonry; it's already intrinsically auto-placing, and only has one possible axis to do it in. (While `dense` originally did nothing in Masonry either, we've since come up with *a* behavior for it, tho it's less powerful than the Grid behavior.) * [Grid 3 adds](https://drafts.csswg.org/css-grid-3/#grid-auto-flow) `row-reverse`, `column-reverse`, and `wrap-reverse`. For Grids, the first two are the same as row/column, but flip the direction you fill the rows/columns in; the third flips to make it fill in the *last* row/column first and wrap back toward the start when you need more rows/columns. In Grid-masonry, we first have the issue that `row-reverse` and `column-reverse` again *do the exact same thing*, because the "auto flow" direction is already fixed. They do finally have *an* effect, tho, which is *vaguely similar* but notably distinct from their effect in Grid: rather than changing the direction you search for holes, it changes which direction you favor when breaking ties for "shortest track". Note that while `row` vs `row-reverse` in Grid will have a pretty large effect on the layout, in Grid-masonry it might *only* have an effect on the first items to be placed (since all tracks are tied initially); afterwards, if there aren't any ties it does nothing. Grid-masonry's `wrap-reverse` is more similar to Grid's in effect and impact, as it causes the tracks to fill from their end side. The name is a little misleading, however; there's no real notion of "wrapping" in Masonry (while it makes perfect sense in Grid). The `display:masonry` properties are much clearer; they borrow their names and syntax from Flexbox, which has similar concepts: just like Flexbox's `flex-direction: row | column | row-reverse | column-reverse`, we have `masonry-direction: row | column | row-reverse | column-reverse`, *with very similar behavior*. Then the one *novel* property in this group, `masonry-fill`, determines how it breaks ties, a situation/behavior that neither Flexbox nor Grid really has. * the [`grid-template`](https://drafts.csswg.org/css-grid-2/#explicit-grid-shorthand) shorthand: * In Grid, this combines `grid-template-rows`/`grid-template-columns` and `grid-template-areas`. Grid-masonry, thus, inherits all the problems of those properties: only *one* of `-row`/`-columns` is used (the other needs to specify `masonry`), and the `-areas` component has its two distinct syntaxes. The combination actually compounds the badness, tho. First, the `-rows`/`-columns` components are in a *specific* order in Grid: the rows, then the columns. This order is, again, completely sensical in Grid, as it corresponds to how you write the 2d grid in ASCII, but in Grid-masonry it's an unnecessary requirement. So a row-masonry needs to be written `repeat(3, 1fr) / masonry` while a column masonry is `masonry / repeat(3, 1fr)` - the masonry's *direction* is completely implicit, never explicitly mentioned. Compare with `display:masonry`, which uses `masonry: row repeat(3, 1fr)` or `masonry: column repeat(3, 1fr)` (or in reverse order) - direction is clearly and explicitly provided via a keyword, and order of the values doesn't matter. Second, the "one string or multiple strings" problem with `grid-template-areas` infects this property too, and compounds. A row masonry is written `"foo" 1fr "bar" 2fr "baz" 3fr / masonry` (with this precisely fixed order of values), while a column masonry is written `"foo bar baz" masonry / 1fr 2fr 3fr`. This *massive* divergence in how you are required to write the property, depending solely on whether it's a row or column masonry, is *completely unacceptable* from a design standpoint. Nobody would *ever* design a property like this on purpose, it's *terrible*. Compare with `display:masonry`, which is `masonry: row "foo bar baz" 1fr 2fr 3fr` (with the three bits in *any* order), or `masonry: column "foo bar baz" 1fr 2fr 3fr`. Free re-ordering to whatever the author prefers, and the only difference between the two directions is the keyword *declaring* that direction. * the [`grid`](https://drafts.csswg.org/css-grid-2/#grid-shorthand) shorthand * In Grid, this combines the three properties of `grid-template` with the `grid-auto-*` properties - `grid-auto-rows`, `grid-auto-columns`, and `grid-auto-flow`. One would think that this inherits *even more problems* - all the `grid-template` issues + the `grid-auto-flow` problems, but actually *the property doesn't work at all*. That is, while you can of course still write a `grid-template` value in `grid`, it also offers this grammar for doing all both `grid-template-*` and `grid-auto-*`: ``` <<'grid-template-rows'>> / [ auto-flow && dense? ] <<'grid-auto-columns'>>? ``` (This specifies explicit rows and auto columns; there's also the reverse for auto rows and explicit columns.) This grammar *is fundamentally incompatible with Masonry*. It only lets you specify *one of* `grid-template-rows`/`-columns`, but Grid-masonry needs *both* so it can declare one axis with explicit tracks and the other as `masonry`. The most you could do is something like `masonry / auto-flow 1fr`: no explicit tracks listed at all, a meaningless `auto-flow` value (because Masonry is always auto-flowing), and a `grid-auto-columns` value that does get used, but only in a *weird* way: the grid will create enough implicit columns (with that size) to fit the largest spanning item or the furthest placed item. (Which means, in the common case, it'll just spawn a single implicit track.) With the Grid 3 values for `grid-auto-flow` it'll be possible to specify a *slightly* meaningful value instead of `auto-flow`, but it'll still mean you only have a single track in the common case. And, again, if you instead wanted to have a row masonry, you'd have to flip the order of things. (The grammar *should* be amended to allow specifying `masonry` on the auto-flow side, so the explicit track list is actually useful. But this is another indicator that the mental model underlying the Grid syntax is simply *not designed* for how Masonry wants to be written.) Also, the `grid` syntax only lets you *choose* between combining `-rows`/`-columns` with `-areas`, *or* with `-auto-*`. You can't actually shorthand all of the properties at the same time. (This isn't super intentional on Grid's part, fwiw. It would be completely reasonable to want to name your columns even if all your rows are auto-generated. The grammar just gets too complex to make that reasonable, so we left it out.) Compare all of this with `display: masonry`, which *trivially* lets you combine all the functionality in a single, fully-reorderable property with no complexity to deal with: `masonry: column-reverse 1fr 2fr 3fr reverse;`. # Conclusion Even with a few more fixes to the grammar to make it work properly, Grid-masonry is still *fundamentally* awkward to specify and use. We would *never* design such a syntax from scratch; we'd be laughed out of the WG telcon. The cost of requiring authors to use such a clumsy syntax, and the precedent it establishes for how clumsy of a syntax we're willing to tolerate, is not remotely worth the benefits of authors not having to learn a new set of property names. As well, the benefit of reusing property names does not *meaningfully* carry over to actually reusing the property *values* in many cases; the actual *meaning* of those values is in several cases significantly distinct anyway, so authors are effectively still learning a new set of properties when using Grid-masonry, just spelled the same as an existing set. This is actually a *detriment* to learning and understanding, versus having to learn a new-but-similar set of *well-designed* properties and values that match the underlying model better and much less clumsily. Even if we designed Flexbox from scratch today, it would similarly not be able to reuse the Grid properties without being awkward and clumsy. I assert (without showing my work) that this would be true *even if we designed the entire thing from scratch*, all three of Flexbox, Grid, and Masonry together without any backwards compat to worry about. And the discussed possible future layout models, like "pillar", are similarly *quite distinct* and would have their own awkward/clumsy integration into such a combined layout mode. In conclusion, Masonry should be its own `display` value, with its own set of properties that are well-designed for it. I *suspect* I (/Chrome) am willing to formally object to the current Grid-masonry design; it really is Just That Bad. # Compromise (that I don't like) # If the WG still disagrees with all of the above and thinks it *is* worth trying to reuse Grid properties in Masonry as much as possible, I offer this in-between proposal that avoids as much of the badness as I can. I must stress that *I do not like this proposal*. A lot of the clumsiness is inherent to the fact that Grid is designed to be fully 2d, while Masonry is, similar to Flexbox, 1d-and-a-bit in its concepts. But it is *substantially* better than the current Grid-masonry proposal. * `grid-template-rows`/`-columns`/`-areas` reused as-is. Do not add the `masonry` keyword. (Ignore whichever one is in the masonry axis.) * Add the "intrinsic sizes in an auto-repeat function" grammar that Masonry allows. Either figure out *some* way to make that usable in Grid, or just give it fallback behavior (take one repetition), like when you use auto-repeats values in an instrincally-sized Grid today. * `grid-auto-rows`/`-columns` reused as-is. (Again, ignoring whichever one is in the masonry axis.) * `grid-row`/etc reused as-is. (Again, ignoring whichever one is in the masonry axis.) * Add a `grid-masonry: none | [ row | column | row-reverse | column-reverse] || [ tie-start | tie-end ]` property, which makes the Grid into a Masonry, and declares the masonry axis and direction, and tie-breaking direction. (Identical to [`masonry-direction` + `masonry-fill` in the current draft](https://drafts.csswg.org/css-grid-3/#masonry-direction), except I'm changing the `masonry-fill` keywords. I want to make that change anyway.) * `grid-auto-flow` is ignored entirely; `grid-masonry` completely subsumes it. * Add a `masonry` shorthand, [matching the current draft](https://drafts.csswg.org/css-grid-3/#masonry-shorthand), which sets `grid-masonry` and all the existing `grid-*` properties appropriately. This design lets us ignore the terrible syntax of `grid-template-areas`, `grid-template`, and `grid` (when used for Masonry) entirely; instead, authors will just use the `masonry` shorthand instead, which is simple and easy. `grid-masonry` makes the masonry axis clearer by reusing the already-understandable Flexbox syntax, and lets us avoid the clumsy reinterpretation of `grid-auto-flow` keywords entirely. If you really want to, tho, you *can* still use `grid-template` or `grid` as usual, and then set `grid-masonry` to activate the masonry axis. It does still carry some dead weight; authors have to be aware that several Grid properties are ignored when Masonry is active. If we *really* wanted to minimize the number of new properties, we could instead merge `grid-masonry` *into* `grid-auto-flow`, as a separate grammar branch with a special keyword, like `grid-auto-flow: masonry row`, and have *that* be the trigger for Masonry behavior. Because this is a completely separate grammar branch, tho, it isn't meaningfully different from just having a separate property. Also, it really does do a *substantially* different thing than what `grid-auto-flow` does today, so merging it in isn't very helpful from a meaningfulness standpoint. I'd recommend against this. Note that accepting this compromise **does not** imply that we'll continue to merge *more* layout modes into Grid just because they're 2d-ish. We'd still evaluate them as they come. -- GitHub Notification of comment by tabatkins Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/11243#issuecomment-2563983858 using your GitHub account -- Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config
Received on Friday, 27 December 2024 19:46:41 UTC