惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

AI
AI
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
N
News and Events Feed by Topic
O
OpenAI News
W
WeLiveSecurity
S
Secure Thoughts
www.infosecurity-magazine.com
www.infosecurity-magazine.com
H
Heimdal Security Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Schneier on Security
Schneier on Security
Cloudbric
Cloudbric
T
Threatpost
T
Troy Hunt's Blog
T
Tenable Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
V
Vulnerabilities – Threatpost
Google Online Security Blog
Google Online Security Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
The Hacker News
The Hacker News
Know Your Adversary
Know Your Adversary
Forbes - Security
Forbes - Security
Cyberwarzone
Cyberwarzone
C
CXSECURITY Database RSS Feed - CXSecurity.com
P
Privacy & Cybersecurity Law Blog
L
Lohrmann on Cybersecurity
博客园 - 司徒正美
腾讯CDC
博客园 - 三生石上(FineUI控件)
爱范儿
爱范儿
J
Java Code Geeks
Hugging Face - Blog
Hugging Face - Blog
博客园 - 【当耐特】
M
MIT News - Artificial intelligence
T
The Blog of Author Tim Ferriss
MongoDB | Blog
MongoDB | Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
GbyAI
GbyAI
D
Docker
V
Visual Studio Blog
云风的 BLOG
云风的 BLOG
I
InfoQ
WordPress大学
WordPress大学
人人都是产品经理
人人都是产品经理
A
Arctic Wolf
L
LINUX DO - 热门话题
The Cloudflare Blog
雷峰网
雷峰网
Microsoft Azure Blog
Microsoft Azure Blog
小众软件
小众软件

CSS Articles by Temani Afif

Get Ready For the Powerful CSS border-shape Property! | CSS-Tricks Let’s Play With Gap Decorations! How to Control Infinite CSS Animations (Part 2 of 2) How to Control Infinite CSS Animations (Part 1 of 2) Two Circles, One Arrow, and Anchor Positioning Making a Responsive Pyramidal Grid With Modern CSS | CSS-Tricks How to Create a CSS-only Elastic Text Effect Making Complex CSS Shapes Using shape() | CSS-Tricks Responsive Hexagon Grid Using Modern CSS | CSS-Tricks Responsive List of Avatars Using Modern CSS (Part 2) | CSS-Tricks Responsive List of Avatars Using Modern CSS (Part 1) | CSS-Tricks Perfectly Pointed Tooltips: To The Corners Perfectly Pointed Tooltips: All Four Sides Perfectly Pointed Tooltips: A Foundation Sequential linear() Animation With N Elements | CSS-Tricks Infinite Marquee Animation using Modern CSS Better CSS Shapes Using shape() — Part 4: Close and Move | CSS-Tricks Drawing CSS Shapes using corner-shape Better CSS Shapes Using shape() — Part 3: Curves | CSS-Tricks Better CSS Shapes Using shape() — Part 1: Lines and Arcs | CSS-Tricks Creating Blob Shapes using clip-path: shape() Creating Flower Shapes using clip-path: shape() Custom progress element using the attr() function A CSS-Only Star Rating Component and More! (Part 2) | CSS-Tricks A CSS-Only Star Rating Component and More! (Part 1) | CSS-Tricks How to Create Wavy Boxes Using CSS Full-Bleed Layout with Modern CSS Fancy Menu Navigation Using Anchor Positioning | CSS-Tricks How to Create a Zig-Zag Box Using CSS How to Create Zig-Zag CSS Loaders Using One Element Custom Progress Element Using Anchor Positioning & Scroll-Driven Animations How to Create Filling CSS Loaders Using One Element How to Create Curved-Edge and Rounded-Edge Shapes Using CSS CSS Tricks That Use Only One Gradient | CSS-Tricks How to create Shapes with Inner Curves using CSS Mask Custom Range Slider Using Anchor Positioning & Scroll-Driven Animations How to Get the Width/Height of Any Element in Only CSS How Keyboard Navigation Works in a CSS Game How To Create Cut-Out Shapes using The clip-path property The Modern Guide For Making CSS Shapes — Smashing Magazine css-shape.com: The Ultimate Collection of CSS-only Shapes ⚡️ Sliding 3D Image Frames In CSS — Smashing Magazine CSS Tricks To Master The clip-path Property Creating Wavy Circles with Fancy Animations in CSS Modern CSS Tooltips And Speech Bubbles (Part 2) — Smashing Magazine Do you need a Tooltip or a Speech Bubble? I have created 100 using CSS 😲 Modern CSS Tooltips And Speech Bubbles (Part 1) — Smashing Magazine Creating Flower Shapes using CSS Mask & Trigonometric Functions CSS Shapes: The Triangle The Complex But Awesome CSS border-image Property — Smashing Magazine CSS Tricks to add 3D Effects to your Text CSS Responsive Multi-Line Ribbon Shapes (Part 2) — Smashing Magazine CSS Responsive Multi-Line Ribbon Shapes (Part 1) — Smashing Magazine I have made 100+ CSS-only Ribbon Shapes | The Perfect Collection 🎀 How to create a CSS-only infinite scroll animation Re-Creating The Pop-Out Hover Effect With Modern CSS (Part 2) — Smashing Magazine Re-Creating The Pop-Out Hover Effect With Modern CSS (Part 1) — Smashing Magazine How to Create CSS Ribbon Shapes with a Single Element CSS Shapes: The Ribbon Revealing Images With CSS Mask Animations — Smashing Magazine css-loaders.com: The Biggest Collection of Loading Animations (more than 500 🤯) How To Define An Array Of Colors With CSS — Smashing Magazine CSS effects on images II Shines, Perspective, And Rotations: Fancy CSS 3D Effects For Images — Smashing Magazine What's your Lucky CSS Pattern? Check It Now! How to Add a CSS Reveal Animation to Your Images Modern Layouts using CSS Grid I created 100+ unique CSS patterns | The best collection 🤩 How to Create a Custom Range Slider Using CSS CSS Shapes: The Heart CSS Shapes: Polygon & Starburst A Text Reveal Animation using CSS CSS Tip: learn CSS the easy way! How to build a CSS-only accordion How to create Breadcrumb Navigation with CSS Different Ways to Get CSS Gradient Shadows | CSS-Tricks A CSS-only responsive Stepper component A Fancy Hover Effect For Your Avatar | CSS-Tricks How to make a zoom effect using CSS Creating a Custom Cursor using CSS CSS Infinite 3D Sliders | CSS-Tricks CSS Infinite Slider Flipping Through Polaroid Images | CSS-Tricks How to create a responsive sidebar menu using CSS CSS Infinite and Circular Rotating Image Slider | CSS-Tricks Making Static Noise From a Weird CSS Gradient Bug | CSS-Tricks CSS Grid and Custom Shapes, Part 3 | CSS-Tricks Fancy Image Decorations: Outlines and Complex Animations | CSS-Tricks Fancy Image Decorations: Masks and Advanced Hover Effects | CSS-Tricks Fancy Image Decorations: Single Element Magic | CSS-Tricks How to create an infinite image slider using CSS How to Create Wavy Shapes & Patterns in CSS | CSS-Tricks How I Made a Pure CSS Puzzle Game | CSS-Tricks How to create a Tooltip/Speech Bubble using CSS CSS Grid and Custom Shapes, Part 2 | CSS-Tricks CSS Grid and Custom Shapes, Part 1 | CSS-Tricks Zooming Images in a Grid Layout | CSS-Tricks How to create a CSS-only loader with one element Exploring CSS Grid’s Implicit Grid and Auto-Placement Powers | CSS-Tricks How to create background pattern using CSS & conic-gradient Single Element Loaders: The Bars | CSS-Tricks
Better CSS Shapes Using shape() — Part 2: More on Arcs | CSS-Tricks
Temani Afif · 2025-05-30 · via CSS Articles by Temani Afif

Ready for the second part? We are still exploring the shape() function, and more precisely, the arc command. I hope you took the time to digest the first part because we will jump straight into creating more shapes!

As a reminder, the shape() function is only supported in Chrome 137+ and Safari 18.4+ as I’m writing this in May 2025.

Sector shape

Another classic shape that can also be used in pie-like charts.

A series of three semi-circles.

It’s already clear that we have one arc. As for the points, we have two points that don’t move and one that moves depending on how much the sector is filled.

Diagram showing the fixed and variable lengths of an arc shape.

The code will look like this:

.sector {
  --v: 35; /* [0 100]*/
  
  aspect-ratio: 1;
  clip-path: shape(from top, arc to X Y of R, line to center);
}

We define a variable that will control the filling of the sector. It has a value between 0 and 100. To draw the shape, we start from the top, create an arc until the point (X, Y), and then we move to the center.

Are we allowed to use keyword values like top and center?

Yes! Unlike the polygon() function, we have keywords for the particular cases such as top, bottom, left, etc. It’s exactly like background-position that way. I don’t think I need to detail this part as it’s trivial, but it’s good to know because it can make your shape a bit easier to read.

The radius of the arc should be equal to 50%. We are working with a square element and the sector, which is a portion of a circle, need to fill the whole element so the radius is equal to half the width (or height).1

As for the point, it’s placed within that circle, and its position depends on the V value. You don’t want a boring math explanation, right? No need for it, here is the formula of X and Y:

X = 50% + 50% * sin(V * 3.6deg)
Y = 50% - 50% * cos(V * 3.6deg)

Our code becomes:

.sector {
  --v: 35; /* [0 100] */
  
  aspect-ratio: 1;
  clip-path: shape(from top,
    arc to calc(50% + 50% * sin(var(--v) * 3.6deg)) 
           calc(50% - 50% * cos(var(--v) * 3.6deg)) of 50%,
    line to center);
}

Hmm, the result is not good, but there are no mistakes in the code. Can you figure out what we are missing?

It’s the size and direction of the arc!

Remember what I told you in the last article? You will always have trouble with them, but if we try the different combinations, we can easily fix the issue. In our case, we need to use: small cw.

Better! Let’s try it with more values and see how the shape behaves:

Oops, some values are good, but others not so much. The direction needs to be clockwise, but maybe we should use large instead of small? Let’s try:

Still not working. The issue here is that we are moving one point of the arc based on the V value, and this movement creates a different configuration for the arc command.

Here is an interactive demo to better visualize what is happening:

When you update the value, notice how large cw always tries to follow the largest arc between the points, while small cw tries to follow the smallest one. When the value is smaller than 50, small cw gives us a good result. But when it’s bigger than 50, the large cw combination is the good one.

I know, it’s a bit tricky and I wanted to study this particular example to emphasize the fact that we can have a lot of headaches working with arcs. But the more issues we face, the better we get at fixing them.

The solution in this case is pretty simple. We keep the use of large cw and add a border-radius to the element. If you check the previous demo, you will notice that even if large cw is not producing a good result, it’s filling the area we want. All we need to do is clip the extra space and a simple border-radius: 50% will do the job!

I am keeping the box-shadow in there so we can see the arc, but we can clearly see how border-radius is making a difference on the main shape.

There is still one edge case we need to consider. When the value is equal to 100, both points of the arc will have the same coordinates, which is logical since the sector is full and we have a circle. But when it’s the case, the arc will do nothing by definition and we won’t get a full circle.

To fix this, we can limit the value to, for example, 99.99 to avoid reaching 100. It’s kind of hacky, but it does the job.

.sector {
  --v: 35; /* [0 100]*/
  
  --_v: min(99.99, var(--v));
  aspect-ratio: 1;
  clip-path: shape(from top,
    arc to calc(50% + 50% * sin(var(--_v) * 3.6deg)) 
           calc(50% - 50% * cos(var(--_v) * 3.6deg)) of 50% large cw,
    line to center);
  border-radius: 50%;
}

Now our shape is perfect! And don’t forget that you can apply it to image elements:

Arc shape

Similar to the sector shape, we can also create an arc shape. After all, we are working with the arc command, so we have to do it.

A series of three circular rings at various lengths.

We already have half the code since it’s basically a sector shape without the inner part. We simply need to add more commands to cut the inner part.

Diagram showing the arc points of a semi-circle shape. There are two arcs, one on the outside and one on the inside. They are joined by straight lines.
.arc {
  --v: 35; 
  --b: 30px;
  
  --_v: min(99.99, var(--v));
  aspect-ratio: 1;
  clip-path: shape(from top,
    arc to calc(50% + 50% * sin(var(--_v) * 3.6deg)) 
           calc(50% - 50% * cos(var(--_v) * 3.6deg)) of 50% cw large,
    
    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)) large
  );
  border-radius: 50%;
}

From the sector shape, we remove the line to center piece and replace it with another line command that moves to a point placed on the inner circle. If you compare its coordinates with the previous point, you will see an offset equal to --b, which is a variable that defines the arc’s thickness. Then we draw an arc in the opposite direction (ccw) until the point 50% var(--b), which is also a point with an offset equal to --b from the top.

I am not defining the direction of the second arc since, by default, the browser will use ccw.

Ah, the same issue we hit with the sector shape is striking again! Not all the values are giving a good result due to the same logic we saw earlier, and, as you can see, border-radius is not fixing it. This time, we need to find a way to conditionally change the size of the arc based on the value. It should be large when V is bigger than 50, and small otherwise.

Conditions in CSS? Yes, it’s possible! First, let’s convert the V value like this:

--_f: round(down, var(--_v), 50)

The value is within the range [0 99.99] (don’t forget that we don’t want to reach the value 100). We use round() to make sure it’s always equal to a multiple of a specific value, which is 50 in our case. If the value is smaller than 50, the result is 0, otherwise it’s 50.

There are only two possible values, so we can easily add a condition. If --_f is equal to 0 we use small; otherwise, we use large:

.arc {
  --v: 35;
  --b: 30px;
  
  --_v: min(99.99, var(--v));
  --_f: round(down,var(--_v), 50);
  --_c: if(style(--_f: 0): small; else: large);
  clip-path: shape(from top,
    arc to calc(50% + 50% * sin(var(--_v) * 3.6deg)) 
           calc(50% - 50% * cos(var(--_v) * 3.6deg)) of 50% 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 know what you are thinking, but let me tell you that the above code is valid. You probably don’t know it yet, but CSS has recently introduced inline conditionals using an if() syntax. It’s still early to play with it, but we have found a perfect use case for it. Here is a demo that you can test using Chrome Canary:

Another way to express conditions is to rely on style queries that have better support:

.arc {
  --v: 35;
  --b: 30px;
  
  --_v: min(99.99, var(--v));
  --_f: round(down, var(--_v), 50);
  aspect-ratio: 1;
  container-name: arc;
}
.arc:before {
  content: "";
  clip-path: shape(from top,
    arc to calc(50% + 50% * sin(var(--_v) * 3.6deg)) 
           calc(50% - 50% * cos(var(--_v) * 3.6deg)) of 50% cw var(--_c, large),
    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, large)
  );
  @container style(--_f: 0) { --_c: small }
}

The logic is the same but, this feature requires a parent-child relation, which is why I am using a pseudo-element. By default, the size will be large, and if the value of --_f is equal to 0, we switch to small.

Note that we have to register the variable --_f using @property to be able to either use the if() function or style queries.

Did you notice another subtle change I have made to the shape? I removed border-radius and I applied the conditional logic to the first arc. Both have the same issue, but border-radius can fix only one of them while the conditional logic can fix both, so we can optimize the code a little.

Arc shape with rounded edges

What about adding rounded edges to our arc? It’s better, right?

A series of three semi-circles with rounded edges at varying lengths.

Can you see how it’s done? Take it as a small exercise and update the code from the previous examples to add those rounded edges. I hope you are able to find it by yourself because the changes are pretty straightforward — we update one line command with an arc command and we add another arc command at the end.

clip-path: shape(from top,
  arc to calc(50% + 50% * sin(var(--_v) * 3.6deg)) 
         calc(50% - 50% * cos(var(--_v) * 3.6deg)) of 50% cw var(--_c, large),
  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, large),
  arc to top of 1% cw
);

If you do not understand the changes, get out a pen and paper, then draw the shape to better see the four arcs we are drawing. Previously, we had two arcs and two lines, but now we are working with arcs instead of lines.

And did you remember the trick of using a 1% value for the radius? The new arcs are half circles, so we can rely on that trick where you specify a tiny radius and the browser will do the job for you and find the correct value!

Conclusion

We are done — enough about the arc command! I had to write two articles that focus on this command because it’s the trickiest one, but I hope it’s now clear how to use it and how to handle the direction and size thing, as that is probably the source of most headaches.

By the way, I have only studied the case of circular arcs because, in reality, we can specify two radii and draw elliptical ones, which is even more complex. Unless you want to become a shape() master, you will rarely need elliptical arcs, so don’t bother yourself with them.

Until the next article, I wrote an article for Frontend Masters where you can create more fancy shapes using the arc command that is a good follow-up to this one.

Three shapes. The first looks like a flower. The second looks like a sun. The third looks like a blob.

Footnotes

(1) The arc command is defined to draw elliptical arcs by taking two radii, but if we define one radius value, it means that the vertical and horizontal radius will use that same value and we have circular arcs. When it’s a length, it’s trivial, but when we use percentages, the value will resolve against the direction-agnostic size, which is equal to the length of the diagonal of the box, divided by sqrt(2).

In our case, we have a square element so 50% of the direction-agnostic size will be equal to 50% of sqrt(Width² + Height²)/sqrt(2). And since both width and height are equal, we end with 50% of the width (or the height).