Skip to content
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

[css-images-4] Proposal to allow replaced elements to paint outside of their bounds #7058

Closed
tabatkins opened this issue Feb 15, 2022 · 10 comments

Comments

@tabatkins
Copy link
Member

Context: WICG/view-transitions#120, where we want to snapshot an element as an image, paint it into a box of the right size, but also get the "decorations" (borders, outlines, shadows) painted outside of that box.

This seems like a useful functionality more generally, as well; in some cases you can use border-image to make an image paint "outside of" an element, but the painting area still takes up layout space (you inset from the edge of the border area, but can't go outside of it). This can allow an author to, for example, create an image with a custom glow or shadow applied, with proper ink-overflow behavior like a CSS shadow would have.

I propose we do this with two new object-* properties:


Name: object-view-box
Values: none | <length-percentage>{4}
Initial: none
Percentages: For the first and third values, relative to the object's original natural width. For the 2nd and 4th, relative to the object's original natural height.

object-view-box takes an x/y/width/height quartet of lengths, and has two effects:

  • The object's "natural size" is changed to the specified width and height. (This happens before the other object-* properties take effect; they work on the new natural size.)
  • The replaced content is shifted such that the point (x,y) of its paint rectangle is in the top-left of its natural size rectangle, and the point (x+width, y+height) is in the bottom-right of its natural-size rectangle.
  • If this puts any pixels outside of the natural size rectangle, and object-fit/position/overflow allow for it, these pixels will be visible. (By default, the natural size rectangle exactly fills the element and is clipped to it, so any pixels outside the natural size rectangle won't be visible.)
  • If the replaced content itself has its own notion of "view box" (such as SVG), this has no special interaction with it - it applies on top with the listed effects.

Name: object-overflow
Values: hidden | visible
Initial: hidden
  • If hidden, the replaced content is clipped to the element's content box.
  • If visible, the replaced content can overflow the element's content-box. This overflowing content is counted as ink overflow. (To overflow, at least one of the other object-* properties must be in use.)

So for the use-case in the linked issue, we'd set the w/h part of object-view-box to the size of the snapshotted element, and x/y according to the amount of snapshot margin captured around it (to get shadows/etc), and set object-overflow: visible to let those shadow ink-overflow properly. If we then let the element size per its natural size, it will correctly take on the size of the snappshotted border box exactly, which is precisely what we want.

Thoughts? Concerns? Other possible directions?

/cc @chrishtr @jakearchibald @khushalsagar

@vmpstr
Copy link
Member

vmpstr commented Feb 16, 2022

I think this would work for both the overflowing pixels and pixels that are clipped to be smaller than the element box (e.g. with clip-path), since that allows us to capture just the bounds of the actual visible pixels and then use object-view-box to position them appropriately.

One question: presumably this has no direct interaction with something like witdth/height changes but is affected by transform changes, right? If we create an animation to change the width, we would have to create an animation to also change the object-view-box properties to change in tandem so that it looks like the element is shrinking and its drop shadow (for e.g) is changing shape but is remaining relative to the bottom right of the new element

I think that's fine, but if we can have some sort of a syntactic sugar to help, it would be awesome. Thinking out-loud: I'm thinking something like if we had object-intrinsic-size and object-view-box where the object-intrinsic-size would specify how big the replaced element naturally is and object-view-box is what you said (except that it only affects the pixel content size not the.. natural size (i fully appreciate that we might need new terminology)). Then if you do a width animation, you would note that the object-intrinsic-size doesn't match the element size anymore, and you can figure out exactly how to scale the object-intrinsic-size to make the two rects align. Then you apply that same scale to the object-view-box which should scale the pixel contents in the same way as the width animation is changing the element

Let me know if any of that makes sense and what your thoughts are

@fantasai
Copy link
Collaborator

fantasai commented Mar 1, 2022

If the replaced content itself has its own notion of "view box" (such as SVG), this has no special interaction with it - it applies on top with the listed effects.

SVG has been on a quest to map all its attributes to CSS properties, why not map this to the viewbox attribute?

@vmpstr
Copy link
Member

vmpstr commented Mar 1, 2022

presumably this has no direct interaction with something like witdth/height changes but is affected by transform changes, right?

I probably misunderstood the proposal, since I think the width/height changes will naturally change the object, by scaling its natural dimensions (subject to object-fit).

@khushalsagar
Copy link
Member

The idea looks good. I want to reiterate to make sure I understand it correctly and also clarify how this interacts with existing sizing/positioning options of object-fit and object-position.

So for example if we have an element sized 100X200 with a 10px shadow on all edges, it's snapshot's intrinsic size is 120X220. We'd render this in a replaced element sized to 100X200 (which provides the specified size) with object-view-box: 10,10,100,200. The natural size used by the replaced element's sizing algorithm is then 100X200 and the paint content is the subset specified using object-view-box.

What happens when this image is rendered in a replaced element of specified size of 100X100 with object-fit: contain and object-position: left top? I'm assuming we'd downscale the complete image to 50X100. So now point (5,5) in the downscaled image will map to the origin of the replaced element's content-box.

@tabatkins
Copy link
Member Author

So for example if we have an element sized 100X200 with a 10px shadow on all edges, it's snapshot's intrinsic size is 120X220. We'd render this in a replaced element sized to 100X200 (which provides the specified size) with object-view-box: 10,10,100,200. The natural size used by the replaced element's sizing algorithm is then 100X200 and the paint content is the subset specified using object-view-box.

Correct.

What happens when this image is rendered in a replaced element of specified size of 100X100 with object-fit: contain and object-position: left top? I'm assuming we'd downscale the complete image to 50X100. So now point (5,5) in the downscaled image will map to the origin of the replaced element's content-box.

Correct; that's still the edge of the element-box part of the image, with the shadow outside of that point. The view-box shifting/stretching happens before any of the other object-* adjustments.

@tabatkins
Copy link
Member Author

One question: presumably this has no direct interaction with something like witdth/height changes but is affected by transform changes, right? If we create an animation to change the width, we would have to create an animation to also change the object-view-box properties to change in tandem so that it looks like the element is shrinking and its drop shadow (for e.g) is changing shape but is remaining relative to the bottom right of the new element

You mean if you're animating the source element providing the image snapshot? Yes, you'd have to align the object-view-box on the pseudo-element hosting the snapshot to match it.

If we wanted sugar, we could invent a new "natural view-box" term (which all existing images wouldn't have), and let the snapshotting process produce an image with the natural view-box set appropriately to match the element dimensions; then we can have an object-view-box: from-image value just use that.

@khushalsagar
Copy link
Member

I'm assuming we'd downscale the complete image to 50X100.

I meant to say that the downscaled image would be 60X110 in this case with a 5px shadow around it. But I think you got it. :)

I'm also assuming that object-view-box should be a strict subset of the image's intrinsic size, since it's meant to be a subset of the image's pixels. So x/y can't have any negative values and x+width/y+height is capped to its intrinsic width/height?

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-images-4] Proposal to allow replaced elements to paint outside of their bounds, and agreed to the following:

  • RESOLVED: We add to Images 5 these 2 properties, object-viewbox and object-overflow. 1 is bool about hidden and visible and the other is eq. of SVG viewbox. We would try and create a mapping between svg and new property. New property would take inset and xywh so there's 2 ways to spec size
The full IRC log of that discussion <dael> Topic: [css-images-4] Proposal to allow replaced elements to paint outside of their bounds
<dael> github: https://github.com//issues/7058
<fantasai> s/current behavior/current syntax
<dael> TabAtkins: While discussing the shared element transitions proposal a couple weeks ago there was some obvious need to control how the image snashot sized better
<dael> TabAtkins: The image snapshot can be size to elements but larger to capture outlines or shadows. Awk interaction with normal image sizing. When looking at Jake prop I realized this would be useful in general and addresses a gap in Paint API
<dael> TabAtkins: General is add 2 prop to object family; object-viewbox and object-overflow. Apply a iewbox to replaced content and allow it to overflow context box
<dael> TabAtkins: Viewbox is same grammer as svg and has same effect. Let's you say x/y boundary is the image boundary.
<dael> TabAtkins: [gives example]
<dael> TabAtkins: The natural size of image adjusts to spec width and height. For sizing it assumes the elemnt transition snapshot is the size of element border box. If part outside is displayed somehow due to something like object-overflow you can see the rest like the shadows
<dael> TabAtkins: Generalizing the use case, one of the use cases we wanted to handle in Houdini is doing custom shadows. Groups like google material design did optics based shadowing. b/c couldn't paint outside hte bounds you had to do weird tricks to do it
<dael> TabAtkins: This would allow you to do that exact thing as well when paired with object-overflow. 2 properties, hidden and visible. hidden is like today
<dael> TabAtkins: With these 2 properties would help a lot with shared element transitions but useful general property
<dael> TabAtkins: fantasai provided feedback might be good to have it just be viewport and we upgrade the svg to a property and do that.
<Rossen_> q?
<smfr> q+
<dael> TabAtkins: I'm fine with that if people believe it's a good way to go
<dael> TabAtkins: Last I checked no other feedback. Any quesitons or concerns I'd love to hear it.
<dael> TabAtkins: Else could add to images
<Rossen_> ack smfr
<dael> smfr: Sounds reasonable. Not a huge fan of new ways to do ink overflow, but I see the use case
<dael> smfr: If you do object overflow and then object-fit:cover you'll have bits outside that don't respond to hit testing. Might enocourage people to make weird content for hit testing
<plinss> q+
<dael> TabAtkins: That is true. I don't know how to address it, though. It's part of core functionality since shadows shouldn't hit, but you could also do cover. Not sure if we can slve that or put a note saying don't do
<dael> smfr: Note is prob fine
<dael> smfr: Other thought is how it interacts with a-r
<Rossen_> q?
<dael> TabAtkins: Should be straightfoward. Changes element natural size to spec width and height and that implies a-r. I'm not sure if we allow width and height w/o a-r. Should have same effect. 2:1 and set square viewbox it's 1:1 for all other purposes
<dael> smfr: Alright
<Rossen_> ack plinss
<dael> plinss: I thought I heard there would be viewbox property that takes same as svg viewbox?
<dholbert> q+
<dael> plinss: Little concerned b/c svg will look a bit like standard rect and it's not. Might be confusing. Maybe have it be a shape function?
<dael> TabAtkins: Would that imply we want to allow other shape or is this just special for the property?
<dael> plinss: Restrict to small set of shapes. rect and inset might make sense.
<dael> plinss: Just confusing about a bare property that takes 4 values but it's not the same
<fantasai> +1 to plinss's suggestion of both rect() and inset()
<dael> TabAtkins: I get you. And having rect and inset would be pretty worthwhile. If you use viewbox you have to get width and height but inset lets you set 20px. I think that sounds pretty good
<dael> plinss: I'm not opposed to having another one to take svg syntax for people who know it but we should call out
<dael> TabAtkins: Doesn't rest take svg?
<dael> plinss: I think it's trbl
<dholbert> s/rest/rect/
<dael> s/rest/rect
<dael> plinss: I don't want to rat hole, but calling out
<dael> TabAtkins: We don't have rect, we have inset. So add a rec or some other name that takes xy stuff
<fantasai> xywh
<vmpstr> q+
<dael> plinss: There is rect on clip unfortunately
<fantasai> xywh()
<dael> TabAtkins: Okay
<Rossen_> ack dholbert
<dael> dholbert: One other concern with svg viewbox is it's a list of 4 numbers and doesn't allow units or %. Might make it we'd have to extend a fair bit. Might be worth abandoning it for that purpose
<dael> TabAtkins: I would have presumed we upgrade to css units. But point taken
<Rossen_> ack vmpstr
<dael> vmpstr: I wanted to confirm how scaling works. If we scale images such that we squish. Squish the viewbox and overflow squishes
<dael> TabAtkins: Right. If you scale image of svg element using viewbox the whole thing squishes including the outside
<dael> fantasai: For rect function media uses xywh. Makes it clear what each argument would be.
<dael> fantasai: For svg correspondence we've tried to create mappings. We should look more b/c if we can make them map it solves viewbox which has not had a mapping
<dael> TabAtkins: I don't think there is a big problem...speaking from ignorance but suspect not a big problem with making this property override viewbox of inline svg. Probably okay.
<dael> TabAtkins: Should put in as an issue without effecting rest of design
<dael> fantasai: If you put it in from an embedded svg it should have same effect
<dael> TabAtkins: not sure about that. That would mean that applying the style to an image element could have distinct effects based ono source type
<dael> fantasai: True for width and height. If you apply sizing to root svg for embedded vs inline get different
<dael> TabAtkins: Talking embedded svg vs png
<dael> fantasai: No, if you have it inside the svg file
<Rossen_> q?
<Rossen_> ack fantasai
<dael> TabAtkins: Yes, yes. If it applies to svg and html it would apply to raw svg as well
<TabAtkins> s/svg and html/svg-in-html/
<dael> fantasai: Sounds like prop is we have these 2 properties, object-viewbox and object-overflow. 1 is bool about hidden and visible and the other is eq. of SVG viewbox. We would try and create a mapping between svg and new property. New property would take inset and xywh so there's 2 ways to spec size
<dael> fantasai: And that's in images 5
<dael> TabAtkins: Correct
<dael> Rossen_: Other opinions?
<dael> Rossen_: Objections?
<dael> RESOLVED: We add to Images 5 these 2 properties, object-viewbox and object-overflow. 1 is bool about hidden and visible and the other is eq. of SVG viewbox. We would try and create a mapping between svg and new property. New property would take inset and xywh so there's 2 ways to spec size

@dholbert
Copy link
Member

dholbert commented Mar 3, 2022

If the viewBox-like property here does indeed end up being an alias / override for the SVG viewBox attribute, then a few things to consider...

  • we should make sure it's unambiguous what happens if you set this new property on the img and/or internal <svg> element in an <img src="foo.svg"> scenario.
  • There are a few other elements (beyond <svg>) that also accept the viewBox attribute. According to https://www.w3.org/TR/SVG2/attindex.html and https://www.w3.org/TR/SVG11/attindex.html , the viewBox attribute applies to the marker, pattern, svg, symbol, and view elements. So, we should probably be sure the new property has similar effects on those elements.

tidoust added a commit to tidoust/csswg-drafts that referenced this issue Mar 8, 2022
The `<basic-shape-rect>` construct introduced in w3c#7058 needs to be wrapped in
another pair of `<>`, otherwise it gets interpreted as an unknown custom markup
tag and property value gets rendered as: `none |`.
fantasai added a commit that referenced this issue Sep 2, 2022
…stylesheet expectations and note about change from 2.1 #7058 #7144
@fantasai
Copy link
Collaborator

@tabatkins The WG resolution said to add this to CSS Images L5, not L4. And it was said to map it to the SVG viewbox attribute, not make it similar but different. Please try to pay more attention to what you're supposed to be editing when making edits? :(

jakearchibald pushed a commit to jakearchibald/csswg-drafts that referenced this issue Jan 16, 2023
jakearchibald pushed a commit to jakearchibald/csswg-drafts that referenced this issue Jan 16, 2023
liZe added a commit to Kozea/WeasyPrint that referenced this issue Mar 13, 2024
liZe added a commit to Kozea/WeasyPrint that referenced this issue Mar 13, 2024
liZe added a commit to Kozea/WeasyPrint that referenced this issue Mar 16, 2024
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

6 participants