In a previous article, we used modern CSS features such as mask
, trigonometric functions, and CSS variables to create flower-like shapes.

The HTML code was a single element, which means we can apply the CSS to image elements and get cool frames like the demo below:
In this article, we are redoing the same shapes using the new shape()
function, which I think will become my favorite CSS feature.
At the time of writing, only Chrome, Edge, and Safari have the full support of the features we will be using.
What is shape()?
You are probably familiar with clip-path: polygon()
, right? A function that allows you to specify different points, draw straight lines between them and create various CSS shapes (I invite you to check my online collection of CSS shapes to see some of them). I said “straight lines” because when it comes to curves, clip-path
is very limited. We have circle()
and ellipse()
, but we cannot achieve complex shapes with them.
shape()
is the new value that overcomes such limitation. In addition to straight lines, it allows us to draw curves. But If you check the MDN page or the specification, you can see that the syntax is a bit complex and not easy to grasp. It’s very similar to SVG path, which is good as it gives us a lot of options and flexibility, but it requires a lot of practice to get used to it.
I will not write a boring tutorial explaining the syntax and all the possible values, but rather focus on one command per article. In this article, we will study the arc
command, and the next article will be about the curve
command. And, of course, we are going to draw cool shapes. Otherwise it’s no fun!
arc
Command
The This command allows you to draw an elliptic arc between two points but I will only consider the particular case of a circle which is easier and the one you will need the most. Let’s start with some geometry to understand how it works.
If you have two points (A and B) and a radius, there are exactly two circles with the given radius that intersect with the points. The intersection of both circles gives us 4 possible arcs between A and B that we can identify with a size (small or large) and a direction (clockwise or counter-clockwise)

The code will look like the below:
clip-path: shape(from Xa Ya, arc to Xb Yb of R [large or small] [cw or ccw])
Code language: CSS (css)
The first command of a shape is always a from
to give the starting point, and then we use the arc
to define the ending point, the radius, the size, and the direction.
Here is a demo to illustrate the different values:
The points and the radii are the same. I am only changing the size and direction to choose one of the four possibilities. It should be noted that the size and direction aren’t mandatory. The defaults are small
and ccw
.
That’s all: we have what we need to draw flower shapes!
Creating The Flower Shape
I will start with a figure from the previous article.

Using the mask method, we had to draw a big circle at the center and small circles placed around it. Using shape()
, we need to draw the arcs of the small circles. The starting and ending points of each arc are placed where the circles touch each other.

The code will look as follows:
.flower {
clip-path: shape(from X0 Y0,
arc to X1 Y1 of R,
arc to X2 Y2 of R,
arc to X3 Y3 of R,
arc to X4 Y4 of R,
...
arc to Xn Yn of R
)
}
Code language: CSS (css)
And like I did with the mask method, I will rely on Sass to create a loop.
$n: 10;
.flower {
$m: ();
$m: append($m,from X0 Y0,comma);
@for $i from 1 through $n {
$m: append($m,arc to Xi Yi of R,comma);
}
clip-path: shape(#{$m});
}
Code language: SCSS (scss)
Now we need to identify the radius of the small circles (R) and the position of the different points (Xi, Yi). I already did the calculation of the radius in the previous article, so I will reuse the same value:
R = 50%/(1 + 1/math.sin(180deg/$n))
Code language: SCSS (scss)
For the points, they are placed around the same circle so their coordinate can be written like below:
Xi = 50% + D*math.cos($i*360deg/$n)
Yi = 50% + D*math.sin($i*360deg/$n)
Code language: SCSS (scss)
Here is a figure to illustrate the distance D and the radius R:

I know you don’t want a boring geometry course so here is the value of D.
D = 50%/(math.tan(180deg/$n) + 1/math.cos(180deg/$n))
Code language: SCSS (scss)
And the final code will be:
$n: 10;
.flower {
$m: ();
$r: 50%/(1 + 1/math.sin(180deg/$n));
$d: 50%/(math.tan(180deg/$n) + 1/math.cos(180deg/$n));
$m: append($m,from
50% + $d*math.cos(0)
50% + $d*math.sin(0),comma);
@for $i from 1 through $n {
$m: append($m,arc to
50% + $d*math.cos($i*360deg/$n)
50% + $d*math.sin($i*360deg/$n)
of $r,comma);
}
clip-path: shape(#{$m});
}
Code language: SCSS (scss)
Wait, we get the inverted shape instead? Why is that?
We didn’t define the size and the direction of the arcs so by default the browser will use small
and ccw
. This gives us the inverted version of the flower. If you try the 4 different combinations (including the default one) you will get the following:

small ccw
and large cw
give us the shape we are looking for. The small cw
is an interesting variation, and the large ccw
is a funny one. We can consider a CSS variable to easily control which shape we want to get.
Combining Both Shapes
Now, what about the shape below?

The idea is to use the same code and alternate between two arc configurations.
$n: 10;
.flower {
$m: ();
$r: 50%/(1 + 1/math.sin(180deg/$n));
$d: 50%/(math.tan(180deg/$n) + 1/math.cos(180deg/$n));
$m: append($m,from
50% + $d*math.cos(0)
50% + $d*math.sin(0),comma);
@for $i from 1 through $n {
$c:small ccw;
@if $i % 2 == 0 {$c:large cw}
$m: append($m,arc to
50% + $d*math.cos($i*360deg/$n)
50% + $d*math.sin($i*360deg/$n)
of $r $c,comma);
}
clip-path: shape(#{$m});
}
Code language: SCSS (scss)
I introduced a new variable $c
within the loop that will have the value small ccw
when $i
is odd and large cw
otherwise.
Cool right? The compiled code will look like the below:
clip-path:
shape(from X0 Y0,
arc to X1 Y1 of R small ccw,
arc to X2 Y2 of R large cw,
arc to X3 Y3 of R small ccw,
arc to X4 Y4 of R large cw,
...
)
The first arc will use the inner curve (small ccw
), the next one the outer curve (large cw
) and so on. We repeat this to get our wavy-flower shape!
Optimizing The Code
We made a generic code that allow us to get any shape variation by simply controlling the size/direction of the arcs, but for each particular case, we can have a more simplified code.
For the inverted variation (small ccw
), the value of D
can be replaced by 50%
. This will simplify the formula and also increase the area covered by the shape. We also need to update the value of the radius.
$n: 10;
.flower {
$m: ();
$r: 50%*math.tan(180deg/$n);
$m: append($m,from
50% + 50%*math.cos(0)
50% + 50%*math.sin(0),comma);
@for $i from 1 through $n {
$m: append($m,arc to
50% + 50%*math.cos($i*360deg/$n)
50% + 50%*math.sin($i*360deg/$n)
of $r,comma);
}
clip-path: shape(#{$m});
}
Code language: SCSS (scss)
We can do the same for the main shape, but this time we can simplify the value of the radius and use 1%
.
$n: 10;
.flower {
$m: ();
$d: 50%/(math.cos(180deg/$n)*(1 + math.tan(180deg/$n)));
$m: append($m,from
50% + $d*math.cos(0)
50% + $d*math.sin(0),comma);
@for $i from 1 through $n {
$m: append($m,arc to
50% + $d*math.cos($i*360deg/$n)
50% + $d*math.sin($i*360deg/$n)
of 1% cw,comma);
}
clip-path: shape(#{$m});
}
Code language: CSS (css)
This one is interesting because using 1%
as a radius is kind of strange and not intuitive. In the explanation of the arc command, I said that we have exactly two circles with the given radius that intersect with the two points, but what if the radius is smaller than half the distance between the points? No circles can meet that condition.
This case falls into an error handling where the browser will scale the radius until we can have at least one circle that meets the conditions (yes, it’s defined within the specification). That circle will simply have its radius equal to half the distance between both points. It also means we only have two arcs with the same size (small
and large
will be equal)

In other words, if you specify a small radius (like 1%
), you are telling the browser to find the radius value for you (a lazy but clever move!). This trick won’t work in all the situations but can be handy in many of them so don’t forget about it.
Conclusion
We are done with this first article! You should have a good overview of the arc command and how to use it. I only studied the particular case of drawing circular arcs but once you get used to this you can move to the elliptic ones even if I think you will rarely need them.
Let’s end with a last demo of a heart shape, where I am using the arc command. Can you figure out how to do it before checking my code?
And don’t forget to bookmark my online generators from where you can get the code of the flower shapes and more!