Squircles, Bevels and Scoops with corner-shape
Introduction
For as long as CSS has had border-radius, a corner could be exactly one shape: a quarter of an ellipse. Anything
else — the smooth "squircle" of an app icon, a bevelled sale tag, the scooped edge of a ticket stub — meant reaching
for SVG, a clip-path polygon, or a stack of pseudo-elements. Each of those has a catch: clip-path cuts off your
borders and shadows, and SVG has to be redrawn whenever the box changes size.
corner-shape fixes this at the source. border-radius keeps deciding how big each corner is;
corner-shape decides what shape fills that space. Because it changes the box itself, the border, background,
box-shadow and outline all follow the new shape automatically.
Basic Usage
.app-icon {
border-radius: 30%;
corner-shape: squircle;
}Two things to notice:
border-radiusis still required. It sets the size of each corner. With a radius of0there's no corner to shape, socorner-shapehas nothing to do.- Everything follows the shape. Unlike
clip-path, which only crops what's already drawn, the border, the background, anybox-shadow(including its spread and offset) and theoutlineare all drawn along the new corners. That includes a gradient border built withbackground-clip, which takes on the shape for free.
The Six Keywords
| Keyword | What the corner looks like |
|---|---|
round | The ordinary elliptical curve — the initial value |
squircle | A smoother, "continuous" curve, like an app icon |
square | No curve at all: a sharp right angle, whatever the radius |
bevel | A straight diagonal cut across the corner |
scoop | A concave curve that bites into the box, like a ticket stub |
notch | A square step cut into the corner |
Every keyword is a named point on one scale, the superellipse() function, which you can also use directly.
Fine Control with superellipse()
.card {
border-radius: 2rem;
corner-shape: superellipse(1.5);
}superellipse() takes one number that sets how far the corner bulges outward (positive) or bites inward
(negative). The keywords map onto it like this:
notchissuperellipse(-infinity)scoopissuperellipse(-1)bevelissuperellipse(0), a straight lineroundissuperellipse(1)squircleissuperellipse(2)squareissuperellipse(infinity)
So superellipse(1.5) sits between an ordinary curve and a squircle, and superellipse(-0.5) gives a gentler scoop
than the keyword does.
Different Shapes per Corner
.speech-bubble {
border-radius: 1.25rem;
/* top-left, top-right, bottom-right, bottom-left */
corner-shape: round round square round;
}Like border-radius, corner-shape takes one to four values, in the same clockwise order starting at the top-left.
There are longhands too — physical ones such as corner-top-left-shape, and logical ones such as
corner-start-start-shape that follow the writing direction, just as
logical properties do.
Combining it with an uneven border-radius opens up more shapes. A radius on the right-hand corners only, bevelled,
turns a plain button into an arrow:
.next-button {
border-radius: 0 1.25rem 1.25rem 0;
corner-shape: bevel;
}It Animates
corner-shape interpolates between values, so a transition morphs the corners smoothly rather than snapping:
.card {
border-radius: 2rem;
corner-shape: scoop;
transition: corner-shape 0.6s ease;
}
.card:hover {
corner-shape: squircle;
}The browser animates the superellipse() value underneath, so even keyword-to-keyword transitions pass through
every shape in between. Duration, easing and delay work exactly as they do for any other property — the
CSS transitions guide covers them in depth. As with any decorative motion,
wrap the transition in a prefers-reduced-motion: no-preference query (or turn it off under reduce) for readers
who've asked for less of it.
Try It Live
The Explorer tab shapes one card that has a border, a shadow and an outline, so you can see every layer follow
the corners. Pick a keyword, choose superellipse() to drag the value from -4 to 4, change the
border-radius, and turn on Animate on hover to watch it morph. The panel underneath shows the CSS for exactly
what you see.
The Gallery tab shows each keyword doing a real job — app icons, tags, ticket stubs, panels, speech bubbles and
directional buttons — every one a single element with no SVG or clip-path.
corner-shape Playground
Shape one bordered, shadowed card with every corner-shape keyword or a superellipse() value, change its border-radius and morph it on hover, then browse squircles, bevels, scoops and notches doing real UI jobs.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>corner-shape Playground</title> </head> <body> <main class="playground"> <header class="playground-header"> <h1>Shaping Corners with corner-shape</h1> <p><code>border-radius</code> sets how big each corner is; <code>corner-shape</code> sets what shape fills that space. The border, background, shadow and outline all follow it.</p> </header> <p class="support-note" id="support-note" hidden>This browser doesn't support <code>corner-shape</code> yet, so every corner falls back to an ordinary rounded <code>border-radius</code>.</p> <div class="tabs" role="tablist" aria-label="Demo" id="demo-switch"> <button class="tab tab--active" type="button" data-mode="explorer" role="tab" aria-selected="true">Explorer</button> <button class="tab" type="button" data-mode="gallery" role="tab" aria-selected="false">Gallery</button> </div> <div id="explorer-demo" class="tab-panel tab-panel--active"> <section class="demo-area" aria-label="Shape preview"> <div class="stage"> <div class="shape-card" id="shape-card"> <strong>Card</strong> <span>Hover me when "Animate on hover" is on</span> </div> </div> </section> <fieldset class="control-group"> <legend>corner-shape</legend> <div class="segmented-control" role="radiogroup" aria-label="Corner shape"> <label class="segment"><input type="radio" name="shape" value="round" checked><span>round</span></label> <label class="segment"><input type="radio" name="shape" value="squircle"><span>squircle</span></label> <label class="segment"><input type="radio" name="shape" value="square"><span>square</span></label> <label class="segment"><input type="radio" name="shape" value="bevel"><span>bevel</span></label> <label class="segment"><input type="radio" name="shape" value="scoop"><span>scoop</span></label> <label class="segment"><input type="radio" name="shape" value="notch"><span>notch</span></label> <label class="segment"><input type="radio" name="shape" value="superellipse"><span>superellipse()</span></label> </div> <div class="fields-grid"> <div class="field" id="curvature-field"> <label for="curvature-input">superellipse() value</label> <div class="range-row"> <span class="range-edge">-4</span> <div class="range-track"> <input type="range" id="curvature-input" min="-4" max="4" step="0.1" value="1.5"> <output class="range-bubble" id="curvature-value" for="curvature-input">1.5</output> </div> <span class="range-edge">4</span> </div> </div> <div class="field"> <label for="radius-input">border-radius</label> <div class="range-row"> <span class="range-edge">0px</span> <div class="range-track"> <input type="range" id="radius-input" min="0" max="60" step="1" value="36"> <output class="range-bubble" id="radius-value" for="radius-input">36px</output> </div> <span class="range-edge">60px</span> </div> </div> </div> <label class="checkbox-field"> <input type="checkbox" id="animate-toggle"> Animate on hover (from <code>scoop</code> to the chosen shape) </label> </fieldset> <output class="readout" id="readout" aria-live="polite"></output> </div> <div id="gallery-demo" class="tab-panel"> <section class="demo-area" aria-label="corner-shape keywords as real UI"> <div class="gallery"> <figure> <div class="app-icon" aria-hidden="true">Aa</div> <figcaption><code>squircle</code><br>App icons</figcaption> </figure> <figure> <div class="tag-label">Sale</div> <figcaption><code>bevel</code><br>Tags and labels</figcaption> </figure> <figure> <div class="ticket">Admit one</div> <figcaption><code>scoop</code><br>Ticket stubs</figcaption> </figure> <figure> <div class="notched-panel">Panel</div> <figcaption><code>notch</code><br>Sci-fi panels</figcaption> </figure> <figure> <div class="speech-bubble">Hello!</div> <figcaption><code>round round square round</code><br>Speech bubbles</figcaption> </figure> <figure> <div class="cut-button">Next</div> <figcaption><code>bevel</code>, right corners only<br>Directional buttons</figcaption> </figure> </div> </section> <p class="tip">Every shape here is one element with a <code>border-radius</code> and a <code>corner-shape</code> — no SVG, no <code>clip-path</code>, no pseudo-elements.</p> </div> </main> <script src="./index.js"></script> </body> </html>
Starting sandbox…
No console output yet.
Browser Support and Fallbacks
corner-shape shipped in Chromium browsers (Chrome and Edge 139) in 2025. Other browsers don't support it yet at the
time of writing — and that's fine, because the fallback is automatic. A browser that doesn't understand
corner-shape ignores it and draws the ordinary rounded corners from border-radius, so the design degrades to
something perfectly usable.
When a shape needs a different radius to look right, adjust it only where the shape will actually render:
.ticket {
border-radius: 0.5rem;
}
@supports (corner-shape: scoop) {
.ticket {
border-radius: 1rem;
corner-shape: scoop;
}
}Common Pitfalls
1. Forgetting the Radius
corner-shape on its own does nothing — the corner has no size until border-radius gives it one. If a shape
doesn't appear, check the radius first.
2. Content Running into Cut-Outs
corner-shape reshapes the box, not its content. Concave shapes like scoop and notch cut into the padding area,
so text placed near a corner can overlap the cut-out. Give those elements enough padding to keep content clear.
3. Large Radii with Concave Shapes
The bigger the radius, the deeper a scoop or notch bites. On a small element, a large radius can cut away most
of the box — keep concave radii modest relative to the element's size.
Key Takeaway
border-radius sets the size of a corner; corner-shape sets its shape. Together they replace most of the SVG
masks, clip-path polygons and pseudo-element tricks once needed for squircles, bevels, scoops and notches — and
unlike those, the border, shadow and outline follow along, it animates, and browsers without support simply fall
back to rounded corners.
Changelog
- — Initial publication.
Share this tutorial
Spotted a mistake or something outdated in this tutorial? Suggest a correction.