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-shapes-2] corner-shape support for superellipsis #10993

Open
fserb opened this issue Oct 2, 2024 · 9 comments
Open

[css-shapes-2] corner-shape support for superellipsis #10993

fserb opened this issue Oct 2, 2024 · 9 comments

Comments

@fserb
Copy link
Member

fserb commented Oct 2, 2024

I worked a bit on trying to find an option for corner-shape on the new API that supported squircles and had inner/outer symmetry.

After a bit of work I'm proposing we add a value se(x) (name TBD) that exposes a superellipse:

$$ \left|\frac{x}{a}\right|^n + \left|\frac{y}{b}\right|^n = 1 $$

Where $a$ and $b$ are the width and height of the shape. And the default parameter $n$ works in the interval $[0,\infty)$ where:

  • $n=0$ is a inwards square
  • $n=0.5$ is a inwards circle
  • $n=1$ is bevel
  • $n=2$ is a circular border (like rounded corner)
  • $n=4$ is a squircle
  • $n\rightarrow\infty$ is a rectangle

My proposal would be to use $x$ as a parameter where $n=2^x$. This leads to a function that is symmetrical on $0$ where positive values are outside the bevel and negative values are inside, such as:

  • $n\rightarrow-\infty$ is a inwards square
  • $n=-1$ is a inwards circle
  • $n=0$ is bevel
  • $n=1$ is a circular border (like rounded corner)
  • $n=2$ is a squircle
  • $n\rightarrow\infty$ is a rectangle

I've wrote a small demo with this function here.

The superellipse is very easily parameterizable (both angular and $[0,1]$), so I'm guessing it shouldn't be a problem to implement it in an efficient way. There's a small issue where if we are "inside the bevel" the corners can sometimes overlap. For those cases, we will have to do some math and limit the size of the corners to prevent it from happening (the demo currently doesn't handle those cases).

There are other available squircle formulations (like the Fernandez-Guasti squircles) where the parameter is more intuitive ($0$ means circle, $0.5$ is squircle, and $1$ is square). Unfortunately, those formulations don't extend to inwards the bevel, so we would lose some expressiveness.

We could, together with this, also provide certain easy-to-use values (like squircle meaning se(2), or bevel meaning se(0), etc...). And they could also contain parameters (i.e. squircle(y) mapping $[0,1]$ to $[1,\infty) $).

Highlights:

  • squircles!
  • the symmetry on the bevel and around $0$ is very nice.
  • It's very expressive of a lot of shapes that people care about.

Lowlights:

  • "superellipsis" is an awful name and very non-descriptive of the intent of the shape.
  • All parameter values (for example, $2$ meaning squircle) are not intuitive or really meaningful.
@fserb
Copy link
Member Author

fserb commented Oct 2, 2024

pinging some folks that talked to me about this at some point @tabatkins @fantasai @smfr @vmpstr @LeaVerou @stubbornella @progers

@tabatkins
Copy link
Member

I quite like this! The fact that it directly handles straight lines (bevel), round corners, squircles, and square corners (and the inversions of all of them; scoop and notch) is great; it also gives us an unexpected new ability to animate between these shapes, which kinda rules.

I don't think it's a problem that the function takes numerical value with "magic numbers". That's just a result of making anything parametrized. The fact that all the "meaningful" behaviors (those common enough to be granted names) are the integer values 0, ±1, and ±2, and ±∞ means, imo, that the argument is meaningful enough.

I definitely agree with log-rescaling the parameter. It makes the function substantially easier to use and understand, with amazing symmetry (positive and negative values are the exact same shape, just convex or concave). It also makes "large enough to look square" a much more achievable value, and thus much friendlier to animation; going from se(10) to se(1) to animate a square corner becoming round looks fairly reasonable even with a linear ease (while doing the same with the original parameter would require se(1024) to se(2), which spends 99% of the animation progress being approximately square). And, due to the symmetry around zero, animating inset corners and outset corners works exactly the same, which is a very good thing. I did the same rescaling with aspect-ratio animation, which has the exact same "shape" (naive range is 0-inf, centered at 1), and it was the right move.

Quick note about animation - I suspect we want to have keywords compute to themselves, but convert to an equivalent se() during interpolation. Just something to keep in mind when speccing it.

And yeah, as you mention (and is also mentioned by Simon and me in #8591 (comment)), we'll need to define how concave corners (negative se() arguments) shrink their corresponding radiuses to avoid diagonal intersection. We can't take the easy route of just preventing the "corner rectangles" from overlapping, as that's already allowed by border-radius today (see border-radius: 100% 0 100% 0;). I'm assuming that, since superellipses have an easy radial interpretation, this isn't that complicated to detect and mitigate.


Unfortunately I do not have a better name suggestion. However, maybe we can simply avoid the issue entirely, and just represent it as a naked <number> in the grammar? This does look like a sufficiently general/useful parametrization of the corner shape that it can meaningfully usurp the entire definition.

@frivoal
Copy link
Collaborator

frivoal commented Oct 3, 2024

I really like this. Just one check, following up on a discussion we had at TPAC: does this formulation deal well with "/" version of border-radius, where for each corner, you can specify the horizontal and vertical radiuses separately?

@tabatkins
Copy link
Member

I think you're asking if superellipses support an elliptical corner (different absolute lengths for x and y radius)? If so, yes, it's a superellipse after all. ^_^

@LeaVerou
Copy link
Member

LeaVerou commented Oct 3, 2024

Okay, I know I said I no longer think corner-shape is a good idea, but this is awesome 🤩 .

@Loirooriol
Copy link
Contributor

Some previous discussions about superellipses in #6296 and #10653

@Loirooriol
Copy link
Contributor

I suspect we want to have keywords compute to themselves, but convert to an equivalent se() during interpolation

font-width is a precedent for the opposite, it computes keywords to the equivalent numeric value.

@LeaVerou
Copy link
Member

LeaVerou commented Oct 4, 2024

I suspect we want to have keywords compute to themselves, but convert to an equivalent se() during interpolation

font-width is a precedent for the opposite, it computes keywords to the equivalent numeric value.

How is that the opposite? It sounds like exactly the same? 🤔

@Loirooriol
Copy link
Contributor

Tab says to convert keywords to equivalent numeric only for interpolation. font-width does it earlier, in the computed value. The difference is observable via getComputedStyle (though we could also define a custom resolved value). But this is just a minor detail, probably better suited for a follow-up issue if we end up adding se().

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