Prepared for the second half? We’re nonetheless exploring the form()
operate, and extra exactly, the arc command. I hope you took the time to digest the primary half as a result of we are going to soar straight into creating extra shapes!
As a reminder, the form()
operate is simply supported in Chrome 137+ and Safari 18.4+ as I’m scripting this in Might 2025.
Sector form
One other basic form that may also be utilized in pie-like charts.

It’s already clear that now we have one arc. As for the factors, now we have two factors that don’t transfer and one which strikes relying on how a lot the sector is crammed.

The code will seem like this:
.sector {
--v: 35; /* [0 100]*/
aspect-ratio: 1;
clip-path: form(from high, arc to X Y of R, line to heart);
}
We outline a variable that may management the filling of the sector. It has a price between 0
and 100
. To attract the form, we begin from the high
, create an arc till the purpose (X, Y), after which we transfer to the heart
.
Are we allowed to make use of key phrase values like
high
andheart
?
Sure! Not like the polygon()
operate, now we have key phrases for the actual instances reminiscent of high
, backside
, left
, and many others. It’s precisely like background-position
that manner. I don’t assume I must element this half because it’s trivial, however it’s good to know as a result of it could actually make your form a bit simpler to learn.
The radius of the arc ought to be equal to 50%
. We’re working with a sq. component and the sector, which is a portion of a circle, must fill the entire component so the radius is the same as half the width (or top).1
As for the purpose, it’s positioned inside that circle, and its place is determined by the V worth. You don’t need a boring math rationalization, proper? No want for it, right here is the formulation of X and Y:
X = 50% + 50% * sin(V * 3.6deg)
Y = 50% - 50% * cos(V * 3.6deg)
Our code turns into:
.sector {
--v: 35; /* [0 100] */
aspect-ratio: 1;
clip-path: form(from high,
arc to calc(50% + 50% * sin(var(--v) * 3.6deg))
calc(50% - 50% * cos(var(--v) * 3.6deg)) of fifty%,
line to heart);
}
Hmm, the consequence will not be good, however there are not any errors within the code. Can you determine what we’re lacking?
It’s the dimensions and route of the arc!
Bear in mind what I informed you within the final article? You’ll all the time have hassle with them, but when we strive the completely different mixtures, we will simply repair the problem. In our case, we have to use: small cw
.
Higher! Let’s strive it with extra values and see how the form behaves:
Oops, some values are good, however others not a lot. The route must be clockwise, however perhaps we must always use massive
as an alternative of small
? Let’s strive:
Nonetheless not working. The problem right here is that we’re transferring one level of the arc primarily based on the V worth, and this motion creates a special configuration for the arc
command.
Right here is an interactive demo to higher visualize what is going on:
Whenever you replace the worth, discover how massive cw
all the time tries to comply with the most important arc between the factors, whereas small cw
tries to comply with the smallest one. When the worth is smaller than 50
, small cw
offers us a very good consequence. However when it’s larger than 50
, the massive cw
mixture is the great one.
I do know, it’s a bit difficult and I needed to review this specific instance to emphasise the truth that we will have plenty of complications working with arcs. However the extra points we face, the higher we get at fixing them.
The answer on this case is fairly easy. We maintain the usage of massive cw
and add a border-radius
to the component. When you examine the earlier demo, you’ll discover that even when massive cw
will not be producing a very good consequence, it’s filling the realm we wish. All we have to do is clip the additional house and a easy border-radius: 50%
will do the job!
I’m preserving the box-shadow
in there so we will see the arc, however we will clearly see how border-radius
is making a distinction on the primary form.
There may be nonetheless one edge case we have to think about. When the worth is the same as 100
, each factors of the arc can have the identical coordinates, which is logical because the sector is full and now we have a circle. However when it’s the case, the arc will do nothing by definition and we received’t get a full circle.
To repair this, we will restrict the worth to, for instance, 99.99
to keep away from reaching 100
. It’s type of hacky, however it does the job.
.sector {
--v: 35; /* [0 100]*/
--_v: min(99.99, var(--v));
aspect-ratio: 1;
clip-path: form(from high,
arc to calc(50% + 50% * sin(var(--_v) * 3.6deg))
calc(50% - 50% * cos(var(--_v) * 3.6deg)) of fifty% massive cw,
line to heart);
border-radius: 50%;
}
Now our form is ideal! And don’t overlook that you may apply it to picture parts:
Arc form
Just like the sector form, we will additionally create an arc form. In any case, we’re working with the arc
command, so now we have to do it.

We have already got half the code because it’s mainly a sector form with out the interior half. We merely want so as to add extra instructions to chop the interior half.

.arc {
--v: 35;
--b: 30px;
--_v: min(99.99, var(--v));
aspect-ratio: 1;
clip-path: form(from high,
arc to calc(50% + 50% * sin(var(--_v) * 3.6deg))
calc(50% - 50% * cos(var(--_v) * 3.6deg)) of fifty% cw massive,
line to calc(50% + (50% - var(--b)) * sin(var(--_v) * 3.6deg))
calc(50% - (50% - var(--b)) * cos(var(--_v) * 3.6deg)),
arc to 50% var(--b) of calc(50% - var(--b)) massive
);
border-radius: 50%;
}
From the sector form, we take away the line to heart
piece and substitute it with one other line
command that strikes to a degree positioned on the interior circle. When you examine its coordinates with the earlier level, you will note an offset equal to --b
, which is a variable that defines the arc’s thickness. Then we draw an arc in the other way (ccw
) till the purpose 50% var(--b)
, which can also be some extent with an offset equal to --b
from the highest.
I’m not defining the route of the second arc since, by default, the browser will use ccw
.
Ah, the identical concern we hit with the sector form is putting once more! Not all of the values are giving a very good consequence because of the identical logic we noticed earlier, and, as you’ll be able to see, border-radius
will not be fixing it. This time, we have to discover a method to conditionally change the dimensions of the arc primarily based on the worth. It ought to be massive
when V is larger than 50
, and small
in any other case.
Situations in CSS? Sure, it’s doable! First, let’s convert the V worth like this:
--_f: spherical(down, var(--_v), 50)
The worth is throughout the vary [0 99.99]
(don’t overlook that we don’t wish to attain the worth 100). We use spherical()
to ensure it’s all the time equal to a a number of of a selected worth, which is 50
in our case. If the worth is smaller than 50
, the result’s 0
, in any other case it’s 50
.
There are solely two doable values, so we will simply add a situation. If --_f
is the same as 0
we use small; in any other case, we use massive:
.arc {
--v: 35;
--b: 30px;
--_v: min(99.99, var(--v));
--_f: spherical(down,var(--_v), 50);
--_c: if(type(--_f: 0): small; else: massive);
clip-path: form(from high,
arc to calc(50% + 50% * sin(var(--_v) * 3.6deg))
calc(50% - 50% * cos(var(--_v) * 3.6deg)) of fifty% cw var(--_c),
line to calc(50% + (50% - var(--b)) * sin(var(--_v) * 3.6deg))
calc(50% - (50% - var(--b)) * cos(var(--_v) * 3.6deg)),
arc to 50% var(--b) of calc(50% - var(--b)) var(--_c)
);
}
I do know what you might be considering, however let me inform you that the above code is legitimate. You in all probability don’t realize it but, however CSS has lately launched inline conditionals utilizing an if()
syntax. It’s nonetheless early to play with it, however now we have discovered an ideal use case for it. Here’s a demo that you may take a look at utilizing Chrome Canary:
One other method to specific circumstances is to depend on type queries which have higher help:
.arc {
--v: 35;
--b: 30px;
--_v: min(99.99, var(--v));
--_f: spherical(down, var(--_v), 50);
aspect-ratio: 1;
container-name: arc;
}
.arc:earlier than {
content material: "";
clip-path: form(from high,
arc to calc(50% + 50% * sin(var(--_v) * 3.6deg))
calc(50% - 50% * cos(var(--_v) * 3.6deg)) of fifty% cw var(--_c, massive),
line to calc(50% + (50% - var(--b)) * sin(var(--_v) * 3.6deg))
calc(50% - (50% - var(--b)) * cos(var(--_v) * 3.6deg)),
arc to 50% var(--b) of calc(50% - var(--b)) var(--_c, massive)
);
@container type(--_f: 0) { --_c: small }
}
The logic is identical however, this function requires a parent-child relation, which is why I’m utilizing a pseudo-element. By default, the dimensions shall be massive
, and if the worth of --_f
is the same as 0
, we change to small
.
Notice that now we have to register the variable --_f
utilizing @property
to have the ability to both use the if()
operate or type queries.
Did you discover one other delicate change I’ve made to the form? I eliminated border-radius
and I utilized the conditional logic to the primary arc. Each have the identical concern, however border-radius
can repair solely one in all them whereas the conditional logic can repair each, so we will optimize the code a bit.
Arc form with rounded edges
What about including rounded edges to our arc? It’s higher, proper?

Are you able to see the way it’s carried out? Take it as a small train and replace the code from the earlier examples so as to add these rounded edges. I hope you’ll be able to discover it by your self as a result of the adjustments are fairly easy — we replace one line
command with an arc
command and we add one other arc
command on the finish.
clip-path: form(from high,
arc to calc(50% + 50% * sin(var(--_v) * 3.6deg))
calc(50% - 50% * cos(var(--_v) * 3.6deg)) of fifty% cw var(--_c, massive),
arc to calc(50% + (50% - var(--b)) * sin(var(--_v) * 3.6deg))
calc(50% - (50% - var(--b)) * cos(var(--_v) * 3.6deg)) of 1% cw,
arc to 50% var(--b) of calc(50% - var(--b)) var(--_c, massive),
arc to high of 1% cw
);
If you don’t perceive the adjustments, get out a pen and paper, then draw the form to higher see the 4 arcs we’re drawing. Beforehand, we had two arcs and two strains, however now we’re working with arcs as an alternative of strains.
And did you keep in mind the trick of utilizing a 1%
worth for the radius? The brand new arcs are half circles, so we will depend on that trick the place you specify a tiny radius and the browser will do the job for you and discover the right worth!
Conclusion
We’re carried out — sufficient in regards to the arc
command! I needed to write two articles that concentrate on this command as a result of it’s the trickiest one, however I hope it’s now clear use it and deal with the route and measurement factor, as that’s in all probability the supply of most complications.
By the best way, I’ve solely studied the case of round arcs as a result of, in actuality, we will specify two radii and draw elliptical ones, which is much more advanced. Except you wish to change into a form()
grasp, you’ll hardly ever want elliptical arcs, so don’t hassle your self with them.
Till the following article, I wrote an article for Frontend Masters the place you’ll be able to create extra fancy shapes utilizing the arc
command that could be a good follow-up to this one.

In our case, now we have a sq. component so 50% of the direction-agnostic measurement shall be equal to 50% of sqrt(Width² + Height²)/sqrt(2)
. And since each width and top are equal, we finish with 50% of the width (or the peak). ⮑