The CSS shape() function recently gained support in both Chromium and WebKit browsers. It’s a way of drawing complex shapes when clipping elements with the clip-path property. We’ve had the ability to draw basic shapes for years — think circle, ellipse(), and polygon() — but no “easy” way to draw more complex shapes.
Well, that’s not entirely true. It’s true there was no “easy” way to draw shapes, but we’ve had the path() function for some time, which we can use to draw shapes using SVG commands directly in the function’s arguments. This is an example of an SVG path pulled straight from WebKit’s blog post linked above:
<svg viewBox="0 0 150 100" xmlns="http://www.w3.org/2000/svg">
<path fill="black" d="M0 0 L 100 0 L 150 50 L 100 100 L 0 100 Q 50 50 0 0 z " />
</svg>
Which means we can yank those <path> coordinates and drop them into the path() function in CSS when clipping a shape out of an element:
.clipped {
clip-path: path("M0 0 L 100 0 L 150 50 L 100 100 L 0 100 Q 50 50 0 0 z");
}
I totally understand what all of those letters and numbers are doing. Just kidding, I’d have to read up on that somewhere, like Myriam Frisano’s more recent “Useful Recipes For Writing Vectors By Hand” article. There’s a steep learning curve to all that, and not everyone — including me — is going down that nerdy, albeit interesting, road. Writing SVG by hand is a niche specialty, not something you’d expect the average front-ender to know. I doubt I’m alone in saying I’d rather draw those vectors in something like Figma first, export the SVG code, and copy-paste the resulting paths where I need them.
The shape() function is designed to be more, let’s say, CSS-y. We get new commands that tell the browser where to draw lines, arcs, and curves, just like path(), but we get to use plain English and native CSS units rather than unreadable letters and coordinates. That opens us up to even using CSS calc()-ulations in our drawings!
Here’s a fairly simple drawing I made from a couple of elements. You’ll want to view the demo in either Chrome 135+ or Safari 18.4+ to see what’s up.
CodePen Embed Fallback
So, instead of all those wonky coordinates we saw in path(), we get new terminology. This post is really me trying to wrap my head around what those new terms are and how they’re used.
In short, you start by telling shape() where the starting point should be when drawing. For example, we can say “from top left” using directional keywords to set the origin at the top-left corner of the element. We can also use CSS units to set that position, so “from 0 0” works as well. Once we establish that starting point, we get a set of commands we can use for drawing lines, arcs, and curves.
I figured a table would help.
Command
What it means
Usage
Examples
line
A line that is drawn using a coordinate pair
The by keyword sets a coordinate pair used to determine the length of the line.
line by -2px 3px
vline
Vertical line
The to keyword indicates where the line should end, based on the current starting point.
The by keyword sets a coordinate pair used to determine the length of the line.
vline to 50px
hline
Horizontal line
The to keyword indicates where the line should end, based on the current starting point.
The by keyword sets a coordinate pair used to determine the length of the line.
hline to 95%
arc
An arc (oh, really?!). An elliptical one, that is, sort of like the rounded edges of a heart shape.
The to keyword indicates where the arc should end.
The with keyword sets a pair of coordinates that tells the arc how far right and down the arc should slope.
The of keyword specifies the size of the ellipse that the arc is taken from. The first value provides the horizontal radius of the ellipse, and the second provides the vertical radius. I’m a little unclear on this one, even after playing with it.
arc to 10% 50% of 1%
curve
A curved line
The to keyword indicates where the curved line should end.
The with keyword sets “control points” that affect the shape of the curve, making it deep or shallow.
curve to 0% 100% with 50% 0%
smooth
Adds a smooth Bézier curve command to the list of path data commands
The to keyword indicates where the curve should end.
The by keyword sets a coordinate pair used to determine the length of the curve.
The with keyword specifies control points for the curve.
I have yet to see any examples of this in the wild, but let me know if you do, and I can add it here.
The spec is dense, as you might expect with a lot of moving pieces like this. Again, these are just my notes, but let me know if there’s additional nuance you think would be handy to include in the table.
Oh, another fun thing: you can adjust the shape() on hover/focus. The only thing is that I was unable to transition or animate it, at least in the current implementation.
I don’t know if I should say this on a website devoted to programming, but I sometimes feel like *lowers voice* coding is actually the least interesting part of our lives.
After all, last time I got excited meeting someone at a conference it was because we were both into bouldering, not because we both use React. And The Social Network won an Oscar for the way it displayed interpersonal drama, not for its depiction of Mark Zuckerberg’s PHP code.
Yet for the past couple years, I’ve been running developer surveys (such as the State of JS and State of CSS) that only ask about code. It was time to fix that.
A new kind of survey
The State of Devs survey is now open to participation, and unlike previous surveys it covers everything except code: career, workplace, but also health, hobbies, and more.
I’m hoping to answer questions such as:
What are developers’ favorite recent movies and video games?
What kind of physical activity do developers practice?
How much sleep are we all getting?
But also address more serious topics, including:
What do developers like about their workplace?
What factors lead to workplace discrimination?
What global issues are developers most concerned with?
Reaching out to new audiences
Another benefit from branching out into new topics is the chance to reach out to new audiences.
It’s no secret that people who don’t fit the mold of the average developer (whether because of their gender, race, age, disabilities, or a myriad of other factors) often have a harder time getting involved in the community, and this also shows up in our data.
In the past, we’ve tried various outreach strategies to help address these imbalances in survey participation, but the results haven’t always been as effective as we’d hoped.
So this time, I thought I’d try something different and have the survey itself include more questions relevant to under-represented groups, asking about workplace discrimination:
As well as actions taken in response to said discrimination:
Yet while obtaining a more representative data sample as a result of this new focus would be ideal, it isn’t the only benefit.
The most vulnerable among us are often the proverbial canaries in the coal mine, suffering first from issues or policies that will eventually affect the rest of the community as well, if left unchecked.
So, facing these issues head-on is especially valuable now, at a time when “DEI” is becoming a new taboo, and a lot of the important work that has been done to make things slightly better over the past decade is at risk of being reversed.
The big questions
Finally, the survey also tries to go beyond work and daily life to address the broader questions that keep us up at night:
There’s been talk in recent years about keeping the workplace free of politics. And why I can certainly see the appeal in that, in 2025, it feels harder than ever to achieve that ideal. At a time when people are losing rights and governments are sliding towards authoritarianism, should we still pretend that everything is fine? Especially when you factor in the fact that the tech community is now a major political player in its own right…
So while I didn’t push too far in that direction for this first edition of the survey, one of my goals for the future is to get a better grasp of where exactly developers stand in terms of ideology and worldview. Is this a good idea, or should I keep my distance from any hot-button issues? Don’t hesitate to let me know what you think, or suggest any other topic I should be asking about next time.
In the meantime, go take the survey, and help us get a better picture of who exactly we all are!
I mentioned last time that I’ve been working on a new website for Emmy-award-winning game composer Mike Worth. He hired me to create a highly graphical design that showcases his work.
Mike loves ’90s animation, particularly Disney’s Duck Tales and other animated series. He challenged me to find a way to incorporate their retro ’90s style into his design without making it a pastiche. But that wasn’t my only challenge. I also needed to achieve that ’90s feel by using up-to-the-minute code to maintain accessibility, performance, responsiveness, and semantics.
Designing for Mike was like a trip back to when mainstream website design seemed more spontaneous and less governed by conventions and best practices. Some people describe these designs as “whimsical”:
adjective
spontaneously fanciful or playful
given to whims; capricious
quaint, unusual, or fantastic
— Collins English Dictionary
But I’m not so sure that’s entirely accurate. “Playful?” Definitely. “Fanciful?” Possibly. But “fantastic?” That depends. “Whimsy” sounds superfluous, so I call it “expressive” instead.
Studying design from way back, I remembered how websites often included graphics that combined branding, content, and navigation. Pretty much every reference to web design in the ’90s — when I designed my first website — talks about Warner Brothers’ Space Jam from 1996.
Warner Brothers’ Space Jam (1996)
So, I’m not going to do that.
Brands like Nintendo used their home pages to direct people to their content while making branded visual statements. Cheestrings combined graphics with navigation, making me wonder why we don’t see designs like this today. Goosebumps typified this approach, combining cartoon illustrations with brightly colored shapes into a functional and visually rich banner, proving that being useful doesn’t mean being boring.
Left to right: Nintendo, Cheestrings, Goosebumps.
In the ’90s, when I developed graphics for websites like these, I either sliced them up and put their parts in tables or used mostly forgotten image maps.
A brief overview of properties and values
Let’s run through a quick refresher. Image maps date all the way back to HTML 3.2, where, first, server-side maps and then client-side maps defined clickable regions over an image using map and area elements. They were popular for graphics, maps, and navigation, but their use declined with the rise of CSS, SVG, and JavaScript.
<map> adds clickable areas to a bitmap or vector image.
<map name="projects">
...
</map>
That <map> is linked to an image using the usemap attribute:
<img usemap="#projects" ...>
Those elements can have separate href and alt attributes and can be enhanced with ARIA to improve accessibility:
Despite their age, image maps still offer plenty of benefits. They’re lightweight and need (almost) no JavaScript. More on that in just a minute. They’re accessible and semantic when used with alt, ARIA, and title attributes. Despite being from a different era, even modern mobile browsers support image maps.
Design by Andy Clarke, Stuff & Nonsense. Mike Worth’s website will launch in April 2025, but you can see examples from this article on CodePen.
My design for Mike Worth includes several graphic navigation elements, which made me wonder if image maps might still be an appropriate solution.
Image maps in action
Mike wants his website to showcase his past work and the projects he’d like to do. To make this aspect of his design discoverable and fun, I created a map for people to explore by pressing on areas of the map to open modals. This map contains numbered circles, and pressing one pops up its modal.
My first thought was to embed anchors into the external map SVG:
This approach is problematic. Those anchors are only active when SVG is inline and don’t work with an <img> element. But image maps work perfectly, even though specifying their coordinates can be laborious. Fortunately, plenty of tools are available, which make defining coordinates less tedious. Upload an image, choose shape types, draw the shapes, and copy the markup:
Image maps work well when images are fixed sizes, but flexible images present a problem because map coordinates are absolute, not relative to an image’s dimensions. Making image maps responsive needs a little JavaScript to recalculate those coordinates when the image changes size:
On the other hand, a map’s <area> coordinates are absolute to the top-left of an image, so <path> values need to be converted. Fortunately, Raphael Monnerat has written PathToPoints, a tool which does precisely that. Upload an SVG, choose the point frequency, copy the coordinates for each path, and add them to a map area’s coords:
Image maps are hard-coded and time-consuming to create without tools. Even with tools for generating image maps, converting paths to points, and then recalculating them using JavaScript, they could be challenging to maintain at scale.
<area> elements aren’t visible, and except for a change in the cursor, they provide no visual feedback when someone hovers over or presses a link. Plus, there’s no easy way to add animations or interaction effects.
But the deal-breaker for me was that an image map’s pixel-based values are unresponsive by default. So, what might be an alternative solution for implementing my map using CSS, HTML, and SVG?
Anchors positioned absolutely over my map wouldn’t solve the pixel-based positioning problem or give me the irregular-shaped clickable areas I wanted. Anchors within an external SVG wouldn’t work either.
But the solution was staring me in the face. I realized I needed to:
Create a new SVG path for each clickable area.
Make those paths invisible.
Wrap each path inside an anchor.
Place the anchors below other elements at the end of my SVG source.
Replace that external file with inline SVG.
I created a set of six much larger paths which define the clickable areas, each with its own fill to match its numbered circle. I placed each anchor at the end of my SVG source:
Then, I reduced those anchors’ opacity to 0 and added a short transition to their full-opacity hover state:
#links a {
opacity: 0;
transition: all .25s ease-in-out;
}
#links a:hover {
opacity: 1;
}
While using an image map’s <area> sadly provides no visual feedback, embedded anchors and their content can respond to someone’s action, hint at what’s to come, and add detail and depth to a design.
I might add gloss to those numbered circles to be consistent with the branding I’ve designed for Mike. Or, I could include images, titles, or other content to preview the pop-up modals:
Designing Mike Worth’s website gave me a chance to blend expressive design with modern development techniques, and revisiting image maps reminded me just how important a tool image maps were during the period Mike loves so much.
Ultimately, image maps weren’t the right tool for Mike’s website. But exploring them helped me understand what I really needed: a way to recapture the expressiveness and personality of ’90s website design using modern techniques that are accessible, lightweight, responsive, and semantic. That’s what design’s about: choosing the right tool for a job, even if that sometimes means looking back to move forward.
Biography: Andy Clarke
Often referred to as one of the pioneers of web design, Andy Clarke has been instrumental in pushing the boundaries of web design and is known for his creative and visually stunning designs. His work has inspired countless designers to explore the full potential of product and website design.
Andy’s written several industry-leading books, including Transcending CSS, Hardboiled Web Design, and Art Direction for the Web. He’s also worked with businesses of all sizes and industries to achieve their goals through design.
Visit Andy’s studio, Stuff & Nonsense, and check out his Contract Killer, the popular web design contract template trusted by thousands of web designers and developers.
Using scroll shadows, especially for mobile devices, is a subtle bit of UX that Chris has covered before (indeed, it’s one of his all-time favorite CSS tricks), by layering background gradients with different attachments, we can get shadows that are covered up when you’ve scrolled to the limits of the element.
Geoff covered a newer approach that uses the animation-timeline property. Using animation-timeline, we can tie CSS animation to the scroll position. His example uses pseudo-elements to render the scroll shadows, and animation-range to animate the opacity of the pseudo-elements based on scroll.
Here’s yet another way. Instead of using shadows, let’s use a CSS mask to fade out the edges of the scrollable element. This is a slightly different visual metaphor that works great for horizontally scrollable elements — places where your scrollable element doesn’t have a distinct border of its own. This approach still uses animation-timeline, but we’ll use custom properties instead of pseudo-elements. Since we’re fading, the effect also works regardless of whether we’re on a dark or light background.
Getting started with a scrollable element
First, we’ll define our scrollable element with a mask that fades out the start and end of the container. For this example, let’s consider the infamous table that can’t be responsive and has to be horizontally scrollable on mobile.
Let’s add the mask. We can use the shorthand and find the mask as a linear gradient that fades out on either end. A mask lets the table fade into the background instead of overlaying a shadow, but you could use the same technique for shadows.
Next, we need to define our custom properties and the animation. We’ll define two separate properties, --left-fade and --right-fade, using @property. Using @property is necessary here to specify the syntax of the properties so that browsers can animate the property’s values.
Instead of using multiple animations or animation-range, we can define a single animation where --left-fade animates from 0 to 3rem between 0-10%, and --right-fade animates from 3rem to 0 between 90-100%. Now we update our mask to use our custom properties and tie the scroll-timeline of our element to its own animation-timeline.
Putting it all together
Putting it all together, we have the effect we’re after:
CodePen Embed Fallback
We’re still waiting for some browsers (Safari) to support animation-timeline, but this gracefully degrades to simply not fading the element at all.
Wrapping up
I like this implementation because it combines two newer bits of CSS — animating custom properties and animation-timeline — to achieve a practical effect that’s more than just decoration. The technique can even be used with scroll-snap-based carousels or cards:
CodePen Embed Fallback
It works regardless of content or background and doesn’t require JavaScript. It exemplifies just how far CSS has come lately.