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

      Sunshine And March Vibes (2025 Wallpapers Edition)

      June 1, 2025

      The Case For Minimal WordPress Setups: A Contrarian View On Theme Frameworks

      June 1, 2025

      How To Fix Largest Contentful Paint Issues With Subpart Analysis

      June 1, 2025

      How To Prevent WordPress SQL Injection Attacks

      June 1, 2025

      7 MagSafe accessories that I recommend every iPhone user should have

      June 1, 2025

      I replaced my Kindle with an iPad Mini as my ebook reader – 8 reasons why I don’t regret it

      June 1, 2025

      Windows 11 version 25H2: Everything you need to know about Microsoft’s next OS release

      May 31, 2025

      Elden Ring Nightreign already has a duos Seamless Co-op mod from the creator of the beloved original, and it’ll be “expanded on in the future”

      May 31, 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

      Student Record Android App using SQLite

      June 1, 2025
      Recent

      Student Record Android App using SQLite

      June 1, 2025

      When Array uses less memory than Uint8Array (in V8)

      June 1, 2025

      Laravel 12 Starter Kits: Definite Guide Which to Choose

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

      Photobooth is photobooth software for the Raspberry Pi and PC

      June 1, 2025
      Recent

      Photobooth is photobooth software for the Raspberry Pi and PC

      June 1, 2025

      Le notizie minori del mondo GNU/Linux e dintorni della settimana nr 22/2025

      June 1, 2025

      Rilasciata PorteuX 2.1: Novità e Approfondimenti sulla Distribuzione GNU/Linux Portatile Basata su Slackware

      June 1, 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

    7 MagSafe accessories that I recommend every iPhone user should have

    June 1, 2025
    News & Updates

    I replaced my Kindle with an iPad Mini as my ebook reader – 8 reasons why I don’t regret it

    June 1, 2025
    Leave A Reply Cancel Reply

    Continue Reading

    Mysterious Windows 11 “actions” menu appears in latest preview build — here’s what it’s for

    News & Updates

    DeepMind CEO claims Google has the “ingredients” to maintain AI lead over DeepSeek’s “exaggerated” success — after Microsoft CEO Satya Nadella said Google already missed the opportunity to be the “default winner”

    News & Updates

    Rilasciata Linux Mint 22.1 “Xia”: Aggiornamenti e Nuove Funzionalità per un’Esperienza Desktop Migliorata

    Linux

    CVE-2025-39398 – Bellevue Hotel + Bed and Breakfast Booking Calendar Theme Missing Authorization Vulnerability

    Common Vulnerabilities and Exposures (CVEs)
    GetResponse

    Highlights

    CVE-2025-48146 – LupsOnline SEO Flow CSRF Stored XSS

    May 16, 2025

    CVE ID : CVE-2025-48146

    Published : May 16, 2025, 4:15 p.m. | 47 minutes ago

    Description : Cross-Site Request Forgery (CSRF) vulnerability in Michael Lups SEO Flow by LupsOnline allows Stored XSS. This issue affects SEO Flow by LupsOnline: from n/a through 2.2.0.

    Severity: 7.1 | HIGH

    Visit the link for more details, such as CVSS details, affected products, timeline, and more…

    Error’d: All Michael

    June 14, 2024

    How to Use Vue.js Transitions for Smooth UI Animations

    December 26, 2024

    New Banshee Stealer Variant Bypasses Antivirus with Apple’s XProtect-Inspired Encryption

    January 9, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

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