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-masking] Find a way for clip-path to play nicely with borders and filters #5881

Closed
noamr opened this issue Jan 19, 2021 · 17 comments
Closed

Comments

@noamr
Copy link
Collaborator

noamr commented Jan 19, 2021

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

  • You can't use it to define the stroke of a border
  • It clips its element's box shadow and filters (e.g. blur / drop-shadow) - while this behavior is vaguely defined.

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

  • it clips the contents when overflow: hidden, but not its own filters
  • The borders trace it

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

@jsnkuhn
Copy link

jsnkuhn commented Oct 2, 2021

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
Copy link
Collaborator Author

noamr commented Oct 3, 2021

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
Copy link
Contributor

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
Copy link
Collaborator Author

noamr commented Jun 30, 2022

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
Copy link

jsnkuhn commented Jun 30, 2022

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
Copy link
Collaborator Author

noamr commented Jun 30, 2022

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
Copy link
Contributor

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
Copy link
Collaborator Author

noamr commented Jul 1, 2022

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
Copy link
Contributor

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

  • border-path defines a path using syntax derived from clip-path and shape-outside.
  • Initial value of border-path is padding-box, meaning the path follows the outside of the padding box, and the stroke is drawn outside of that. That is, the path is defined as the edge between the inner border and the outer padding (and that path describes the padding box), with the stroke always being outside the path, not centered on it like SVG path strokes. This is a CSS border.
  • If you were to use SVG to simulate/polyfill a border-path with a border, you would need to offset the path by half the border-width and then use the border-width as the stroke width.
  • 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.
  • Margin: just as border-radius doesn’t affect margin, neither does border-path. However:
  • 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. shape-outside: margin-box would still use the four sides and 4 corners to determine float shape.
  • Any non-initial value for border-path will cause box-shadow to follow the path (think of it as shape-shadow, if that helps). The shadow is a blurred and/or offset and/or spread copy of the shape created by the border-path, with the original non-offset, non-blurred, non-spread shape knocked out of it (or if inset keyword is used, everything outside of that shape knocked out of it).

@noamr
Copy link
Collaborator Author

noamr commented Jul 6, 2022

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
Copy link
Contributor

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
Copy link
Collaborator Author

noamr commented Jul 7, 2022

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
Copy link
Collaborator Author

noamr commented Jul 7, 2022

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

@jsnkuhn
Copy link

jsnkuhn commented Jul 7, 2022

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
Copy link
Contributor

smfr commented Aug 2, 2024

See also #6997

@smfr
Copy link
Contributor

smfr commented Aug 27, 2024

I think #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 noamr closed this as completed Aug 27, 2024
@noamr
Copy link
Collaborator Author

noamr commented Aug 27, 2024

I think #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.

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