Skip to content

[css-contain] Add contain-paint-margin CSS property #4934

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

Closed
chrishtr opened this issue Apr 9, 2020 · 20 comments
Closed

[css-contain] Add contain-paint-margin CSS property #4934

chrishtr opened this issue Apr 9, 2020 · 20 comments

Comments

@chrishtr
Copy link
Contributor

chrishtr commented Apr 9, 2020

As mentioned in WICG/display-locking#147, the addition of contain:paint to subtree-visibility:auto elements constrains use of ink overflow to the border box of the element. This is good for avoiding flicker and limiting raster costs, but makes it difficult or awkward for developers to support content that has legitimate ink overflow.

This can be fixed by a new contain-paint-margin CSS property that allows the contain:paint clip to be expanded by a fixed amount. Syntax:

contain-paint-margin: none | <width height>

Here width and height are positive, physical lengths. none has the same effect as 0px 0px.

Even in cases where a developer is not using subtree-visibility but wishes to apply contain:paint, this property will still be useful as a way to allow a fixed amount of ink overflow.

@frivoal
Copy link
Collaborator

frivoal commented Apr 10, 2020

I'm confused. If you allow ink overflow to escape, even by some known distance, how can you still do the optimizations enabled by the fact that contain:overflow usually allows nothing to escape? Alternatively, if you're not interested in the optimizations, why are you using contain:paint ?

@chrishtr
Copy link
Contributor Author

If you allow ink overflow to escape, even by some known distance, how can you still do the optimizations enabled by the fact that contain:overflow usually allows nothing to escape?

contain:paint on an element allows the browser to be sure that nothing in the painted subtree of the element exceeds that element's border box. This can be use to optimized away/schedule raster and related rendering work when the element is scrolled offscreen.

Expanding the border box by a fixed, and known-to-the-browser amount, still allows such optimizations to apply, but the browser just needs to account for the additional space. The same optimizations can still apply. It's just a somewhat bigger raster area.

@fantasai fantasai added the css-contain-2 Current Work label Apr 17, 2020
@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed #4934 Add contain-paint-margin CSS property, and agreed to the following:

  • RESOLVED: Add ink-skip-clip-margin property (name tbd) with non-negative values
The full IRC log of that discussion <dael> Topic: #4934 Add contain-paint-margin CSS property
<dael> github: https://github.com//issues/4934
<dael> chrishtr: Purpose is to apply a clip so the ink overflow does not exceed it. Allows UA to detect that it's offscreen so don't have to paint. THis property allows dev to have a margin to expand the clip. In case devs have certain amount of ink overflow b/c useful for layout like shadows ot flourishes
<florian> q+
<Rossen_> ack
<Rossen_> ack f
<dael> florian: Additional use case is outline and accessibility purposes. If the outline would be in screen and rest wouldn't you might want to see. Optimizations can work if you just expand boundary of rectangle a bit. If it works, why not
<dael> fantasai: Is contain:paint containing paint of elemet or contents?
<dael> florian: both
<dael> fantasai: Seems weird. Mostly cotnain is about contents. Why would you clip box shadows?
<smfr> q+
<Rossen_> ack fantasai
<dael> TabAtkins: Containing layout is one thing b/c elements own layout won't extend but children can so it's fine. But box shadows can be arbitrarily far away so if you only clip children you still have to look at element, compute styles,a nd car about paint.
<dael> fantasai: For to do it anyway so you know where it is
<dholbert> q+
<dael> florian: Element yes, but the paint. If you have containment you can say it's offscreen so stop. not check if there's a shadow and maybe compute that
<dael> AmeliaBR: Also when having element on its own layer how big is it an reserving memory for animations and whatnot
<Rossen_> ack smfr
<dael> smfr: I think I would understand better if desc how data goes into compositor. Is it knowing when overlap?
<dael> chrishtr: Reducing size of textures is an optimization. Motivation is allow dev to benefit without being strict about clipping to size of element.
<TabAtkins> This is basically exactly analogous to the filter margin concept for SVG filters
<dael> florian: fantasai point about just for children doesn't change problem. Could have 0 padding 0 border div around something with a shadow and same problem
<Rossen_> ack dholbert
<dael> dholbert: Similar to shape marging property but that rounds at corners. IS that what we're intending?
<dael> TabAtkins: SHouldn't be necessary. Just extending rectangular box
<Rossen_> ack fantasai
<Zakim> fantasai, you wanted to suggest auto initial value
<dholbert> s/shape marging/shape-margin/
<dael> fantasai: I'm still skeptical about definition. Would it make sense to have auto that calc things like default outline width or outset of border image or poss ignoring blur radius box shadow distance
<dael> florian: Possibly. We have a bunch of cases where win of optimization is real but small. If you have a bunch of checks before it defets optimization
<dael> fantasai: It's auto. You don't do every time you paint optimize. You look at 3 properties and calc outsets
<dael> AmeliaBR: Animating shadow
<dael> fantasai: If you have auto on contain paint that's aniumated
<dael> chrishtr: But has to be cached and that's a cost. It also doesn't solve content in subtree issue
<TabAtkins> Back on the last discussion, btw, Sass defines *both* `@if`/`@else` at-rules, *and* an `if(bool, val1, val2)` function, so yeah, it's got conditionals on both rule-level and value-level.
<dael> florian: If you do width of outline you wouldn't on child of element that's adjacent so only partly helping.
<dael> fantasai: Having arbitrary clip of what's doing containment is weird
<dael> AmeliaBR: Is the concern default is too strict? Could give a default of something like margin 16px. Default you assume a little overflow.
<dael> fantasai: Maybe. Havign a typical box shadow or border image get clipped when you contain paint seems weird
<dael> fantasai: Understand about clipping children. but clip box itself
<dael> dholbert: That's not what happens in FF or Chrome. We clip descendants to padding box
<Rossen_> q
<dael> TabAtkins: You should clip box shadow, not border
<fantasai> s/itself/itself is weird/
<dael> florian: I misspoke earlier. Spec says content of element incl paint of descendants must be clipped. Border is not content so what fantasai wants is what spec says. If it should is another question, but we're not spec you must clip
<dael> fantasai: sgtm
<dael> Rossen_: Would adding this property address the issues you're aware of chrishtr?
<dael> chrishtr: yes. Adding contain-paint:marging is
<dael> florian: You still want this in light of me misunderstanding?
<dael> chrishtr: Yeah, I came into it with that believe
<dael> fantasai: I'm okay with it.
<dholbert> I tested box-shadow, and indeed it's not clipped.
<dael> Rossen_: Obj to adding contain-paint amrgin property to contain paint?
<dholbert> My testcase (shadow isn't clipped in Firefox or in Chrome): data:text/html,<div style="border: 3px solid black; box-shadow: 50px 50px black; contain:paint; height: 10px; ">abc
<dael> florian: No objection. We looked at non-0 initial value so with this understanding lets have default be 0
<dael> chrishtr: Can set to non-0
<dael> smfr: Cna you set negative?
<dael> chrishtr: Good question. Not sure there's a purpose
<dael> smfr: Should it clamp?
<dael> florian: Yeah...
<dholbert> shape-margin definition, for reference: https://drafts.csswg.org/css-shapes-1/#propdef-shape-margin
<dael> Rossen_: When we discussed in context of shape outside resolution was easy that you want to have shape margin be non-camppped so you can pull content under
<dael> florian: Yes, but this is a lot weirder.
<dael> Rossen_: Should we clip to non-negative
<dael> florian: I think so
<dael> fantasai: Start and expand
<dael> cbiesinger: I think you want clamp not error
<astearns> +1 to fantasai
<dael> fantasai: Yeah
<cbiesinger> s/clamp not error/error not clamp/
<dael> florian: One number or one or two or three or four numbers?
<dael> smfr: Same as margin t/r/b/l
<dael> smfr: Do adjacent paint margins collapse?
<dael> fantasai: no
<dael> smfr: It has margin in the name and it makes me concerned
<dael> florian: adjacent margin collapsing has no meaning
<dael> smfr: SHould it have margin in name or call it outset?
<dael> fantasai: We always have scroll-snap-margin so there's precedent
<dael> smfr: okay
<dael> TabAtkins: and filter-marking in svg
<dael> Rossen_: Objections to adding?
<TabAtkins> s/marking/margin/
<dael> fantasai: Scope question
<dael> fantasai: Makes sense to have it overact with overflow clip?
<dael> fantasai: Why not operate on overflow clip so maybe more generica name
<dael> dholbert: THen same behavior as overflow scroll
<dael> fantasai: hidden, auto,a nd ascroll are different. Overflow clip is different.
<dael> dholbert: Yeah, if defined in terms of overflow-clip it would have to
<dael> chrishtr: If it's a paint thing that's fine
<dael> fantasai: And useful if people don't want full containment
<dael> florian: clip-margin?
<dael> dholbert: Don't love the name. Clips to a margin is what it sounds like
<dael> AmeliaBR: Or related to clip-path, Yeah. Name to be bikeshed?
<dael> AmeliaBR: Need to look more details about complications from overflow-clip
<dael> fantasai: I think it's worth looking at things that clip and which should hook in
<dael> Rossen_: ink-margin?
<dael> fantasai: I don't think that's how people think about it
<fantasai> overflow-margin?
<dael> florian: Doesn't add margin to ink. Visible ink overflow it doesn't do anything. It's an ink-clip-margin-ish
<dael> florian: Bikeshedding to be done
<dael> Rossen_: Obj Adding ink-skip-clip-margin property (name tbd) with non-negative values
<faceless2_> Sounds like a Dr Seuss plotline
<dael> RESOLVED: Add ink-skip-clip-margin property (name tbd) with non-negative values

@smfr
Copy link
Contributor

smfr commented May 7, 2020

Does this property ever cause things to be clipped when they weren't before, or is it just advisory? For example, does a box with contain-paint-margin: 20px; box-shadow: 0 0 50px black get a clipped shadow?

@chrishtr
Copy link
Contributor Author

chrishtr commented May 7, 2020

@smfr it would not receive a clip in that case. The clip would only apply if contain:paint or overflow:clip were present.

@frivoal
Copy link
Collaborator

frivoal commented May 8, 2020

Right. It's not a "please clip" property, it's a "if you would clip, please do so a bit further out" property.

An interesting question is whether it should apply to clip path or not. If it does, It probably should do so in a way that is similar to how shape-margin affects: take a single, possibly negative, value.

If we keep it to just contain:paint and overflow:clip, supporting 1 to 4 values to be able to differentiate between top / bottom/ left / right seems more useful, but I don't think that'd be possible to extend to clip-path.

As for negative values, I/we said during the call that there probably was no use for them, and therefore we should reject them. Actually, I'm not sure: if you know your box as 20px worth of blank padding on all sides in which nothing will ever intrude, you might want to clip to that using a negative clip-margin, which would allow the browser to skip repainting even if some of the element is on screen, as long as it's not more than those 20px.

@AmeliaBR
Copy link
Contributor

AmeliaBR commented May 8, 2020

An interesting question is whether it should apply to clip path or not.

I would strongly argue no. The geometric nature of the clipping is too different. We can leave open the possibility of a clip-path-margin in the future if a need ever arises, but for now the most important thing is to make sure that the name of this property is clear that it doesn't affect clip-path clipping.

Based on the agreement in the call, I'd lean towards the name overflow-margin (although that might be confused for something that affects scrolling), overflow-paint-margin, or overflow-clip-margin. Focusing on overflow emphasizes that this is about clipping the child content, not the painting of this element itself. (If we were all confused about that, other people are going to be confused about it.) Another option would be to name it content-paint-margin to match with the content-visibility name we chose in #4843 .

@chrishtr
Copy link
Contributor Author

chrishtr commented May 8, 2020

Update on rounded corners: a question was raised during the call yesterday about whether the clip must take into account rounded corners. In the call we said no. Turns out contain:paint does require taking into account corner clipping, which makes sense because its clipping behavior is defined to be equivalent to overflow:clip.

Chromium and Gecko both implement this behavior for contain:paint. Example here.

I don't think there will be any significant problem implementing clipping behavior, at least in Chromium there will not. We already implement all of the complexities of these rounded corners, up to and including GPU-composited code paths.

I think the right behavior is to apply the margin before applying the algorithms of clipping behavior. Otherwise I think it would be hard to reason about for developers. Therefore in the example I linked above, if a margin of 20px was applied, the resulting clip shape would be a circle of diameter 140px; it's a circle because the example has border-radius: 50%.

@chrishtr
Copy link
Contributor Author

chrishtr commented May 8, 2020

I like the suggestion of overflow-clip-margin. This makes it clear that it has to do with the overflow clip.

@frivoal
Copy link
Collaborator

frivoal commented May 9, 2020

That all makes sense to me.

What do you think about the questions I asked in #4934 (comment) about negative values and about longhands?

@chrishtr
Copy link
Contributor Author

What do you think about the questions I asked in #4934 (comment) about negative values and about longhands?

The longhands you mentioned sounds fine. AIUI you're proposing it have the same longhands as the margin CSS property right? That was what was part of the resolution last week I think.

For negative values: sure, sounds fine to me. margin has them also, so why not. I can't think of a way this would make the implementation much harder either.

@AmeliaBR
Copy link
Contributor

I think negative values are problematic, since that could mean clipping the scrollbar on scrollable content. (For the contain-paint case, which works with overflow: auto/scroll.) Also, the use cases I can think of are graphical effects rather than performance optimizations, so it's not really consistent with the intent of the property.

I also agree with following the margin syntax. I'm not sure that it's necessary (vs having a single value for all directions), but it's clear & consistent.

@fantasai
Copy link
Collaborator

I would suggest we start off with a single value here. If we need to split it out, we can do it later. But that's 8 longhands for a single property... I'm not sure that we really need that much control in this case. If it's requested in the future we can split it out.

I think overflow-margin is fine. We don't need to make it longer imho: overflow is about contents that overflow the box, which is what we're talking about here.

@fantasai fantasai added the css-overflow-3 Current Work label May 15, 2020
@chrishtr
Copy link
Contributor Author

I would be fine with the single value for now, agree the complexity is not justified until there is a known use case.

Re overflow-margin: the problem is that we don't want it to apply to values of overflow other than clip, to avoid excessive complexity relating to scrolling, scrollbars, layout overflow and so on. overflow-clip-margin communicates that restriction more clearly.

@frivoal
Copy link
Collaborator

frivoal commented May 26, 2020

For negative margins, I'm convinced by the argument that this would make interaction with scrollbars needlessly complicated.

So, as far as I can tell, the current consensus-in-progress is that:

  • it's called either overflow-margin or overflow-clip-margin
  • it takes a single value, and doesn't have longhands (but could be expanded later if need be)
  • negative values are invalid
  • it does not add a margin to clip-path
  • it does take rounder corners into account, to avoid a discontinuity between overflow-clip-margin: 0 and overflow-clip-margin: 1px

@chrishtr
Copy link
Contributor Author

So, as far as I can tell, the current consensus-in-progress is that:

Agreed.

@frivoal frivoal reopened this May 29, 2020
frivoal added a commit that referenced this issue May 29, 2020
frivoal added a commit to frivoal/csswg-drafts that referenced this issue May 29, 2020
@frivoal
Copy link
Collaborator

frivoal commented May 29, 2020

Reopening, as I think @tabatkins' edits don't quite nail it.
Fixes I've done already:

  • Define negative values to be invalid (in prose and using the range syntax)
  • Move the description of the interaction between paint overflow and overflow-clip-margin from css-contain L1 to L2 (L1 is at REC already, and doesn't need to define interaction with features introduced after it)

Things I haven't done yet, but think we should consider:

  • The current way this is set up in spec terms is awkward, due to action at a distance: overflow-clip and paint containment are defined in terms of the padding edge, which is not defined to be affected by oveflow-clip-margin. Then overflow-clip-margin says it changes where these properties apply, and paint containment (but not overflow-clip) has an extra bit of text that contradicts the earlier statement that we clip at the padding edge, and says we clip further out taking overflow-clip-margin into account. Instead of doing this, I suggest:
    • defining a new term, like overflow clip edge
    • defining overflow-clip and paint overflow to clip at the overflow clip edge, instead of the padding edge
    • define how overflow-clip-margin makes the overflow clip edge expand outwards from the padding edge

@frivoal
Copy link
Collaborator

frivoal commented May 29, 2020

See #5134 for an attempt at doing what I suggested in the previous comment.

@tabatkins
Copy link
Member

Define negative values to be invalid (in prose and using the range syntax)

Whoops, sorry about that.

Move the description of the interaction between paint overflow and overflow-clip-margin from css-contain L1 to L2

👍

The current way this is set up in spec terms is awkward

My assumption was that interactions in the same spec have a low enough "distance" that the action is fine, but I'm fine with making things more explicit as well.

tabatkins pushed a commit that referenced this issue May 29, 2020
@tabatkins
Copy link
Member

Merged #5134, so I'll go ahead and re-close. Thanks for the review, @frivoal!

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

8 participants