Over the previous few years, there’s been a whole lot of discuss and experimentation with scroll-driven animations. It’s a really shiny function for positive, and as quickly because it’s supported in Firefox (with no flag), it’ll be baseline. It’s a part of Interop 2026, in order that needs to be comparatively quickly. Primarily, scroll-driven animations tie an animation timeline’s place to a scroll place, so if you happen to have been 50% scrolled you then’d even be 50% into the animation, they usually’re surprisingly simple to arrange too.
I’ve been seeing vital curiosity within the new CSS corner-shape property as effectively, although it solely works in Chrome for now. This permits us to create corners that aren’t as rounded, or aren’t even rounded in any respect, permitting for some intriguing shapes that take little-to-no effort to create. What’s much more intriguing although is that corner-shape is mathematical, so it’s simply animated.
Therefore, say hi there to scroll-driven corner-shape animations (requires Chrome 139+ to work totally):
corner-shape in a nutshell
Actual fast — the totally different values for corner-shape:
corner-shape key phrase |
superellipse() equal |
|---|---|
sq. |
superellipse(infinity) |
squircle |
superellipse(2) |
spherical |
superellipse(1) |
bevel |
superellipse(0) |
scoop |
superellipse(-1) |
notch |
superellipse(-infinity) |
However what’s this superellipse() operate all about? Effectively, mainly, these key phrase values are the results of this operate. For instance, superellipse(2) creates corners that aren’t fairly squared however aren’t fairly rounded both (the “squircle”). Whether or not you utilize a key phrase or the superellipse() operate instantly, a mathematical equation is used both manner, which is what makes it animatable. With that in thoughts, let’s dive into that demo above.
Animating corner-shape
The demo isn’t too sophisticated, so I’ll begin off by dropping the CSS right here, after which I’ll clarify the way it works line-by-line:
@keyframes bend-it-like-beckham {
from {
corner-shape: superellipse(notch);
/* or */
corner-shape: superellipse(-infinity);
}
to {
corner-shape: superellipse(sq.);
/* or */
corner-shape: superellipse(infinity);
}
}
physique::earlier than {
/* Fill viewport */
content material: "";
place: fastened;
inset: 0;
/* Allow click-through */
pointer-events: none;
/* Invert underlying layer */
mix-blend-mode: distinction;
background: white;
/* Don’t overlook this! */
border-bottom-left-radius: 100%;
/* Animation settings */
animation: bend-it-like-beckham;
animation-timeline: scroll();
}
/* Added to playing cards */
.no-filter {
isolation: isolate;
}
Within the code snippet above, physique::earlier than mixed with content material: "" creates a pseudo-element of the with no content material that’s then fastened to each fringe of the viewport. Additionally, since this animating form will likely be on prime of the content material, pointer-events: none ensures that we will nonetheless work together with mentioned content material.
For the form’s shade I’m utilizing mix-blend-mode: distinction with background: white, which inverts the underlying layer, a classy impact that to a point solely maintains the identical degree of shade distinction. You gained’t wish to apply this impact to every thing, so right here’s a utility class to exclude the impact as wanted:
/* Added to playing cards */
.no-filter {
isolation: isolate;
}
A comparability:

You’ll want to mix corner-shape with border-radius, which makes use of corner-shape: spherical beneath the hood by default. Sure, that’s proper, border-radius doesn’t really spherical corners — corner-shape: spherical does that beneath the hood. Reasonably, border-radius handles the x-axis and y-axis coordinates to attract from:
/* Syntax */
border-bottom-left-radius: ;
/* Utilization */
border-bottom-left-radius: 50% 50%;
/* Or */
border-bottom-left-radius: 50%;

In our case, we’re utilizing border-bottom-left-radius: 100% to slip these coordinates to the other finish of their respective axes. Nonetheless, we’ll be overwriting the implied corner-shape: spherical in our @keyframe animation, so we consult with that with animation: bend-it-like-beckham. There’s no have to specify a period as a result of it’s a scroll-driven animation, as outlined by animation-timeline: scroll().
Within the @keyframe animation, we’re animating from corner-shape: superellipse(notch), which is like an inset sq.. That is equal to corner-shape: superellipse(-infinity), so it’s not really squared however it’s so aggressively sharp that it seems squared. This animates to corner-shape: superellipse(sq.) (an outset sq.), or corner-shape: superellipse(infinity).
Animating corner-shape… revisited
The demo above is definitely a bit totally different to the one which I initially shared within the intro. It has one minor flaw, and I’ll present you tips on how to repair it, however extra importantly, you’ll be taught extra about an intricate element of corner-shape.
The flaw: at the start and finish of the animation, the curvature seems fairly harsh as a result of we’re animating from notch and sq., proper? It additionally seems like the form is being sucked into the corners. Lastly, the form being caught to the perimeters of the viewport makes the entire thing really feel too contained.
The answer is straightforward:
/* Change this... */
inset: 0;
/* ...to this */
inset: -1rem;
This stretches the form past the viewport, and although this makes the animation seem to start out late and end early, we will repair that by not animating from/to -infinity/infinity:
@keyframes bend-it-like-beckham {
from {
corner-shape: superellipse(-6);
}
to {
corner-shape: superellipse(6);
}
}
Positive, because of this a part of the form is all the time seen, however we will fiddle with the superellipse() worth to make sure that it stays exterior of the viewport. Right here’s a side-by-side comparability:

And the unique demo (which is the place we’re at now):
Including extra scroll options
Scroll-driven animations work very effectively with different scroll options, together with scroll snapping, scroll buttons, scroll markers, easy textual content fragments, and easy JavaScript strategies akin to scrollTo()/scroll(), scrollBy(), and scrollIntoView().
For instance, we solely have so as to add the next CSS snippet to introduce scroll snapping that works proper alongside the scroll-driven corner-shape animation that we’ve already arrange:
:root {
/* Snap vertically */
scroll-snap-type: y;
part {
/* Snap to part begin */
scroll-snap-align: begin;
}
}
“Masking” with corner-shape
Within the instance beneath, I’ve basically created a border across the viewport after which a notched form (corner-shape: notch) on prime of it that’s the identical shade because the background (background: inherit). This form utterly covers the border at first, however then animates to disclose it (or on this case, the 4 corners of it):
If I make the form a bit extra seen, it’s simpler to see what’s taking place right here, which is that I’m rotating this form as effectively (rotate: 5deg), making the form much more attention-grabbing.

This time round we’re animating border-radius, not corner-shape. After we animate to border-radius: 20vw / 20vh, 20vw and 20vh refers back to the x-axis and y-axis of every nook, respectively, that means that 20% of the border is revealed as we scroll.
The one different factor price mentioning right here is that we have to fiddle with z-index to make sure that the content material is larger up within the stacking context than the border and form. Aside from that, this instance merely demonstrates one other enjoyable manner to make use of corner-shape:
@keyframes tech-corners {
from {
border-radius: 0;
}
to {
border-radius: 20vw / 20vh;
}
}
/* Border */
physique::earlier than {
/* Fill (- 1rem) */
content material: "";
place: fastened;
inset: 1rem;
border: 1rem strong black;
}
/* Notch */
physique::after {
/* Fill (+ 3rem) */
content material: "";
place: fastened;
inset: -3rem;
/* Rotated form */
background: inherit;
rotate: 5deg;
corner-shape: notch;
/* Animation settings */
animation: tech-corners;
animation-timeline: scroll();
}
foremost {
/* Stacking repair */
place: relative;
z-index: 1;
}
Animating a number of corner-shape components
On this instance, we’ve a number of nested diamond shapes because of corner-shape: bevel, all leveraging the identical scroll-driven animation the place the diamonds improve in dimension, utilizing padding:
@keyframes diamonds-are-forever {
from {
padding: 7rem;
}
to {
padding: 14rem;
}
}
#diamonds {
/* Heart them */
place: fastened;
inset: 50% auto auto 50%;
translate: -50% -50%;
/* #diamonds, the s inside */
&, div {
corner-shape: bevel;
border-radius: 100%;
animation: diamonds-are-forever;
animation-timeline: scroll();
border: 0.0625rem strong #00000030;
}
}
foremost {
/* Stacking repair */
place: relative;
z-index: 1;
}
That’s a wrap
We simply explored animating from one customized superellipse() worth to a different, utilizing corner-shape as a masks to create new shapes (once more, whereas animating it), and animating a number of corner-shape components without delay. There are such a lot of methods to animate corner-shape apart from from one key phrase to a different, and if we make them scroll-driven animations, we will create some actually attention-grabbing results (though, they’d additionally look superior in the event that they have been static).
