Skip to content

[css-borders-4] Consider constraining radii for concave corner-shape when opposite corners overlap #12098

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

Open
noamr opened this issue Apr 17, 2025 · 13 comments

Comments

@noamr
Copy link
Collaborator

noamr commented Apr 17, 2025

The following CSS would cause the top-right and bottom-left corner to overlap, even without borders:

border-top-right-radius: 70%;
border-bottom-left-radius: 70%;
corner-shape: notch;

Basically anything more concave than this:
Image

would result in the corner collapsing on each other.

Since we constraint radius to not overlap in the ordinary round case, we should probably constraint it (or constraint the shape) in this case as well.

(Brought up by @smfr)

@SebastianZ
Copy link
Contributor

Sounds reasonable on first thought. Though avoiding overlapping also quickly becomes complex. Should borders also be accounted for? Should different shapes be handled differently (e.g. scoop vs. notch or round)? What about <length>s vs. <percentage>s as border radii? What about different values for horizontal and vertical radii?

Also, should there be an option to control this constraint? I assume there are use cases, for which overlapping is actually expected to achieve specific effects for split backgrounds. (Those might also be achievable via border-shape, though, depending on specifics in its definition.)

Sebastian

@noamr
Copy link
Collaborator Author

noamr commented Apr 21, 2025

Sounds reasonable on first thought. Though avoiding overlapping also quickly becomes complex. Should borders also be accounted for? Should different shapes be handled differently (e.g. scoop vs. notch or round)? What about <length>s vs. <percentage>s as border radii? What about different values for horizontal and vertical radii?

In a previous resolution we concluded on not considering borders here, only the outer contour. It simplifies things a lot.
Note that radii on the same axis are already constrained.

I think this is a question of whether we constrain the radii or the shape to not overlap diagonally in the outer contour. The rest would fall into place.

Also, should there be an option to control this constraint? I assume there are use cases, for which overlapping is actually expected to achieve specific effects for split backgrounds. (Those might also be achievable via border-shape, though, depending on specifics in its definition.)

Since we don't allow it for border-radius currently I think we should stick with that. The edge cases for overlapping borders are sufficiently edgy.

Sebastian

@smfr
Copy link
Contributor

smfr commented Apr 21, 2025

Overlapping brings up all kinds of complexities, like whether you're filling the border with an even-odd or non-zero wind rule. I think we have to avoid it.

@SebastianZ
Copy link
Contributor

Sounds reasonable on first thought. Though avoiding overlapping also quickly becomes complex. Should borders also be accounted for? Should different shapes be handled differently (e.g. scoop vs. notch or round)? What about <length>s vs. <percentage>s as border radii? What about different values for horizontal and vertical radii?

In a previous resolution we concluded on not considering borders here, only the outer contour. It simplifies things a lot.

I see! I obviously missed that resolution. So yes, that makes it much easier to handle.

Note that radii on the same axis are already constrained.

I know, but radii on the same axis are also relatively simple to handle.

I think this is a question of whether we constrain the radii or the shape to not overlap diagonally in the outer contour. The rest would fall into place.

Right.

Also, should there be an option to control this constraint? I assume there are use cases, for which overlapping is actually expected to achieve specific effects for split backgrounds. (Those might also be achievable via border-shape, though, depending on specifics in its definition.)

Since we don't allow it for border-radius currently I think we should stick with that. The edge cases for overlapping borders are sufficiently edgy.

Fair enough. If use cases for that come up at some point, we can still discuss it separately.

Overlapping brings up all kinds of complexities, like whether you're filling the border with an even-odd or non-zero wind rule. I think we have to avoid it.

Just to note, even if we don't allow overlapping for corner-shape + border-radius, we still need to solve that for border-shape.

Sebastian

@noamr
Copy link
Collaborator Author

noamr commented May 6, 2025

I actually thought of a third option that has some nice attributes to it: constraint the radius in only one of the dimensions.

For the following CSS:

border-top-right-radius: 70%;
border-bottom-left-radius: 70%;
corner-shape: notch;

where the radius in the next clockwise dimension remains unconstrained, and the radius in the counterclockwise dimension is constrained in such a way that the corners do not overlap. See options below for how this is going to look like.

This gives us 3 options. For each one I'll show what CSS it would be equivalent to and how the OP example would look like:

Original:

border-top-right-radius: 70%;
border-bottom-left-radius: 70%;
corner-shape: notch;
  1. Constrain the radius.
border-top-right-radius: 50%;
border-bottom-left-radius: 50%;
corner-shape: notch;

Image

  1. Constrain the shape
border-top-right-radius: 70%;
border-bottom-left-radius: 70%;
/* 1.02 is some computed number that puts the diagonal half corners in the same point */
corner-shape: superellipse(-1.02);

Image

  1. Constrain the radius in one dimension
border-top-right-radius: 50% 70%;
border-bottom-left-radius: 50% 70%;
corner-shape: notch;

Image

@tabatkins
Copy link
Member

When we're shrinking adjacent corners, we purposely shrink both radiuses, to keep the corner "shaped" the same. We also shrink both corners the same amount, just because there's no way to know which we should bias towards. I think we should do the same here.

That is, I think option 1 is the only reasonable option. Changing the superellipse parameter, or changing only one radius, both change the corner shape in dramatic ways, and I don't think either of those are acceptable outcomes.

@noamr
Copy link
Collaborator Author

noamr commented May 6, 2025

When we're shrinking adjacent corners, we purposely shrink both radiuses, to keep the corner "shaped" the same. We also shrink both corners the same amount, just because there's no way to know which we should bias towards. I think we should do the same here.

That is, I think option 1 is the only reasonable option. Changing the superellipse parameter, or changing only one radius, both change the corner shape in dramatic ways, and I don't think either of those are acceptable outcomes.

I am fine with this but would like to hear from others (@smfr / @SebastianZ / @LeaVerou / @fantasai ?).
If we get a rough consensus I'm happy to async resolve, or Agenda+ if we don't.

@LeaVerou
Copy link
Member

LeaVerou commented May 7, 2025

Happy to resolve to what @tabatkins proposed.

@smfr
Copy link
Contributor

smfr commented May 7, 2025

Agree that constraining the radii makes more sense than changing the shape. But how exactly does the constraining work? Does the superellipse value affect the amount of constraining? If one corner is notch, and the opposite corner is scoop, are they constrained equally? Do we constrain just enough such that the inner edge of the border touches (i.e. you never end up with two disjoint shapes as seen above)?

@noamr
Copy link
Collaborator Author

noamr commented May 8, 2025

Agree that constraining the radii makes more sense than changing the shape. But how exactly does the constraining work? Does the superellipse value affect the amount of constraining? If one corner is notch, and the opposite corner is scoop, are they constrained equally? Do we constrain just enough such that the inner edge of the border touches (i.e. you never end up with two disjoint shapes as seen above)?

A model we can follow is something as such:

  • We compute the hull for both corners
  • If the hulls don't intersect, do nothing
  • Find how many pixels the diagonal of each corner would have to shrink by independently in order for the hulls to touch but not intersect
  • Find the ratio between diagonals
  • Use the above to shrink the corners so that the ratio between the diagonals remains the same as before, but the hulls touch and don't intersect.
  • exact math TBD, but with hulls it's not too complex/costly.

@tabatkins
Copy link
Member

Yes, that sounds like a reasonable approach to me. I suspect there's a lot of potential flexibility in what exactly we do, but that algo seems straightforward and thematically consistent with how we shrink adjacent corners.

@noamr
Copy link
Collaborator Author

noamr commented May 12, 2025

Yes, that sounds like a reasonable approach to me. I suspect there's a lot of potential flexibility in what exactly we do, but that algo seems straightforward and thematically consistent with how we shrink adjacent corners.

@smfr does this seem reasonable to you?
The algorithm in the spec would be a bit high level, something like defining the "find a scale factor by which scaling both radii would make it so the quadrilateral hulls created by the shaped corners would touch but not intersect".

An implementation can use bsearch for this or find something more fancy/optimized.

@noamr
Copy link
Collaborator Author

noamr commented May 12, 2025

I've prototyped this: https://noamr.github.io/squircle-testbed/radii-constraint.html
(This works with the existing blink implementation of corner-shape, so requires Chrome Canary with experimental features turned on)

I think it looks quite reasonable.

@noamr noamr added the Agenda+ label May 12, 2025
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

5 participants