I’ve been tracking the CSS corner-shape spec for a while, and it’s finally solid enough to be worth writing about. For years, a “folded corner” effect meant wrestling with complex clip-path polygons or linear-gradient hacks that were a pain to maintain. With corner-shape, we get a cleaner, more semantic way to handle non-rounded corners.
I recently came across Kitty Giraudel’s clip-path technique, which works everywhere today. But as a dev who hates overhead, doing this natively with a single property is too good to pass up. With corner-shape, you define how a corner is “cut,” and the standard rounded border becomes something else.
The logic behind the fold
To see why this works, remember that border-radius defines two coordinates, one on the X axis and one on the Y axis. Browsers default to a “round” shape between those points. Set corner-shape: bevel and the browser draws a straight line instead. Combined with a pseudo-element, that sharp diagonal cut reads as a paper fold.
I’ve written about modern CSS features that let me delete hacks, and this fits right in. You define your coordinates, set the shape, and you’re done.
Implementing the bevel
First we set up the coordinates as CSS variables, which makes the effect easy to animate or tweak globally. Then we apply border-top-right-radius and switch the shape to bevel.
:root {
--x-coord: 9rem;
--y-coord: 5rem;
}
.folded-card {
/* Set the cut distance */
border-top-right-radius: var(--x-coord) var(--y-coord);
/* Change from default 'round' to 'bevel' */
corner-top-right-shape: bevel;
position: relative;
overflow: clip;
}
Sculpting the flip side
The “fold” itself is just a ::before pseudo-element. We inherit the background, match the dimensions to our coordinates, and add another corner-bottom-left-shape: bevel for the crease. To keep it looking realistic, I’d reach for container style queries with the range syntax, which holds the fold consistent no matter the card’s size.
.folded-card::before {
content: "";
background: inherit;
width: var(--x-coord);
height: var(--y-coord);
position: absolute;
inset: 0 0 auto auto;
/* Create the crease */
corner-bottom-left-shape: bevel;
box-shadow: 0 0 calc((var(--x-coord) + var(--y-coord)) / 3) rgba(0,0,0,0.3);
}
@container style(--x-coord < --y-coord) {
.folded-card::before {
border-bottom-left-radius: 100% calc(100% - var(--x-coord));
}
}
If you’re tired of doing these calculations by hand, my guide on using the CSS hypot function covers working these distances out dynamically.
The browser bottleneck
A quick war story: I pushed this to a staging site last month, forgetting that Firefox and Safari still treat it as experimental. In browsers without support, it falls back to a rounded corner. That’s graceful degradation, but it’s not always what the client wants. So if you need 100% parity today, stick with clip-path. If you’re building for the future, CSS corner-shape is worth the switch.
If this CSS corner-shape work is eating up your dev hours, let me take it off your plate. I’ve been wrestling with WordPress since the 4.x days.
Final takeaway
The corner-shape property shows CSS catching up to what modern design needs. It’s cleaner, easier to debug, and needs no JavaScript. For the technical details, see the official MDN reference for corner-shape.