The `-path` of Least Resistance (Part 1)
The `-path` of Least Resistance (Part 1) êŽë š
Thereâs a whole layer of CSS that lives just below the surface of most interfaces. Itâs not about layout, spacing, or typography. Itâs about shape. About cutting through the default boxes and letting your UI move in new directions. This series is all about one such family of features, the kind that doesnât just style your layout but gives you entirely new ways to shape, animate, and express your interface.
In this first part, weâll explore clip-path
. Weâll start simple, move through the functions and syntax, and work our way up to powerful shape logic that goes way beyond the basic polygons you might be used to. And just when you think things canât get any more dynamic, part two will kick in with offset-path
, where things really start to move.
Article Series
What is clip-path
in CSS?
At its core, clip-path
lets us control which parts of an element are visible. Itâs like a stencil or cookie cutter for HTML elements. Instead of displaying a rectangular box, you can show just a circle, a triangle, a star, or any complex shape you define. And you can do it with a single line of CSS.
This opens the door to more expressive designs without relying on images, SVG wrappers, or external tools. Want to crop a profile picture into a fancy blob shape? Easy. Want to reveal content through a custom cutout as a hover effect? Done. Thatâs exactly where clip-path
shines. But to use it effectively, we need to understand what itâs made of.
Before the syntax
To really get clip-path
, letâs break it into two basic concepts: clip and path. No joke, each one of those carries an important lesson of its own.
This is not the âclipâ you know
Weâve all seen clipping in CSS before, usually through the overflow
property, set to hidden
or clip
. By doing so, anything that spills out of the elementâs box just vanishes.
But hereâs the key difference. While the overflow
property clips the content of the element (on the padding box for hidden
, and on the overflow clip edge for clip
), the clip-path
property clips the element itself.
This means that even the simplest clip-path, which visually mimics overflow clipping, will still hide parts of the element itself. That includes things like a box-shadow
you were expecting to see, or an outline
on a button that suddenly disappears and breaks accessibility.
Also worth noting: just like overflow
, clip-path
lives entirely in two dimensions. No depth, no perspective. It flattens everything. That means transform-style: preserve-3d
is ignored, and any 3D motion will stay locked to the elementâs plane.
The âpathâ to success
This one trips people up. Especially when youâre working with functions like polygon()
, itâs tempting to think of the shape as just a bunch of points. But itâs not just the points that matter, itâs the order they come in. Youâre not dumping coordinates into a bucket, youâre connecting them, one by one, like a game of âconnect the dots.â

The path is the journey from one point to the next. The way you sequence them defines the outline, the curves, and eventually the clipped shape. If the points are out of order, your shape wonât behave the way you expect.
Values and Coordinates
You can set the coordinates for your shapes in absolute units like pixels, which stay fixed regardless of the elementâs size, or in relative units like percentages, which adapt based on the elementâs dimensions. Absolute values give you precision, while relative values make your shapes more responsive. In practice, youâll often mix the two to balance consistency and flexibility.
By default, every shape you define with clip-path
is calculated relative to the elementâs border-box. This means the point 0 0
sits at the top-left corner of that box, and all coordinates extend from there. Positive X values move to the right, and positive Y values move down.
Note
Note that youâre not limited to the border-box; the clip-path
property also accepts an optional <geometry-box>
value, which lets you choose the reference box for your shape, giving you more control over how the clip is applied.
Basic Shapes
Letâs begin with the simplest shape of all. The circle()
function creates a circular clipping path that allows you to cut content into a perfect circle shape. This function accepts two main parameters: the radius of the circle and its center position.
The basic syntax follows this pattern:
clip-path: circle(radius at position);
The radius can be specified in various units, like pixels (px
), percentages (%
), or viewport units (vw
, vh
). The position defines where the center of the circle should be placed, using coordinates relative to the elementâs dimensions.
This demo shows a live preview of the circle()
function in action. You can drag the control nodes to adjust both the center position and radius of the circular clip path. As you manipulate these controls, youâll see the clipped area update in real time, and the corresponding CSS values will be displayed below the preview.
Use the checkbox to toggle between pixel and percentage values to see how the result can be expressed in different units. This is particularly useful when you need responsive clipping that adapts to different screen sizes.
Using Keywords
Beyond specific coordinate values, CSS also supports several convenient keywords for positioning the circleâs center. You can use keywords like center
, top
, bottom
, left
, and right
, or combine them for more precise placement, such as top left
or bottom right
. These keywords provide a quick way to achieve common positioning without calculating exact pixel or percentage values.
You can also use special keywords for the radius: closest-side
and farthest-side
. The closest-side
keyword sets the radius to the distance from the center to the closest edge of the element, while farthest-side
extends the radius to the farthest edge.
For example:
clip-path: circle(50px at left);
clip-path: circle(30% at top right);
clip-path: circle(closest-side at top 25%);
clip-path: circle(farthest-side at center);
Slightly stretched: ellipse()
Now letâs take that circle and give it two radii instead of one. The ellipse()
function works similarly to circle()
, but instead of creating a perfect circle, it produces an oval shape by accepting two separate radius values. This gives you independent control over both the horizontal and vertical dimensions of the clipping shape.
The syntax extends the circle pattern with an additional radius parameter:
clip-path: ellipse(radiusX radiusY at position);
This demo shows the ellipse()
function with three control nodes, that allow you to independently adjust the horizontal and vertical radii. Notice how you can create anything from a wide, flat oval to a tall, narrow shape by manipulating these controls separately.
Rectangular Shapes
While circle()
and ellipse()
create curved clipping paths, CSS also provides several functions for creating rectangular clips. These functions offer different approaches to defining the same basic shape: a rectangle with straight edges.
inset()
, rect()
, and xywh()
These three are all about boxes, but each one approaches it differently.
inset()
defines distances to clip inward from each edge. Its like padding in reverse, instead of adding space inside the box, you remove it.rect()
uses absolute coordinates from the top-left corner to define the rectangleâs edges. A legacy function from the oldclip
property, but still valid and supported in CSS.xywh()
defines a rectangle by position and size. The first two values set the X and Y coordinates for the top-left corner, and the next two define the width and height. Clean and straightforward.
This demo lets you compare all three rectangular functions using the same visual controls. Drag the red control lines to adjust the clipping boundaries, and use the dropdown to switch between the different function syntaxes. Notice how the same visual result produces different coordinate values depending on which function you choose.
The inset()
function is generally the most intuitive since it works similarly to CSS padding, while rect()
follows the traditional clipping rectangle approach. The newer xywh()
function uses a more familiar x, y, width, height pattern commonly found in graphics programming.
Now for the fun part: polygon()
Hereâs where things get interesting. While circles, ellipses, and rectangles are useful, theyâre also predictable. The polygon()
function is where you start building custom shapes, point by point, corner by corner.
At its heart, polygon()
is wonderfully straightforward. You define a series of coordinate pairs, and CSS connects them in order to create your shape:
clip-path: polygon(x1 y1, x2 y2, x3 y3, ...);
Remember when we talked about the âpathâ concept earlier? This is where it really shows. Each coordinate pair is a waypoint, and CSS draws straight lines between them in the exact sequence you provide. Hereâs a perfect example of why order matters. Take these five points:
/* Pentagon-like shape */
clip-path: polygon(50% 0%, 98% 35%, 79% 91%, 21% 91%, 2% 35%);
/* Same points, different order - creates a star */
clip-path: polygon(50% 0%, 79% 91%, 2% 35%, 98% 35%, 21% 91%);
Same coordinates, completely different shapes. The first creates a neat pentagon-like outline, while the second forms a classic five-pointed star. Itâs that simple connection from point to point that builds your final shape.
Polygon Builder
Hereâs a demo that lets you create and modify polygons in real time. You can drag the red control nodes to reshape your polygon, add or remove points, and see the resulting CSS code update instantly. Toggle the checkbox to switch between pixel and percentage values for responsive design.
Use the âAdd Nodeâ button to introduce new points along your polygonâs edges, or âRemove Nodeâ to simplify the shape. Notice how each modification creates a completely new pathâand how the order of your points defines the final appearance.
When Straight Lines Arenât Enough
Polygons are powerful, but they have one fundamental limitation: theyâre made entirely of straight lines. Sometimes your design calls for curves, smooth transitions, or complex shapes that canât be achieved by connecting points with straight edges. Thatâs where path()
and shape()
step in.
path()
: Raw Power, Borrowed from SVG
The path()
function brings the full power of SVG path syntax directly into CSS. If youâve ever worked with vector graphics, this will feel familiar. The syntax is identical to SVGâs <path>
element:
clip-path: path("M 10,10 L 50,10 L 50,50 Z");
You can use any SVG path command: M
for move, L
for line, C
for cubic curves, Q
for quadratic curves, and so on. This gives you incredible precision and the ability to create complex shapes with smooth curves and sharp angles exactly where you want them.
If youâre not comfortable writing path commands by hand, there are plenty of free online SVG path editors like SVG Path Editor or Boxy SVG that can generate the path string for you.
Hereâs a simple heart shape as an example:
clip-path: path("M100,178 L87.9,167 C45,128 16.7,102 16.7,71 C16.7,45 37,25 62.5,25 C77,25 90.9,32 100,42 C109.1,32 123,25 137.5,25 C163,25 183.3,45 183.3,71 C183.3,102 155,128 112.1,167 Z");
But hereâs the catch: because path()
comes from the SVG world, it only works with absolute values. There are no percentages, no responsive units. If your element changes size, your clipping path stays exactly the same. For truly flexible, responsive shapes, we need something more modern.
shape()
: The Modern Approach
Enter shape()
- CSSâs answer to the limitations of path()
. It provides the same curve capabilities as path()
but with a more CSS-friendly syntax and support for relative units like percentages.
Hereâs the same heart shape, but using shape()
with relative coordinates:
clip-path: shape(
from 50% 89%,
line to 43.95% 83.5%,
curve to 8.35% 35.5% with 22.5% 64% / 8.35% 51%,
curve to 31.25% 12.5% with 8.35% 22.5% / 18.5% 12.5%,
curve to 50% 21% with 38.5% 12.5% / 45.45% 16%,
curve to 68.75% 12.5% with 54.55% 16% / 61.5% 12.5%,
curve to 91.65% 35.5% with 81.5% 12.5% / 91.65% 22.5%,
curve to 56.05% 83.5% with 91.65% 51% / 77.5% 64%,
close);
This demo shows the same heart shape created with both methods. The key difference becomes apparent when you resize the containers. Grab the bottom-right corner of each shape and drag to change its size.
Notice how the path()
version maintains its fixed pixel dimensions regardless of the container size, while the shape()
version scales proportionally thanks to its percentage-based coordinates. This responsiveness is what makes shape()
particularly powerful for modern web design and represents the future of CSS clipping paths.
Syntax Table
If youâre coming from an SVG background, youâll find the transition to shape()
remarkably intuitive. The syntax translates beautifully from SVG path commands, maintaining the same logic while embracing CSSâs flexible unit system.
Just as SVG paths distinguish between absolute (uppercase) and relative (lowercase) commands, shape()
uses the keywords to
and by
. Commands with to
are positioned relative to the elementâs origin, while commands with by
are positioned relative to the previous point in the path.
SVG Path | Shape Equivalent | Description |
---|---|---|
M /m | from | Set first point |
M 10 20 m 10 20 | move to 10px 20px move by 10px 20px | Move point |
L 30 40 l 30 40 | line to 30px 40px line by 30px 40px | Draw line |
H 50 h 50 | hline to 50px hline by 50px | Horizontal line |
V 60 v 60 | vline to 60px vline by 60px | Vertical line |
C x1 y1 x2 y2 x y c x1 y1 x2 y2 x y | curve to x y with x1 y1 / x2 y2 curve by x y with x1 y1 / x2 y2 | Cubic curve with two control points |
S x1 y1 x y s x1 y1 x y | curve to x y with x1 y1 curve by x y with x1 y1 | Cubic curve with one control point |
Q x1 y1 x y q x1 y1 x y | smooth to x y with x1 y1 smooth by x y with x1 y1 | smooth curve with one control point |
T x y t x y | smooth to x y smooth by x y | smooth curve with no control point |
A rx ry angle la sw x y a rx ry angle la sw x y | arc to x y of rx ry sw la angle arc by x y of rx ry sw la angle | Arc with radii, rotation, and flags |
Z /z | close | Close the path |
Self-Intersecting Polygons and Fill Rules
Hereâs where things get mathematically interesting. When you create shapes where lines cross over each other, CSS has to decide which areas should be visible and which should remain transparent. This is controlled by fill rules, and understanding them unlocks some powerful creative possibilities.
CSS supports two fill rules: evenodd
and nonzero
. The difference becomes clear when you see them in action. Hereâs a simple rounded star with both fill rules:
- Even-odd rule: (on the left) Think of it as a simple counting game. Draw an imaginary line from any point to the edge of your element. Every time that line crosses a path edge, count it. If you end up with an odd number, that area gets filled. Even number? It stays transparent. This is why star centers appear hollow, the crossing lines create even-numbered intersections there.
- Nonzero rule: (default value, on the right) This oneâs about direction and flow. As your path travels around the shape, it creates a âwindingâ effect. Areas that get wound in one direction stay filled, while areas where clockwise and counter-clockwise paths cancel each other out become transparent. In most simple shapes like our star, everything winds the same way, so everything stays filled.
This gives you precise control over complex self-intersecting shapes, letting you create intricate patterns with internal cutouts or solid fills, all depending on which fill rule you choose.
Wrapping up
Weâve covered a lot of ground here. From simple circles to complex self-intersecting stars, clip-path
gives you an entirely new vocabulary for shaping your interface. We started with basic geometry, built up to custom polygons, and finally broke free from straight lines with curves and precision.
But hereâs the thing: everything weâve explored so far has been about containment. About cutting away, hiding, cropping. Weâve been thinking inside the box, even when weâre changing its shape.
What if I told you thereâs another way to think about paths in CSS? What if, instead of using them to constrain and contain, you could use them to guide and direct? What if your elements could follow curves, travel along custom routes, and move through space in ways that feel natural and intentional?
Thatâs exactly where weâre heading in part two. Weâre going to shift from static shapes to dynamic motion, from clip-path
to offset-path
. Your elements wonât just be differently shapedâtheyâll be dancing along curves you design, following trajectories that bring your interface to life.
The path of least resistance is about to get a whole lot more interesting.
Article Series