You could use an SVG. You can create path elements with arcs and stroke them in the respective color.
Arcs are fairly complicated with many parameters:
A rx ry x-axis-rotation large-arc-flag sweep-flag x y a rx ry x-axis-rotation large-arc-flag sweep-flag dx dy
I would recommend reading the MDN documentation or the spec.
Example of a segment:
<svg width="320" height="320" xmlns="http://www.w3.org/2000/svg"> <path d="M 50 50 a 50 50 0 0 1 50 0" stroke="black" stroke-width="20" fill="none"/> </svg>
The easiest method is probably calculating the angles (start/end) and converting from polar to cartesian. The paths only need two commands: Absolute move to start location & absolute arc to end location.
Full example with clickable segments and keyboard support for additional accessibility (could still be improved, e.g. by supplying screen reader texts):
<script> let radius = 150; let stroke = 20; let gap = 5; let segments = [ '#ff0000', '#00ff00', '#0000ff', ]; function getCoordinates(i, gap) { const angleDelta = 360 / segments.length; const start = polarToCartesian(radius, i * angleDelta + gap); const end = polarToCartesian(radius, i * angleDelta + angleDelta); return { start, end }; } const polarToCartesian = (r, angle) => { return { x: r * Math.cos(rad(angle)), y: r * Math.sin(rad(angle)), } } const rad = x => x * Math.PI / 180; const onClick = i => alert('Segment ' + i); </script> <div> <svg width="320" height="320" xmlns="http://www.w3.org/2000/svg"> <g transform="translate(160, 160) rotate({-90 - gap/2})"> {#each segments as segment, i (i)} {@const { start, end } = getCoordinates(i, gap)} <path d="M {start.x} {start.y} A {radius} {radius} 0 0 1 {end.x} {end.y}" stroke={segment} stroke-width={stroke} fill="none" tabindex={0} on:keydown={e => { if (e.key == 'Enter') onClick(i); }} on:click={() => onClick(i)} /> {/each} </g> </svg> </div>
REPL