shape(): A New Powerful Drawing Syntax in CSS

Chris Coyier Chris Coyier on

I first saw in the Safari 18.4 release notes that shape(), a new function is now supported. Then I saw on MDN it’s actually already in Chrome, too!

The shape() function joins friends like polygon(), circle(), rect(), inset(), and a handful of others. These functions are used as values for a handful of things in CSS, namely:

  • clip-path — Clipping away parts of elements
  • offset-path — Moving elements along a path
  • shape-outside — Applied to a float-ed element such that content flows along the path

Fair warning: shape() only seems to work with clip-path. I couldn’t find a ton of information on this, but the Chrome blog does state it. It will probably work with the other properties in due time.

Let’s focus on clip-path here which I might argue is the most useful anyway, as it makes an entire element into the shape described which feels like a more generally applicable thing.

I got into this on the CodePen blog where I equated shape() to <path d=""> in SVG, which is surely the intention. You can actually set the d attribute in CSS, but it only works on <path> elements, and the unitless values translate only to pixels, which makes it not particularly CSSy or useful.

One situation I mentioned was Trys Mudford’s blog post where this was the design situation at hand:

Oh look, a use case.

Those light yellow boxes are basically polygons with rounded corners. In a perfect world, polygon() could do this with the round keyword, as specced, but alas that doesn’t work just yet. But because shape() is essentially all-powerful, that does work now (in Chrome and Safari anyway, and this feels like a decently progressive-enhancement thing).

Temani Afif saw that and did the work!

This is very awesome. This is quite the power tool for shape-making. I think we’re going to see a lot of fancy stuff come out of this.

It’s true we already have a path() function, but remember, it’s sooooo limited. The values are only pixels, which are some pretty big handcuffs in a responsive world full of intrinsic content (that is, elements on the web that respond to their contents and environment). Simon Fraser on the WebKit blog introduces this new feature and calls it out:

… using path() in clip-path can’t be responsive; you can’t write CSS rules so that the path adapts to the size of the element. This is where the new shape() function comes in.

Coincidentally, Simon’s demo (Jen’s demo?) also shows off an arrow shape:

That’s using multiple different drawing commands (line and arc, but there are more), keywords like top and left (excellent, but I wonder why logical properties don’t work?), and, even more deliciously, container units (e.g. cqh). The orange border there is a good reminder that clip-path, well, clips. So it’ll lop off anything at all on this element in those areas, including content.

Noam Rosenthal got in on the fun over on the Chrome for Developers blog, underscoring just how hard this stuff used to be:

clip-path: shape() lets you clip your element using arbitrary and responsive shapes, previously only possible using techniques like conic gradients or JavaScript-constructed SVG.

And like all this good company, absolutely couldn’t resist peppering in other CSS goodness into a demo. His demo here uses different drawing commands than we’ve seen so far, custom properties (which are an extremely natural fit), and even animation (!!).

I see Temani is hard on the case with a blob generator using shape(), which, I believe as long as there are the “same number of points”, can be animated by changing the clip-path entirely. Like:

And obviously I love this:

The Actual Shape Commands

The spec covers them, but the best writeup I’ve seen is Geoff’s on CSS-Tricks. He’s got a bit more detail so check that out, but here’s the list:

  • line
  • vline
  • hline
  • arc
  • curve
  • smooth

Each of them have a bit of sub-syntax to themselves. Like the curve command might look like curve to 50% 50% with 50% 0 which would continue drawing the shape to the exact center of the element in a curve in which the top center is a “control point” and so curves in that direction.

In my experience it’s quite easy to make a small mistake in the syntax and wreck the whole thing. But hey that’s understandable.

Squircles with shape()

I get to have some fun too! It occurred to me that digital designs most elusive beast, the squircle, might be now achievable with reasonable normal web tech.

SVG can do it, but I wouldn’t call it particularly readable code. “Monoco is a tiny JavaScript library that adds squircles” (via SVG background images) and it does a pretty good job of it I’d say, but that’s more technology than I normally like to throw at something like this. Jared White by way of Simeon Griggs has a pretty nice SVG-based solution as well, leveraging SVG-as-clip-path.

I like how relatively chill that SVG path is, but still, shape() can allow us to squish this down into just CSS which is kinda sweet.

That is… if I was fully smart enough to do it.

I crudely drew one in Figma so that I could label the points for writing the syntax out.

I figured if I just did a curve to every one of those points with control points a bit the edges, it would… work? So basically like this:

div {
  clip-path: shape(
    from 5% 3%,
    curve to 95% 3% with 50% 0,
    curve to 97% 5% with 97% 3%,
    curve to 97% 95% with 100% 50%,
    curve to 95% 97% with 97% 97%,
    curve to 5% 97% with 50% 100%,
    curve to 3% 95% with 3% 97%,
    curve to 3% 5% with 0% 50%,
    curve to 5% 3% with 3% 3%,
  );
}Code language: CSS (css)

Which basically works. I tried playing around with arc and smooth instead but couldn’t manage to make it any better (with my like zero geometry skills). Then instead of hard coding those percentage values, I made them in custom properties with sliders to squiggle them around a little.

It’s a little janky — but I trust someone make like a real quality geometrically sound version eventually.

Need front-end development training?

Frontend Masters logo

Frontend Masters is the best place to grow in your career as a developer. We have courses on all the most important front-end technologies and beyond, from React to CSS, to backend with Node.js and Full Stack.

7-Day Free Trial

3 responses to “shape(): A New Powerful Drawing Syntax in CSS”

  1. Noam Rosenthal says:

    Thanks for this post!
    It’s definitely possible to create pseudo-squircles with shape(), however the corner-shape property we’re working on would enable those also in borders and shadows and not just as a clip-path.

  2. Noam Rosenthal says:

    (… also, offset-path: shape() works in Safari, and will work in Chrome starting version 137).

Leave a Reply to Noam Rosenthal Cancel reply

Your email address will not be published. Required fields are marked *

Did you know?

Frontend Masters Donates to open source projects. $363,806 contributed to date.