w3c / csswg-drafts

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

[css-masking] Find a way for clip-path to play nicely with borders and filters #5881

Closed noamr closed 1 month ago

noamr commented 3 years ago

Currently, clip-path has a lot of great options. However, it has two limitations:

It would be great if there was a shape-based option that behaved like border-radius:

Maybe a new property border-path (between border-radius and clip-path)? Maybe an additional parameter to clip-path?

jsnkuhn commented 3 years ago

clip-stroke is a name that comes to mind.

Something that creates an inner stroke by default to match the border behavior. Would also need the options to match border-style, border-width and border-color

noamr commented 3 years ago

clip-stroke is a name that comes to mind.

Though note that this is not just about stroke - also about clipping behavior of filters. It's more in line with how border-radius works in conjunction with overflow: scroll than it is about stroke.

SebastianZ commented 2 years ago

What about a clip-type property? That property would define to what clip-path applies and how it works.

The default value would then define the current behavior and a second one the behavior @noamr describes.

Having this as a separate property allows to change or animate the path without having to re-define the clipping behavior.

(And with that, maybe clip could be revived as a shorthand for both. Though that discussion is off-topic.)

Sebastian

noamr commented 2 years ago

What about a clip-type property? That property would define to what clip-path applies and how it works.

The default value would then define the current behavior and a second one the behavior @noamr describes.

Having this as a separate property allows to change or animate the path without having to re-define the clipping behavior.

I like the idea, though maybe I would bikeshed about a word other than type which tends to be overused (masking-behavior?)

Anyway I'd love to see how to advance this. @tabatkins any thoughts? Should I try to get this into a CSSWG call agenda?

jsnkuhn commented 2 years ago

I don't really think giving clip-path the ability to have borders/box-shadows etc is the right way to go here. clip-path is already doing what it's intended to do i.e clip a path. Personally think It makes more sense for there to be a separate property to deal with the creation of an element shape (like element-shape/ element-path).

I find the idea of using a property called clip-path to create a non clipped element confusing. Why create a situation where a property name including the the word "clip" in it doesn't clip?

noamr commented 2 years ago

I don't really think giving clip-path the ability to have borders/box-shadows etc is the right way to go here. clip-path is already doing what it's intended to do i.e clip a path. Personally think It makes more sense for there to be a separate property to deal with the creation of an element shape (like element-shape/ element-path).

I find the idea of using a property called clip-path to create a non clipped element confusing. Why create a situation where a property name including the the word "clip" in it doesn't clip?

It would still clip the contents but not the border/effects. So it's still a clip, albeit partial. But I could go both ways with it as long as it's going forward :)

bradkemper commented 2 years ago

I find the idea of using a property called clip-path to create a non clipped element confusing. Why create a situation where a property name including the the word "clip" in it doesn't clip?

Yeah, but I think that’s a mistake in its original property name. Imagine it was just called path. Then authors would not be surprised to learn that the background wasn’t visible outside the path, and might naturally expect border to follow the path. The box-shadow property would be most convenient for having a shadow that followed the path, even though the shape is probably not a box anymore, and certainly filter: drop-shadow() would be expected to follow the shape. And shape-outside/inside should be able to use the same shape without repeating it.

noamr commented 2 years ago

I find the idea of using a property called clip-path to create a non clipped element confusing. Why create a situation where a property name including the the word "clip" in it doesn't clip?

Yeah, but I think that’s a mistake in its original property name. Imagine it was just called path. Then authors would not be surprised to learn that the background wasn’t visible outside the path, and might naturally expect border to follow the path. The box-shadow property would be most convenient for having a shadow that followed the path, even though the shape is probably not a box anymore, and certainly filter: drop-shadow() would be expected to follow the shape. And shape-outside/inside should be able to use the same shape without repeating it.

I think part of this came from the fact that border-radius is part of the css-background spec and clip-path is part of the css-masking spec, though IMO they should have been made consistent, clip-path being a superset of border-radius allowing the same behavior but more expressive paths than just rounded corners. That's why I initially proposed border-path to remain consistent.

bradkemper commented 2 years ago

I think part of this came from the fact that border-radius is part of the css-background spec and clip-path is part of the css-masking spec, though IMO they should have been made consistent, clip-path being a superset of border-radius allowing the same behavior but more expressive paths than just rounded corners.

Yes, border-radius is proof that the ordinary and familiar border property can follow more complex shapes, without having to resort to something live SVG stroke (though if we went that route for arbitrary shapes, we'd need to decide how to handle path corner mitering in a CSS way [maybe not stroke-linejoin], which is probably a whole other conversation).

Border-radius is still useful and convenient by itself though.

That's why I initially proposed border-path to remain consistent.

This idea is growing on me. Here are my thoughts on how that could work:

Border-path

noamr commented 2 years ago

Border-path

  • border-path defines a path using syntax derived from clip-path and shape-outside.

Namely, a CSS shape.

  • clip-path can have a value of border-path, which would cause it to use the path defined in the border-path property.
  • Any non-initial value for border-path will set the computed value of clip path to either border-path or none, depending on if overflow is visible or not.
  • shape-outside can have a value of border-path, which would cause it to use the path defined in the border-path property, offset by the value of margin-top or margin-block-start. This is only visibly noticeable if the element is floated.

Interesting, I like the direction. I originally thought you'd need to use CSS variables for them to be the same. I wonder though, why could border-path be a reference value, and not shape-outside or clip-path? (i.e.. why clip-path: border-path and not border-path: clip-path)? What makes that one the "root" path?

I also wonder how it should interact with border-radius.

bradkemper commented 2 years ago

Namely, a CSS shape.

Yes, but including <basic-shape>, <shape-box>, and <image>.

The CR of Shapes is here:

https://www.w3.org/TR/css-shapes/#typedef-basic-shape

and it could use <clip-source> too from Clip Path:

https://www.w3.org/TR/css-masking-1/#typedef-clip-source

Interesting, I like the direction. I originally thought you'd need to use CSS variables for them to be the same. I wonder though, why could border-path be a reference value, and not shape-outside or clip-path? (i.e.. why clip-path: border-path and not border-path: clip-path)? What makes that one the "root" path?

It just felt like border-path would mostly subsume using clip-path as a separate property, and it wouldn’t really be needed much anymore, unless someone wanted to see a clipped border or something. Thus, people would set the path in border-path, and other properties, including border, overflow, clip-path, and margin would adapt to intuitive expectations, where border, overflow, and margin would be used in super-familiar ways, but for a shape that isn’t necessarily a (potentially round-cornered) rectangle. So, you wouldn’t need to need to use clip-path by itself much anymore, and you wouldn’t want circular references for what the shape is (both properties saying to get the shape from the other one). You would still need clip-rule to affect a path that was imported into border-path.

For shape-outside, I think you still might want some other shape besides the one set by border-path, so that should be where you make that choice. It didn’t seem like much would be gained by going the other way, other than possible circular references again. So, let the path of the border shape be the reference.

I also wonder how it should interact with border-radius.

I’d say it mostly shouldn’t. You either use the box and it’s sides and (possibly rounded) corners for the shape, or you create some other shape. Suppose you have border-path: padding-box inset(12px) and border-radius: 30px. Then your shape would still be a rounded rectangle, with 18px corner radii.

noamr commented 2 years ago

Namely, a CSS shape.

Yes, but including <basic-shape>, <shape-box>, and <image>. Got it.

It just felt like border-path would mostly subsume using clip-path as a separate property, and it wouldn’t really be needed much anymore, unless someone wanted to see a clipped border or something. Thus, people would set the path in border-path, and other properties, including border, overflow, clip-path, and margin would adapt to intuitive expectations, where border, overflow, and margin would be used in super-familiar ways, but for a shape that isn’t necessarily a (potentially round-cornered) rectangle. So, you wouldn’t need to need to use clip-path by itself much anymore, and you wouldn’t want circular references for what the shape is (both properties saying to get the shape from the other one). You would still need clip-rule to affect a path that was imported into border-path.

Thanks, this explains it.

I also wonder how it should interact with border-radius.

I’d say it mostly shouldn’t. You either use the box and it’s sides and (possibly rounded) corners for the shape, or you create some other shape. Suppose you have border-path: padding-box inset(12px) and border-radius: 30px. Then your shape would still be a rounded rectangle, with 18px corner radii.

OK, I get how it would behave in those simple cases, but what would be the behavior if you have both a complex path and a border radius?

noamr commented 2 years ago

@bradkemper: either way, I'm happy to continue pushing this and flesh out the details. How do we go about this?

jsnkuhn commented 2 years ago

inset() already has an optional syntax for rounding corners ie clip-path: inset(12px round 30px); so

border-path: padding-box inset(12px) ;
border-radius: 30px;

would really never need to be a thing.

I've also suggested in other threads that clip-path: corners( angle round 50%); could probably become a thing as an alternative way to implement corner-shape

smfr commented 2 months ago

See also https://github.com/w3c/csswg-drafts/issues/6997

smfr commented 1 month ago

I think https://github.com/w3c/csswg-drafts/issues/6997 is closer to what we want than this. Something like border-shape isn't about clipping and masking, it's closer to corner-shape/border-radius. I'd prefer we close this as a dup of that issue, and continue discussion there.

noamr commented 1 month ago

I think https://github.com/w3c/csswg-drafts/issues/6997 is closer to what we want than this. Something like border-shape isn't about clipping and masking, it's closer to corner-shape/border-radius. I'd prefer we close this as a dup of that issue, and continue discussion there.

Works for me! I like where #6997 is heading.