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