DEV Community

Cover image for How to create perfect CSS circle sectors
Andrei Gheorghiu
Andrei Gheorghiu

Posted on

How to create perfect CSS circle sectors

Spinning the wheel: building a jackpot roulette

As a CSS enthusiast, you’ve probably dabbled in gradients, animations, and maybe even a bit of trigonometry. But have you ever built a responsive, interactive CSS jackpot roulette? Recently, I had to tackle just that, and it turned out to be an challenging exercise in geometry, creativity, and a sprinkle of frontend magic. Let me take you through the process step by step.


The challenge: responsive highlightable sectors

The roulette needed to:

  1. Highlight the sectors as the needle pointed at them.
  2. Be fully responsive, adapting to screen size.
  3. Allow a variable number of sectors—a critical feature that ruled out using static images or SVGs.

This meant one thing: we’d need to draw the sectors dynamically and calculate their shapes and positions geometrically. Intrigued? Let’s dive in.


The setup: rotating spans around the center

To start, I used <span> elements to represent the sectors, rotating each around the center of the circle. Each sector would be clipped along its radius to avoid overlap. You can take a peek at my initial setup here.

For the rotation, I iterated through the sectors, incrementally rotating them by:

transform: rotate(calc(360deg / number_of_sectors * index));
Enter fullscreen mode Exit fullscreen mode

At this stage, everything was pretty simple. I had a circle, some rotated spans of text and rays in between them. But in order to highlight a sector I needed to draw its precise shape.


The math: highlighting a sector

The first thing I needed to calculate was the sector height based on the angle between its boundaries and the radius. The formula for the distance between two points on a circle, given the radius and angle, is:

2 * radius * Math.sin(θ / 2)
Enter fullscreen mode Exit fullscreen mode

Where θ is the angle in radians. For our evenly spaced sectors, θ becomes:

2 * Math.PI / number_of_sectors
Enter fullscreen mode Exit fullscreen mode

Plugging that in:

2 * radius * Math.sin(Math.PI / number_of_sectors)
Enter fullscreen mode Exit fullscreen mode

This gave me the precise height of each sector (or, should I say, its dimension on the axis perpendicular to its text orientation).


The clipping: precision meets aesthetic

Initially, I tried clipping each sector from the top-right corner to the left-center and then to the bottom-right corner:

clip-path: polygon(100% 0, 0 50%, 100% 100%);
Enter fullscreen mode Exit fullscreen mode

This worked… sort of. With many sectors, it was passable. But with fewer sectors—say three—it was, glaringly wrong:

Sectors clipped wrong

The fix? Calculating the exact intersection points between the sector and the circle, clipping from there to the center. After some research, I found the formulas for the segments formed on a horizontal ray by the line connecting the two points on the circle:

Image for segment formulas

  • Segment close to the center:
const m = radius * (1 - Math.cos(θ / 2))
Enter fullscreen mode Exit fullscreen mode
  • Segment farther from the center:
const n = radius * Math.cos(θ / 2)
Enter fullscreen mode Exit fullscreen mode

Just like above, θ is in radians:

2 * Math.PI / number_of_sectors
Enter fullscreen mode Exit fullscreen mode

The ratio between the two segments gave me the exact clipping point:

const clipPosition = 100 * (m + n) / m
Enter fullscreen mode Exit fullscreen mode

Plugging in to our example:

const clipPosition = Math.cos(Math.PI / number_of_sectors) * 100
Enter fullscreen mode Exit fullscreen mode

...which led to the final clip-path:

  clip-path: polygon(
    100% 0,
    ${clipPosition}% 0,
    0 50%,
    ${clipPosition}% 100%,
    100% 100%
  );
Enter fullscreen mode Exit fullscreen mode

Now the sectors aligned perfectly:
Sectors clipped correctly


Bringing it to life

I implemented the spinning logic using Vue. Clicking the center produces a random spin, while clicking on a sector spins the roulette a few times and then lands on that sector. You can check out the result here.
Code available here.


Closing thoughts

Hope you enjoyed this exploration. What’s the next CSS challenge you’re tackling? Let me know — I’m always eager to swap tricks and ideas!

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

Top comments (0)

Billboard image

Create up to 10 Postgres Databases on Neon's free plan.

If you're starting a new project, Neon has got your databases covered. No credit cards. No trials. No getting in your way.

Try Neon for Free →

👋 Kindness is contagious

Engage with a sea of insights in this enlightening article, highly esteemed within the encouraging DEV Community. Programmers of every skill level are invited to participate and enrich our shared knowledge.

A simple "thank you" can uplift someone's spirits. Express your appreciation in the comments section!

On DEV, sharing knowledge smooths our journey and strengthens our community bonds. Found this useful? A brief thank you to the author can mean a lot.

Okay