w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.5k stars 660 forks source link

Proposal: CSS Gap Decorations Level 1 #10393

Open kbabbitt opened 5 months ago

kbabbitt commented 5 months ago

Explainer

This is a fresh take on a topic that has been brought up previously in discussions such as #2748 and #6748. Authors have long been asking for ways to paint gap decorations in grids and other containers.

Why a new proposal?

The core of this proposal is similar to ones we've seen previously: We introduce row-rule-width, row-rule-style, and row-rule-color as complements to column-rule-width, column-rule-style, and column-rule-color and apply both sets of properties to grid, flex, and masonry containers. But one reason for the fresh take is to address use cases such as this one that call for varying gap decorations over a single container, or a subregion of a container. To do that, I've drawn inspiration from grid area syntax and the parallel lists + list cycling behavior in CSS Transitions and Animations to allow things like:

.alternate-heavy-light {
  display: grid;
  grid-template: repeat(auto-fill, 30px) / repeat(3, 100px);
  grid-gap: 10px;
  row-rule: 2px solid black / 1px solid lightgray;
}

Example grid with alternating heavy and light row separator

.grid-multiple-decoration-areas {
  display: grid;
  grid-template-rows: [top] 30px [main-top] repeat(6, 30px) [bottom];
  grid-template-columns: [left] 100px [main-left] repeat(3, 100px) [right];
  grid-gap: 10px;
  grid-row-rule-area: left / top / main-left / bottom,
                      main-left / main-top / right / bottom;
  row-rule: 1px solid lightblue,
            1px solid black;
  grid-column-rule-area: main-left / top / main-left / bottom;
  column-rule: 1px solid lightblue;
}

Example grid with varying gap decorations

Applying to the specific use case I cited above:

.container {
  gap-rule-style: solid:
  gap-rule-color: lightgray;
  column-rule-width: 1px repeat(auto, 2px) 1px;
  row-rule-width: 0px repeat(auto, 2px 1px);
  grid-gap-rule-area: 2 / 2 / -1 / -1;
}

Example grid with varying gap decorations

Responsive layout

Another use case I hadn't seen addressed before is responsive flex layout. This proposal also introduces a set of logical properties to help with these cases: main-rule-* and cross-rule-* which map to row-rule-* or column-rule-* depending on flex direction.

.container {
  display: flex;
  cross-rule: 3px solid gray;
}

Example flexbox in row direction with gap decorations Example flexbox in column direction with gap decorations

What about gap images?

Gap decoration images is definitely something I'd like to explore. However, as pointed out in this comment, gap decoration images have much more complex definition needs than border images, and there are several use cases cited in #2748 that can be addressed without the use of images. So I've left them to a future level for now.


There is also a skeleton spec as a companion to the explainer. So far it's mostly a place where I've parked notes about grammar details - there are a lot of details still to be fleshed out.

I would welcome any comments/suggestions/additional use cases, either in this thread or as issues in the MSEdgeExplainers repo: New issue

cc @alisonmaher @KurtCattiSchmidt @ethanjv @oSamDavis @bfgeek @tabatkins

SebastianZ commented 5 months ago

Thanks for bringing this topic back again and providing a different approach to it!

Just a quick note, @rachelandrew, @tantek and I discussed a way forward for gap decorations as well in a breakout session at last year's F2F meeting in Cupertino. Unfortunately, I never actually took the time to write down our conclusions for a level 1 of this feature. So I take this opportunity to do that now and also comment on your proposal. What we concluded back then was (Rachel, Tantek, please correct me if I'm misremembering!):


Regarding your proposal, here a few quick thoughts after skimming through the explainer and the draft spec:

Sebastian

benface commented 4 months ago

Thank you for writing this proposal @kbabbitt and thank you for the great response @SebastianZ. I previously wrote a much more simplistic / undetailed proposal in #9482.

This is, in my opinion, one of the most glaringly missing and needed features of CSS in 2024, in the sense that there is no good workaround (even gap was easier to "polyfill" with negative margin on the container and positive margin on children). I really hope this gets picked up and prioritized, and will gladly help in any way I can.

kbabbitt commented 4 months ago

Thank you @SebastianZ and @benface for the comments and support!

Re: Gap decoration areas and varying decorations - I agree they add a lot of complexity, and I could see moving them out of level 1 because we can still solve a lot of use cases without them. Beyond that, it sounds like the explainer is largely in alignment with what was discussed at the Cupertino F2F.

Tagging to discuss briefly at next week's F2F - I'd love to get more thoughts on the direction and opinions on what should be in and out of scope for level 1.

rachelandrew commented 4 months ago

Really happy to see this, and very much in favor. It does seem well aligned to what we have discussed previously. I do think reducing this to the simplest form for a L1 is a good idea, it will solve most of the use cases I see (most of the asks are pretty simple) then we can see what rises to the top in terms of developer needs.

LeaVerou commented 4 months ago

Agreed on pursuing this, and on scoping it more narrowly for L1. Wrt alternating borders, I would much rather see the row/col pseudos move forward than bake the pattern into the syntax for these properties.

kbabbitt commented 4 months ago

Wrt alternating borders, I would much rather see the row/col pseudos move forward than bake the pattern into the syntax for these properties.

Thanks for this suggestion, capturing links to a couple of discussions I found on this for reference, @LeaVerou please let me know if there's others I missed: #1943, #8352

tabatkins commented 4 months ago

I think this proposal looks really good! So the proposal, skeleton'd, is:

The third/fourth bullet points are required to match the usability of table borders, presumably, which makes sense to me. In many cases where you want to style something like a table you should be using a table, but that's not always appropriate; calendar, for example, probably can't use tables since there can be overlapping entries.

We will need rules for determining who "wins" for rules bordering two areas, or if you define overlapping areas. I suggest that later areas win. (We lack the hierarchy of table elements that tables have, which let us make reasonable assumptions about which element should win when there's a conflict.)


The logical equivalents to the -rule- properties are important. (Just to note, https://github.com/w3c/csswg-drafts/issues/6748#issuecomment-1036109018 before.)

They're already logical; "row" and "column" are logical terms, see Flexbox and Grid. (And, for that matter, Multicol, where column rules are always block-axis aligned.)

The mix of slashes and commas in the syntaxes needs to be discussed, especially in regard of a possible gap-rule shorthand (as mentioned in the explainer).

Yes, we need to be careful with this. In general we want to minimize the use of slashes; they tend to be confusing, especially when mixed with commas. (We're consistent with which separator wins wrt grouping, and this proposal follows that practice, but still.) Maybe we can do a cycling function or something to make it more obvious.

benface commented 4 months ago

(personally I'd prefer to just use row-rule and column-rule)

Me too. There is no main-gap or cross-gap property, so I don't think we need main-rule or cross-rule either.

In many cases where you want to style something like a table you should be using a table, but that's not always appropriate; calendar, for example, probably can't use tables since there can be overlapping entries.

Off topic, but sounds like what we'd really need for a calendar is #4416, since rendering multiple elements in the same grid cell would currently make them overlap instead of placing them one after the other.

SebastianZ commented 4 months ago

In many cases where you want to style something like a table you should be using a table, but that's not always appropriate; calendar, for example, probably can't use tables since there can be overlapping entries.

Off topic, but sounds like what we'd really need for a calendar is #4416, since rendering multiple elements in the same grid cell would currently make them overlap instead of placing them one after the other.

That use case is also discussed in #1183, but yeah, off-topic.

Sebastian

GrimLink commented 4 months ago

+1 on this for me

css-meeting-bot commented 4 months ago

The CSS Working Group just discussed Proposal: CSS Gap Decorations Level 1, and agreed to the following:

The full IRC log of that discussion <dbaron> kbabbitt: gap decorations in layouts like grid is something authors have been asking for.
<dbaron> kbabbitt: discussed in some other issues... this proposal takes some inspiration from previous discussions
<dbaron> kbabbitt: this adds row-rule-* to complement column-rule-* and makes them apply to grid and masonry
<dbaron> kbabbitt: this adds a new idea of varying gap decorations
<TabAtkins> q+
<dbaron> kbabbitt: ??
<dbaron> kbabbitt: this example is a calendar grid with varying light and dark decorations.
<TabAtkins> alternating light and *very light* rules
<dbaron> kbabbitt: this is a calendar grid set up with alternating heavier and lighter rules for hours of the day and days of the week
<dbaron> kbabbitt: this specifies area where they apply and patterns for drawing
<dbaron> kbabbitt: plenty of use cases work without this, so could be pushed to a later version
<una> q+
<dbaron> kbabbitt: basic part here is row-rule-{width,style,color} and applying row-rule-* and column-rule-* to grid
<rachelandrew> q+
<dbaron> fantasai: how does it address the last part, where there's no rule where there's no cells?
<dbaron> kbabbitt: the basic model that I have for where the decorations from the stem of the T to the next intersection
<dbaron> rachelandrew: same as multicol, don't draw the rules where there's no content
<florian> q+
<dbaron> TabAtkins: strongly approve overall. Having control over rules is important for grid, useful for flexbox, useful for masonry. I agree with deferring rule images until some later time. They are mildly complicate in multicol, incredibly complicated in a 2D grid. Can't appreciate thus until you try to design it. Put that far in the future!
<rachelandrew> +1 on deferring rule images
<emilio> q+ to ask how does this interact with gap? Presumably the effective gap would be max(rule-width, gap)?
<dbaron> TabAtkins: I like ideas here about areas. Captures functionality you can get tables, we don't have the hierarchy of elements in grid, good to be able to reproduce that.
<dbaron> TabAtkins: I don't like use of slashes mixed with commas more than we usually like. We can do that later, such as with a wrapping function.
<dbaron> TabAtkins: overall looks really good, don't have a strong opinion on what should be deferred versus MVP for level 1. Fine with simplest possible, but could see how much to get into a level 1.
<dbaron> TabAtkins: some other things mentioned in thread was about controlling length of rules so they don't fill entire row/column.
<dbaron> TabAtkins: model here does make that a little more complicated... might need some thought.
<dbaron> TabAtkins: but I'm happy to deal that in the future.
<dbaron> TabAtkins: maybe that means we should simplify to bare minimum in L1 to keep our options open for later.
<dbaron> kbabbitt: One comment about use of slashes: in this example, using slashes in the context of a shorthand. Not opposed to functional notation.
<dbaron> TabAtkins: you have to save commas for matching up with row rule areas.
<dbaron> TabAtkins: we try to avoid 2 levels of demarcation because it's hard to read. We can figure that out in the issues.
<iank_> q+
<dbaron> una: generally think it's a good idea. Some questions:
<dbaron> una: regarding which line item is on tap -- is it the horizontal or vertical? is it consistent? is there author control?
<dbaron> una: you mentioned no images -- agree it adds a lot of complexity. What about gradients? Could be effective.
<dbaron> una: my first thought was that given lack of intersection control, maybe want blend modes or filters to control how they intersect. Or can authors use blend modes etc. for gap decorators? Or is that a v2 thing?
<dbaron> kbabbitt: re paint order, there's a property for that, just to choose row or column on top. Could explore more flexibility later.
<dbaron> kbabbitt: for gradients and mix blend modes I'd think of that as something to explore later. Probably some complexity.
<dbaron> kbabbitt: for blend modes in particular, re how decorations interct when they meet at an intersection.
<dbaron> iank_: then also, gradients are images (at the spec level, no diference)
<lea> q+
<dbaron> kbabbitt: I'm also still collecting use cases, please post examples to the issue.
<TabAtkins> Gradients *can* be 1-d (see border-stripe) but that still requires the rendering model to have a well-defined and understandable notion of "length"
<dbaron> rachelandrew: generally a fan of this. Something we know people asking for.
<lea> q+ to say I'd rather keep L1 as small as possible (possibly just `row-rule` that is identical to the existing `column-rule` and leave the bells and whistles for later, once we have experience of what authors do
<dbaron> rachelandrew: I'm interested in the area syntax, i ithink it solves quite a few use cases, people asking for multicol pseudo-elements for grid. I think this would help with some of those.
<dbaron> rachelandrew: you perhaps saw my comments on ?'s original proposal... were hoping to do multicol overflow in th eblock direction which gives us ? rows. (??)
<dbaron> florian: I think it's a well thought out propsoal. A lot of complexity, but a lot of details. We can fiddle with the details a lot. I like it. Should turn it into a spec. Not sure if we want to spec more than the minimal set and then trip, or just spec minimal set.
<dbaron> florian: you mention orthogonal direction doesn't apply to multicol around spanners and things... we might later extend multicol to multiple rows of multiple columns
<dbaron> florian: But I like it.
<dbaron> kbabbitt: for spanners in particular there are probably multiple solutions that work, such as putting borders on the spanning element
<dbaron> kbabbitt: i want to reserve ???
<emeyer> q+
<florian> s/around spanners and things/around spanners and things, and I agree with that choice.
<Zakim> emilio, you wanted to ask how does this interact with gap? Presumably the effective gap would be max(rule-width, gap)?
<dbaron> kbabbitt: One reason I explored designing subareas was to make sure syntax could be extended, but I think I am leaning towards deferring it.
<florian> s/we might later extend multicol to multiple rows of multiple columns/we might later extend multicol to multiple rows of multiple columns, and that's where they should apply
<dbaron> emilio: does do the row length and the gap length interact? Like in columns?
<dbaron> emilio: if you have a rule length that is bigger than the gap between the items, does that expand the gap or do something else?
<rachelandrew> made the same comment re multicol, rows, and spanners in the proposal from Mats https://github.com/w3c/csswg-drafts/issues/6748#issuecomment-947375179
<dbaron> kbabbitt: gap decorations do not affect layout at all -- they're a paint-only effect. They'd paint behind the items like gap decorations in multicol.
<dbaron> kbabbitt: This is because for some of the subarea definitions, want to be able ot define an area to cover rows and columns generated implicitly as part of layout. So has to come after we figure out the size of the grid.
<dbaron> iank_: +1 to starting small. We'd have had to integrate with the column-rule* properties anyway.
<dbaron> iank_: you mentioned images explicitly left out because they're super complicated. Anything else left out initially?
<dbaron> kbabbitt: The other thing was application to tables.
<dbaron> kbabbitt: The underlying spec is still marked as not ready for implementation, probably still some compat differences.
<TabAtkins> q+
<dbaron> kbabbitt: and existing table borders solve many scenarious
<dbaron> iank_: existing table model is different, and gap property doesn't apply there. So makes sense.
<Zakim> lea, you wanted to say I'd rather keep L1 as small as possible (possibly just `row-rule` that is identical to the existing `column-rule` and leave the bells and whistles for later,
<Zakim> ... once we have experience of what authors do
<TabAtkins> q+ about avoiding painting across occupied areas
<dbaron> lea: Just +1 to keeping level 1 super simpler -- get row-role-* as well and maybe a shorthand for both, and see what authors do before adding more.
<florian> q?
<dbaron> fantasai: I was on the queue to disagree with that -- I think the spanning and 2D nature of grid raises a lot of issues not present with multicol. If we don't know how we're going to solve those, we'll likely get ourselves into a bit of a mess as we try to expand to handle those cases properly. I think it's great that this deals with all of those scenarios. I think the first spec draft should continue to do that so we have an idea
<dbaron> fantasai: ...how it all fits together. If some properties marked as at risk or not ready, that's fine.
<dbaron> fantasai: I like that this is handling the gaps. I think the part about grid area stuff I'm a little less sure.
<florian> +1 to fantasai on spec broadly and then trim / at-risk / defer the more advanced parts rather than spec narrowly
<lea> +1 to discussing these issues, my point was about reducing initial implementation burden, the design work should definitely happen
<dbaron> fantasai: your initial value says "1 1 -1 -1" to cover the whole grid -- but that only covers the explicit grid. One of the challenges with grid is that there's no way to address the last line including the implicit grid.
<dbaron> fantasai: Overall looks pretty good, probably some syntax we want to tweak. Glad you through through the intersections carefully.
<Zakim> fantasai, you wanted to say we need to sketch the whole thing
<dbaron> emeyer: haven't read this in detail... but doesn't seem to be a way to select a specific track?
<dbaron> emeyer: let's say I have a week calendar, and I want a rule next to Sunday and Saturday. From what I can see I'd have to do column-rule-color: black transparent transparent ... transparent black.
<dbaron> kbabbitt: Other way to do that is this syntax with grid-row-rule-area specifying 2 areas.
<dbaron> kbabbitt: works like transitions or animations where the area definitions are the controlling properties and the other properties line up.
<dbaron> TabAtkins: quick question: if you want to avoid drawing underneath a large spanning item? Handled by setting up another grid rule area and saying no rules in that area?
<dbaron> kbabbitt: A copule ways to do it. That's one. Also a way to stop short of intersections -- column-rule-outset. Default to running the line through, but can adjust.
<dbaron> TabAtkins: That would work OK if the item was opaque, but if it wasn't you'd need to suppress painting under it.
<dbaron> TabAtkins: one other question: What's the conflict resolution mechanism when 2 areas specify the same gap edge?
<dbaron> kbabbitt: not written yet, but I think your comment said last wins, and I think I agree.
<dbaron> fantasai: meaning in a comma separated list of areas the last one wins?
<iank_> (I think in the default case you likely want to draw behind, e.g. for interactive grids, i.e. a calendar, opacity triggers upon drag, which you want to see the stuff behind for that particular interaction)
<dbaron> fantasai: might want to consider first wins to match font-family and backgrounds?
<dbaron> kbabbitt: I was thinking of painting order.
<dbaron> TabAtkins: conflicting precedence in other properties, e.g., last animation wins.
<florian> q?
<dbaron> fantasai: Any objcetions to starting an editor's draft for this feature?
<dbaron> fantasai: I suggest calling it grid gaps and rules level 2... pull in the gap property.
<dbaron> florian: gaps don't only apply to grid
<dbaron> fantasai: oops, CSS Gaps and Rules
<dbaron> TabAtkins: I don't like starting modules with a new name and numbers greater than 1.
<dbaron> fantasai: Want me to publish a gap level 1 that only has gap?
<dbaron> TabAtkins: I won't stop you from busywork to justify incrementing a number...
<dbaron> fantasai: shortname? css-gaps? ...
<dbaron> kbabbitt: I called it gap-decorations
<dbaron> una: calling it just gaps is about styling the size of the gaps
<dbaron> fantasai: I'm proposing pulling that into the same module
<TabAtkins> css-rules is no good (too many synonyms) so just css-gaps
<dbaron> florian: Calling it gaps alone might make sense when we eventually put in images.
<TabAtkins> css-gaps-and-the-things-that-go-in-them
<dbaron> dbaron: singular or plural?
<dbaron> fantasai: I think plural
<dbaron> fantasai: proposed resolution: import the draft with shortname css-gaps and title CSS Gap Decorations with kbabbitt as editor.
<dbaron> kbabbitt: I don't want people to misinterpret is a document with the shortcomings of CSS!
<dbaron> kbabbitt: what I'm hearing in terms of content is definitely row and rule lines, but areas might get pulled into later level.
<dbaron> fantasai: I think spec what you've got and then decide what to defer.
<dbaron> fantasai: And it starts as an editor's draft, ask for FPWD when you think you're ready. (Doesn't have to be perfect.)
<dbaron> fantasai: as long as it's mostly complete in terms of functionality
<dbaron> RESOLVED: import the draft with shortname css-gaps and title CSS Gap Decorations with kbabbitt as editor.
JoshTumath commented 4 months ago

It's great to see this proposal progressing. I just wanted to share our use case for this at the BBC, in case it's helpful.

The BBC News front page in the UK has a CSS grid of cards linking to articles. (International visitors can still see a similar design on the BBC Sport front page.)

Screenshot of BBC News front page with many links to different articles. Links that don't have a picture above them have a grey border above them instead.

You can see how only the cards that don't include a picture have a top border. But the top border is not a desirable design because it's not in the gap between grid cells. So this proposal would allow us to achieve the originally intended design where there would be a grey divider in the centre of the above gap.

However, we wouldn't want a continuous line across the entire row gap. I've not seen anyone discuss the proposed row-rule-outset property in the Explainer document. It would allow us to still have the desired design where the grey divider line is only above the cells and doesn't enter the column gaps.

kbabbitt commented 4 months ago

@JoshTumath thanks for that example. One question - in a world with gap decorations, would you also want to draw lines in the gaps between cards with pictures? In other words - is the lack of lines above pictures in your example part of the intended design, or is it something that happened because you're using borders as a workaround?

The reason I ask is to inform prioritization for gap decoration areas.

JoshTumath commented 4 months ago

@JoshTumath thanks for that example. One question - in a world with gap decorations, would you also want to draw lines in the gaps between cards with pictures? In other words - is the lack of lines above pictures in your example part of the intended design, or is it something that happened because you're using borders as a workaround?

I've just spoken to the UX Designer who originally created that layout pattern. The lack of lines in the column gap is an intentional part of the design.

It may also be desirable to have the gap decoration aligned to the edge of the gap so that it's touching the cell, rather than being centred in the gap.