Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      Why Non-Native Content Designers Improve Global UX

      July 18, 2025

      DevOps won’t scale without platform engineering and here’s why your teams are still stuck

      July 18, 2025

      This week in AI dev tools: Slack’s enterprise search, Claude Code’s analytics dashboard, and more (July 18, 2025)

      July 18, 2025

      Report: 71% of tech leaders won’t hire devs without AI skills

      July 17, 2025

      Remedy offers update on ‘FBC: Firebreak,’ details coming improvements — “We’ve seen many players come into the game and leave within the first hour.”

      July 18, 2025

      I ran with Samsung’s Galaxy Watch 8 Classic, and it both humbled and motivated me

      July 18, 2025

      You can finally move Chrome’s address bar on Android – here’s how

      July 18, 2025

      Is your Ring camera showing strange logins? Here’s what’s going on

      July 18, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      The details of TC39’s last meeting

      July 18, 2025
      Recent

      The details of TC39’s last meeting

      July 18, 2025

      Online Examination System using PHP and MySQL

      July 18, 2025

      A tricky, educational quiz: it’s about time..

      July 18, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured

      Remedy offers update on ‘FBC: Firebreak,’ details coming improvements — “We’ve seen many players come into the game and leave within the first hour.”

      July 18, 2025
      Recent

      Remedy offers update on ‘FBC: Firebreak,’ details coming improvements — “We’ve seen many players come into the game and leave within the first hour.”

      July 18, 2025

      Ubuntu 25.10 Shrinks its Raspberry Pi Install Footprint

      July 18, 2025

      Microsoft kills Movies & TV storefront on Windows and Xbox — here’s what will happen to your purchased media

      July 18, 2025
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»News & Updates»Better CSS Shapes Using shape() — Part 2: More on Arcs

    Better CSS Shapes Using shape() — Part 2: More on Arcs

    May 30, 2025

    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);
    }
    CodePen Embed Fallback

    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.

    CodePen Embed Fallback

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

    CodePen Embed Fallback

    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:

    CodePen Embed Fallback

    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:

    CodePen Embed Fallback

    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!

    CodePen Embed Fallback

    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:

    CodePen Embed Fallback

    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.

    CodePen Embed Fallback

    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:

    CodePen Embed Fallback

    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.

    CodePen Embed Fallback

    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!

    CodePen Embed Fallback

    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). ⮑


    Better CSS Shapes Using shape() — Part 2: More on Arcs originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

    Source: Read More 

    Facebook Twitter Reddit Email Copy Link
    Previous ArticleDeepSeek Releases R1-0528: An Open-Source Reasoning AI Model Delivering Enhanced Math and Code Performance with Single-GPU Efficiency
    Next Article Complete Beginner’s Guide to Creating AI Applications with OpenAI

    Related Posts

    News & Updates

    Remedy offers update on ‘FBC: Firebreak,’ details coming improvements — “We’ve seen many players come into the game and leave within the first hour.”

    July 18, 2025
    News & Updates

    I ran with Samsung’s Galaxy Watch 8 Classic, and it both humbled and motivated me

    July 18, 2025
    Leave A Reply Cancel Reply

    For security, use of Google's reCAPTCHA service is required which is subject to the Google Privacy Policy and Terms of Use.

    Continue Reading

    CVE-2024-55912 – IBM Concert Software Cryptographic Weakness

    Common Vulnerabilities and Exposures (CVEs)

    Adobe Firefly app is finally launching to users. Here’s how to access (and the perks)

    News & Updates

    Windows 11 is planning a huge redesign of its Start Menu

    Operating Systems

    CVE-2025-3221 – IBM InfoSphere Information Server Denial of Service Vulnerability

    Common Vulnerabilities and Exposures (CVEs)

    Highlights

    Linux

    Raspberry Pi Imager 1.9.4 Released with Various Changes

    June 4, 2025

    A new version of the Raspberry Pi Imager, an open source and cross-platform image writing…

    Your AI Agents Might Be Leaking Data — Watch this Webinar to Learn How to Stop It

    July 4, 2025

    The Front-End Performance Optimization Handbook – Tips and Strategies for Devs

    May 8, 2025

    Meeting European Accessibility Act (EAA) Standards: A Developer’s Checklist

    July 16, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.