Open LeaVerou opened 1 year ago
I really like this proposal!
Also, I think I need to mention one case which could be one of the most common, and which also falls in line with a lot of the recent work on popovers and stuff (html modals/popovers, anchor positioning) — the popovers' tip/arrows. So it would be really awesome to also have this in our toolbox.
Here is what I mean when trying the https://projects.verou.me/corner-shape/
Basically, most current implementations of the tips/arrows are either using the border hack, or using a rotated item, or masks, while using something like a corner-angle
seems like a very obvious usage for this.
This seems like a lot of added complexity for a fairly niche style.
Clip-path with polygon()
already supports basic bevels so long as you don't mind actually clipping & don't need a border style.
Then, the missing feature would be to create a border that follows the clipped shape, or to create that shape without actually clipping child overflow. So, my suggestion would be to create a border-shape
property that takes a <basic-shape>
value, the same as clip-path, but it only clips the backgrounds and then strokes the shape in the border width & style.
(Complication: which border-width & style? The shape wouldn't necessarily have 4 sides, but the browser would still need to handle a mix of border styles! Maybe: divide up the border box into triangles created by diagonal lines and stroke the sections of the border-shape that fall in each triangle according to that border style.)
For rounding corners after bevelling, I'd be happy to see a single corner-radius parameter added to the polygon function, for all of the places where the function is used: clip-path, shape outside, offset-path, maybe one day SVG. Or maybe two corner-radius parameters, one for convex corners and one for concave.
(Long term, I'd also like to get an accepted syntax for combining percentages, unit-lengths, and calc/math function with SVG path notation, so you could get all sorts of shapes! But polygons with slightly rounded corners is a nice, simple solution to many use cases.)
Benefits of replacing corner-shape
with a property that uses <basic-shape>
values:
Many styles, only 1 new property
You could create bevels, notches, arrows, or angular speech bubbles, simply by defining the polygon() accordingly.
By adding corner-rounding to the polygon()
function, instead of as a new property, it can be used in all properties that use basic shape functions.
You're not overloading border-radius
, so it can be adjusted for better fallback.
Whenever a path-like shape syntax is chosen for combining curved path segments with responsive units, then you could also do scooped corners, more elegant speech bubbles, thought clouds, and more. (In the meantime, you could do absolutely-sized path shapes. Which might be useful for fancy icon buttons? I don't know. Non-responsive path()
isn't very useful in CSS currently.)
Downside:
@AmeliaBR
This seems like a lot of added complexity for a fairly niche style.
I disagree that it is niche. Perhaps actual beveled corners are niche, but the kinds of polygons one can create with this are all over the web. https://github.com/w3c/csswg-drafts/issues/6980 has several use cases.
So, my suggestion would be ...
It looks like this is basically my earlier element-shape
proposal. The problem with all of these is that it is very difficult to define all the border edge cases, border-radius
has been a huge pain for this reason and nobody wants to do this work for arbitrary shapes. This is exactly why there is no implementor interest for corner-shape
, and your proposal makes the problem even harder, not easier. Furthermore, this doesn't really cover the extremely common case of a polygon with rounded angles, unless you are willing to spell the path out with (pretty lengthy, and inflexible) SVG, at which point you may as well have been using an image. And, as you point out, even for simple shapes, the syntax can get complicated fast.
it is very difficult to define all the border edge cases, border-radius has been a huge pain for this reason
Only because the spec encourages elegant transitions in style and width as the border goes around the corner! If we accept that fancy borders with different width/colour/styles on each side are edge cases & it's OK to have hard stop changes, then my suggestion of clipping the stroke shape to the box diagonals is easy enough to implement.
So, my suggestion would be ...
It looks like this is basically my earlier
element-shape
proposal.
For which I created a separate issue because it goes way beyond corner shapes.
If we accept that fancy borders with different width/colour/styles on each side are edge cases & it's OK to have hard stop changes, then my suggestion of clipping the stroke shape to the box diagonals is easy enough to implement.
@AmeliaBR mask-border
already goes into that direction, though it just clips the border, it doesn't have any influence on the stroke shape.
@LeaVerou Are the transitions between borders the reason implementers are reluctant to implement corner shapes? Or is it the missing specification regarding those transitions in Level 4? Or is it something else?
I assume the implementation of bevel shapes in combination with different widths, colors and styles is much easier than for rounded corners. And those are implemented more or less interoperably for so many years now. But it would really be good to get some implementer feedback on the reasons.
Regarding the two suggested approaches, I think Lea's idea is easier for authors to use in general.
Though I wouldn't actually throw away the idea of the general corner-shape
property. Instead of introducing a new property that only covers bevels, we could add a function for that to corner-shape
. The big advantage is that we'd only have one new property which can be extended to cover other use cases in the future. And it still plays well with border-radius
.
Picking up the examples from above, this could look like:
corner-shape: bevel(50%); /* diamond */
corner-shape: edge edge bevel(100%); /* triangle top left */
corner-shape: bevel(50%) bevel(50%) 0 0 / bevel(100%);
Though those examples are rather edge cases, anyway. More common cases would be something like corner-shape: bevel(10px)
.
So, how would we map
border-radius
's four corners to these eight? We could map it to pairs (e.g.border-radius
for top left corner rounds both 1 and 2 above), but that's pretty weird. But the primary problem is pointless complexity and wasted implementation effort to make any possibleborder-radius
value work with these polygons, when the vast majority of use cases just needs a single radius. Personally, I have not seen single a single use case that requires more than that.
I'd rather stay with border-radius
for that and I don't think it is that weird to let it apply to two actual corners on the shape.
The implementation complexity can be reduced by restricting the values of border-radius
that apply when corner-shape
is something else than edge
(the default).
E.g. it could be specified that only values without vertical radii (i.e. without the values after the slash) are applied.
Sebastian
@LeaVerou Are the transitions between borders the reason implementers are reluctant to implement corner shapes? Or is it the missing specification regarding those transitions in Level 4? Or is it something else?
You would need to ask implementers :)
And it still plays well with
border-radius
.
Yeah, I'm not convinced that's an advantage :)
E.g. it could be specified that only values without vertical radii (i.e. without the values after the slash) are applied.
Note that you can get elliptical radii with a single border-radius
value, if it's a percentage.
So in general, I agree with adding an additional property to work with border-radius, and it taking some form of angle hinting, but I'm going to throw out a crazy idea here:
Using cubic bezier function to draw the radius, with corner-angle
taking angle values for the "handles", and border-radius
redefined as the scalars.
Default value would be 0deg, and straight
can be used to sit the angle on a straight line. This should cover for both scoop and bevel; notch can't be, but that should be covered with other tricks, and is niche, as you say.
Downside is that you can't perfectly represent a circle with cubic-bezier.
@SYwaves using cubic-bezier()
for the corners has been discussed many, many times. Nobody doubts it would be useful, but it introduces even more complexity than the current corner-shape
property, and we are trying to reduce complexity so that it has chances of getting implemented, not increase it.
It would be good to come up with a syntax that allows for such extensions in the future but it's not an option right now. We don't want something that will sit in the spec unimplemented for another 10 years.
And it still plays well with border-radius.
Yeah, I'm not convinced that's an advantage :)
E.g. it could be specified that only values without vertical radii (i.e. without the values after the slash) are applied.
Note that you can get elliptical radii with a single
border-radius
value, if it's a percentage.
Right, elliptical radii probably aren't expected in combination with bevels or other corner shapes.
So giving this a little more thought, I now tend to agree with you that it might be better to introduce a separate property for that. It's also better in regard of implementations because it could be handled separately.
@LeaVerou Are the transitions between borders the reason implementers are reluctant to implement corner shapes? Or is it the missing specification regarding those transitions in Level 4? Or is it something else?
You would need to ask implementers :)
@tabatkins, @smfr, @emilio What needs to happen to get some attention for this feature?
Sebastian
I'm not the expert on the graphics-side of border-radius
, but ftr I don't think it's a trivial feature at all (it infests a lot of the background / shadow / border / clipping / hit-testing / etc code). In that sense, a clip-path-like thing seems a lot easier to implement (you don't care about semi-transparent borders, stuff overlapping etc etc).
I think at least from the CSS / layout side the current corner-shape: {round,angle}
thing seems fine. In an ideal world, thinking more as an author, something that supersedes border-radius seems a bit better. Maybe we could make border*-radius
shorthands for corner-*-shape: radii(<length-percentage>) ...
or so? But maybe not worth the complexity and the current thing seems like it'd be less effort to implement (compared to adding a new per-corner property plus all the relevant shorthands).
Anyways this doesn't look too hard to implement, specially if it's built on top of border-radius as it is on the current backgrounds-4 spec. But it doesn't seem to be in the "trivial-ish" camp either. I'd have to defer to @lsalzman / @mstange / @jrmuizel / @glennw / @kdashg for actual graphics-knowledge. My guess is that this is probably not technically hard to do, just some / a bunch of work to make fast.
@emilio Thank you for the detailed feedback! Just to be clear, as I understand you, you'd prefer the currently specified corner-shape
over the corner-angle
proposal made in this issue (disregarding the angle-radius
which is rather orthogonal)?
Sebastian
@LeaVerou I see. If that's the case, is it not easier to extend another property rather than working around current implementation of border-radius
? Like adding a property to draw a stroke on clip-path
, or expanding round
to be used with polygon() instead of just inset rectangles.
I suppose that's just shifting the issue elsewhere, but I think the need to draw arbitrary shapes isn't exactly isolated to just the bounding box and border-radius
.
Opened "standards positions" github issues for 'corner-shape' with Mozilla and Webkit just to try and get this on folks radar. Mozilla has a response, nothing yet from WebKit:
Mozilla: https://github.com/mozilla/standards-positions/issues/823 WebKit: https://github.com/WebKit/standards-positions/issues/229
I'm playing with corner-shape
in WebKit now, mostly as a vehicle for supporting squircles somehow. But having implemented bevel
, I do have some thoughts:
round
and bevel
? That animation could just adjust the control point locations, which then implies that maybe allowing author control of the control points does make sense, via a functional syntax.corner-top-right-shape
etc?scoop
is always renderable, since the scoops from different corners might intersecthttps://github.com/w3c/csswg-drafts/issues/6980#issuecomment-1023357465 has a mixed-corner-treatment example.
the only animated example I've run into so far is between square/rect and angle: https://github.com/w3c/csswg-drafts/issues/6980#issuecomment-1020198847
The OP of the use case thread has an index with a "mixed shapes" category. There are 3 listed right now: https://github.com/w3c/csswg-drafts/issues/6980#issue-1112004771 . Now that I know someone is looking at it I'll work on getting the index caught up to the last year or so of examples.
on the potential intersecting scoops: Isn't this true of any shape? Even round? Aren't there clamping rules for border-radius
that deal with this?
on the potential intersecting scoops: Isn't this true of any shape? Even round? Aren't there clamping rules for border-radius that deal with this?
No, the "indent" type corners (scoop
, notch
) need additional constraints to avoid overlaps.
In particular, the diagonally opposite corners might intersect. The existing radius-clamping rules do indeed prevent adjacent corners from intersecting.
See, for example:
.box {
width: 100px; height: 100px;
border-radius: 100px 0 100px 0;
border: thin solid;
corner-shape: scoop;
}
angle
corner shape is the maximal cut you can do without running into issues; anything that cuts out more needs additional shrinking rules.
The lower shape here shows intersecting scoop
corners (before implementing any additional constraints):
At this point, it's very clear that there is little implementor interest for
corner-shape
as it's currently specified. It has been in the spec for over a decade, so this is unlikely to change.However, looking at the
corner-shape
use cases in #6980, I am observing these things:a) The vast majority just need
bevel
(angled) corners.scoop
andnotch
are rather rare and niche enough that I think are fine being delegated to CSS shapes or border-image. b) Quite frequently, these angled corners also require rounding, which the current syntax does not actually do.Therefore, the current design of
corner-shape
which treatsborder-radius
as a fallback with the same radius, won't work (and there has been feedback that this is a worse fallback than nothing at all for most use cases).Angled corners
Instead, I propose a
corner-angle
property (name TBB) which only does angled corners. It takes the same syntax asborder-radius
, with keywords for common cases, such as:diamond
=50% 50% 50% 50%
triangle top left
=0 0 100%
(+ keywords for all four corners)triangle top
=50% 50% 0 0 / 100%
(+ keywords for all four sides)Brainstorming for property name:
- `corner-angle` - `corner-cutout` - `cutout-size` - `bevel-radius` - `corner-size` - `angled-corners` - `cutout-corner-size`Angle rounding
It seems that rounding is essential to satisfy the use cases in the real world, otherwise authors will still resort to images.
border-radius
could work together with the new property to add rounding to the angles generated. This has the advantage of re-using an existing property, but it has some pretty major disadvantages:First,
border-radius
is designed to specify rounding for 1-4 corners, whereas this syntax can create up to 8 angles:So, how would we map
border-radius
's four corners to these eight? We could map it to pairs (e.g.border-radius
for top left corner rounds both 1 and 2 above), but that's pretty weird. But the primary problem is pointless complexity and wasted implementation effort to make any possibleborder-radius
value work with these polygons, when the vast majority of use cases just needs a single radius. Personally, I have not seen single a single use case that requires more than that.So perhaps we need a separate property (
angle-radius
?) that just takes a single<length>
. Though it is a bit weird that this basically becomes a subset ofborder-radius
whencorner-angle
isinitial
.Other considerations
Whatever syntax we go with, we should make sure it allows combining regular
border-radius
with this, e.g. it should allow having two angled corners (with or without rounding) plus two rounded corners (with the full power ofborder-radius
).cc @fantasai @SebastianZ