DEV Community

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

Posted on • Edited on

How to create perfect CSS circle sectors

Building a jackpot roulette

I've recently had to tackle building a jackpot roulette using CSS. Because each sector had to be interactive (e.g: change border color and background on :hover), I had to dig deeper than usual in my long-forgotten trigonometry bag of tricks. 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.
  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.


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 defining radiuses 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 quite 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.


Drawing the 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 the text orientation).


Clipping: aesthetic meets precision

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

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

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

Sectors clipped wrong

The fix? Calculating the exact intersection between the sector and the circle, and clipping from that point 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!

SurveyJS custom survey software

JavaScript UI Libraries for Surveys and Forms

SurveyJS lets you build a JSON-based form management system that integrates with any backend, giving you full control over your data and no user limits. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more.

Learn more

Top comments (0)