import styles from "../styles/radarChart.module.scss";
const ALLOW_VERTEX_COUNTS = [3, 5, 6, 7];
const MIN_SCORE_VALUE = 0;
const MAX_SCORE_VALUE = 10;
export default function RadarChart({
let scoreValues: number[] = [];
if (!ALLOW_VERTEX_COUNTS.includes(scores.length)) throw new Error();
scoreValues = scores.map(({ value }) => {
if (value < MIN_SCORE_VALUE || value > MAX_SCORE_VALUE) throw new Error();
<div className={styles.block}>
xmlns="http://www.w3.org/2000/svg"
viewBox={`0 0 ${BASE_SIZE} ${BASE_SIZE}`}
<title>{ariaLabel}</title>
{createPaths(ariaLabel, baseColor, scoreValues)}
<dl className={styles.scores}>
<div className={styles.score}>
<dd className={styles.value}>{score.value.toFixed(1)}</dd>
const vertexCount = scoreValues.length;
const sizes: number[] = [];
for (let i = vertexCount; i > 0; i--) {
sizes.push((BASE_SIZE * i) / vertexCount);
const allPentagonOffsets: Offset[][] = [];
for (const size of sizes) {
const pentagonOffsets: Offset[] = [];
for (let i = 0; i < vertexCount; i++) {
pentagonOffsets.push(getOffset(vertexCount, size, i));
allPentagonOffsets.push(pentagonOffsets);
const outwardLinePaths: JSX.Element[] = [];
allPentagonOffsets[0].forEach((offset, index) => {
d={`M ${BASE_SIZE / 2} ${BASE_SIZE / 2} L ${offset.x} ${offset.y}`}
const pentagonLinePaths: JSX.Element[] = [];
allPentagonOffsets.forEach((allPentagonOffset, index) => {
for (let i = 0; i <= allPentagonOffset.length; i++) {
if (i !== 0) pentagonPath += " L ";
i === allPentagonOffset.length
pentagonPath += `${offset.x} ${offset.y}`;
<path d={pentagonPath} fill="none" stroke="#dce5eb" key={index} />
const vertexPaths: JSX.Element[] = [];
for (let i = 0; i <= vertexCount; i++) {
if (i !== 0) pentagonD += " L ";
(scoreValues[i] / MAX_SCORE_VALUE) * BASE_SIZE,
<circle cx={offset.x} cy={offset.y} r="3" fill={baseColor} key={i} />
(scoreValues[0] / MAX_SCORE_VALUE) * BASE_SIZE,
pentagonD += `${offset.x} ${offset.y}`;
<path d={pentagonD} fill={`${baseColor}30`} stroke={baseColor} />
function getOffset(vertexCount: number, size: number, i: number): Offset {
(size / 2) * Math.sin(((2 * Math.PI) / vertexCount) * i)
(size / 2) * Math.cos(((2 * Math.PI) / vertexCount) * i)