Radar Chart

Multi-axis radial comparison.

Speed Quality Cost Reach Ease 70 65 75 68 72 95 40 30 85 45
Balanced profile scores out of 100
MetricScore
Speed70
Quality65
Cost75
Reach68
Ease72
Specialised profile scores out of 100
MetricScore
Speed95
Quality40
Cost30
Reach85
Ease45

html

<div class="radar-chart radar-chart--balanced radar-chart--grid radar-chart--values" style="--accent:#D9C8F0;--sw:2;">
  <svg class="radar-chart__svg" viewBox="0 0 140 135" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Radar chart of a balanced profile across 5 metrics: speed, quality, cost, reach and ease">
    <g class="rc-gridwrap">
      <polygon class="rc-grid" points="70,15 122.3,53 102.3,114.5 37.7,114.5 17.7,53"></polygon>
      <polygon class="rc-grid" points="70,42.5 96.15,61.5 86.15,92.25 53.85,92.25 43.85,61.5"></polygon>
      <line class="rc-spoke" x1="70" y1="70" x2="70" y2="15"></line>
      <line class="rc-spoke" x1="70" y1="70" x2="122.3" y2="53"></line>
      <line class="rc-spoke" x1="70" y1="70" x2="102.3" y2="114.5"></line>
      <line class="rc-spoke" x1="70" y1="70" x2="37.7" y2="114.5"></line>
      <line class="rc-spoke" x1="70" y1="70" x2="17.7" y2="53"></line>
    </g>
    
      <text class="rc-axis-label" x="70" y="8">Speed</text>
      <text class="rc-axis-label" x="131.8" y="49.9">Quality</text>
      <text class="rc-axis-label" x="108.2" y="126">Cost</text>
      <text class="rc-axis-label" x="31.8" y="126">Reach</text>
      <text class="rc-axis-label" x="8.2" y="49.9">Ease</text>
    <polygon class="rc-poly rc-poly--balanced" points="70,31.5 104,58.95 94.25,103.37 48.01,100.26 32.34,57.76"></polygon>
    <polygon class="rc-poly rc-poly--specialised" points="70,17.75 90.92,63.2 79.70,83.35 42.51,107.82 46.47,62.35"></polygon>
    <text class="rc-value rc-value--balanced" x="70" y="20">70</text>
      <text class="rc-value rc-value--balanced" x="112" y="55">65</text>
      <text class="rc-value rc-value--balanced" x="100" y="110">75</text>
      <text class="rc-value rc-value--balanced" x="40" y="107">68</text>
      <text class="rc-value rc-value--balanced" x="24" y="55">72</text>
    <text class="rc-value rc-value--specialised" x="70" y="6">95</text>
      <text class="rc-value rc-value--specialised" x="98" y="60">40</text>
      <text class="rc-value rc-value--specialised" x="82" y="90">30</text>
      <text class="rc-value rc-value--specialised" x="34" y="114">85</text>
      <text class="rc-value rc-value--specialised" x="36" y="58">45</text>
  </svg>
  <div class="rc-srtable-wrap rc-srtable-wrap--balanced">
      <table class="radar-chart__srtable">
        <caption>Balanced profile scores out of 100</caption>
        <thead><tr><th scope="col">Metric</th><th scope="col">Score</th></tr></thead>
        <tbody>
          <tr><td>Speed</td><td>70</td></tr>
          <tr><td>Quality</td><td>65</td></tr>
          <tr><td>Cost</td><td>75</td></tr>
          <tr><td>Reach</td><td>68</td></tr>
          <tr><td>Ease</td><td>72</td></tr>
        </tbody>
      </table></div>
  <div class="rc-srtable-wrap rc-srtable-wrap--specialised">
      <table class="radar-chart__srtable">
        <caption>Specialised profile scores out of 100</caption>
        <thead><tr><th scope="col">Metric</th><th scope="col">Score</th></tr></thead>
        <tbody>
          <tr><td>Speed</td><td>95</td></tr>
          <tr><td>Quality</td><td>40</td></tr>
          <tr><td>Cost</td><td>30</td></tr>
          <tr><td>Reach</td><td>85</td></tr>
          <tr><td>Ease</td><td>45</td></tr>
        </tbody>
      </table></div>
</div>
<style>
.radar-chart { max-width: 320px; font: 11px system-ui, sans-serif; color: #241430; background: #FAF7F2; padding: 12px; box-sizing: border-box; }
.radar-chart__svg { width: 100%; height: auto; display: block; }
.radar-chart .rc-grid { fill: none; stroke: #24143022; stroke-width: 1; }
.radar-chart .rc-spoke { stroke: #24143022; stroke-width: 1; }
.radar-chart .rc-gridwrap { display: none; }
.radar-chart--grid .rc-gridwrap { display: block; }
.radar-chart .rc-axis-label { font-size: 8.5px; fill: #241430; text-anchor: middle; font-weight: 600; }
.radar-chart .rc-poly { fill: var(--accent); fill-opacity: .38; stroke: var(--accent); stroke-width: var(--sw); stroke-linejoin: round; display: none; }
.radar-chart--balanced .rc-poly--balanced { display: block; }
.radar-chart--specialised .rc-poly--specialised { display: block; }
.radar-chart .rc-value { font-size: 7.5px; fill: #241430; text-anchor: middle; font-weight: 700; display: none; }
.radar-chart--balanced.radar-chart--values .rc-value--balanced { display: block; }
.radar-chart--specialised.radar-chart--values .rc-value--specialised { display: block; }
.radar-chart .rc-srtable-wrap { display: none; }
.radar-chart--balanced .rc-srtable-wrap--balanced { display: block; }
.radar-chart--specialised .rc-srtable-wrap--specialised { display: block; }
.radar-chart .rc-srtable { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
</style>

tailwind

<div class="radar-chart-tw radar-chart-tw--balanced radar-chart-tw--grid radar-chart-tw--values" style="--accent:#D9C8F0;--sw:2;">
  <svg class="radar-chart-tw__svg" viewBox="0 0 140 135" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Radar chart of a balanced profile across 5 metrics: speed, quality, cost, reach and ease">
    <g class="rc-gridwrap">
      <polygon class="rc-grid" points="70,15 122.3,53 102.3,114.5 37.7,114.5 17.7,53"></polygon>
      <polygon class="rc-grid" points="70,42.5 96.15,61.5 86.15,92.25 53.85,92.25 43.85,61.5"></polygon>
      <line class="rc-spoke" x1="70" y1="70" x2="70" y2="15"></line>
      <line class="rc-spoke" x1="70" y1="70" x2="122.3" y2="53"></line>
      <line class="rc-spoke" x1="70" y1="70" x2="102.3" y2="114.5"></line>
      <line class="rc-spoke" x1="70" y1="70" x2="37.7" y2="114.5"></line>
      <line class="rc-spoke" x1="70" y1="70" x2="17.7" y2="53"></line>
    </g>
    
      <text class="rc-axis-label" x="70" y="8">Speed</text>
      <text class="rc-axis-label" x="131.8" y="49.9">Quality</text>
      <text class="rc-axis-label" x="108.2" y="126">Cost</text>
      <text class="rc-axis-label" x="31.8" y="126">Reach</text>
      <text class="rc-axis-label" x="8.2" y="49.9">Ease</text>
    <polygon class="rc-poly rc-poly--balanced" points="70,31.5 104,58.95 94.25,103.37 48.01,100.26 32.34,57.76"></polygon>
    <polygon class="rc-poly rc-poly--specialised" points="70,17.75 90.92,63.2 79.70,83.35 42.51,107.82 46.47,62.35"></polygon>
    <text class="rc-value rc-value--balanced" x="70" y="20">70</text>
      <text class="rc-value rc-value--balanced" x="112" y="55">65</text>
      <text class="rc-value rc-value--balanced" x="100" y="110">75</text>
      <text class="rc-value rc-value--balanced" x="40" y="107">68</text>
      <text class="rc-value rc-value--balanced" x="24" y="55">72</text>
    <text class="rc-value rc-value--specialised" x="70" y="6">95</text>
      <text class="rc-value rc-value--specialised" x="98" y="60">40</text>
      <text class="rc-value rc-value--specialised" x="82" y="90">30</text>
      <text class="rc-value rc-value--specialised" x="34" y="114">85</text>
      <text class="rc-value rc-value--specialised" x="36" y="58">45</text>
  </svg>
  <div class="rc-srtable-wrap rc-srtable-wrap--balanced">
      <table class="radar-chart-tw__srtable">
        <caption>Balanced profile scores out of 100</caption>
        <thead><tr><th scope="col">Metric</th><th scope="col">Score</th></tr></thead>
        <tbody>
          <tr><td>Speed</td><td>70</td></tr>
          <tr><td>Quality</td><td>65</td></tr>
          <tr><td>Cost</td><td>75</td></tr>
          <tr><td>Reach</td><td>68</td></tr>
          <tr><td>Ease</td><td>72</td></tr>
        </tbody>
      </table></div>
  <div class="rc-srtable-wrap rc-srtable-wrap--specialised">
      <table class="radar-chart-tw__srtable">
        <caption>Specialised profile scores out of 100</caption>
        <thead><tr><th scope="col">Metric</th><th scope="col">Score</th></tr></thead>
        <tbody>
          <tr><td>Speed</td><td>95</td></tr>
          <tr><td>Quality</td><td>40</td></tr>
          <tr><td>Cost</td><td>30</td></tr>
          <tr><td>Reach</td><td>85</td></tr>
          <tr><td>Ease</td><td>45</td></tr>
        </tbody>
      </table></div>
</div>
<style>
.radar-chart-tw { max-width: 320px; font: 11px system-ui, sans-serif; color: #241430; background: #FAF7F2; padding: 12px; box-sizing: border-box; }
.radar-chart-tw__svg { width: 100%; height: auto; display: block; }
.radar-chart-tw .rc-grid { fill: none; stroke: #24143022; stroke-width: 1; }
.radar-chart-tw .rc-spoke { stroke: #24143022; stroke-width: 1; }
.radar-chart-tw .rc-gridwrap { display: none; }
.radar-chart-tw--grid .rc-gridwrap { display: block; }
.radar-chart-tw .rc-axis-label { font-size: 8.5px; fill: #241430; text-anchor: middle; font-weight: 600; }
.radar-chart-tw .rc-poly { fill: var(--accent); fill-opacity: .38; stroke: var(--accent); stroke-width: var(--sw); stroke-linejoin: round; display: none; }
.radar-chart-tw--balanced .rc-poly--balanced { display: block; }
.radar-chart-tw--specialised .rc-poly--specialised { display: block; }
.radar-chart-tw .rc-value { font-size: 7.5px; fill: #241430; text-anchor: middle; font-weight: 700; display: none; }
.radar-chart-tw--balanced.radar-chart-tw--values .rc-value--balanced { display: block; }
.radar-chart-tw--specialised.radar-chart-tw--values .rc-value--specialised { display: block; }
.radar-chart-tw .rc-srtable-wrap { display: none; }
.radar-chart-tw--balanced .rc-srtable-wrap--balanced { display: block; }
.radar-chart-tw--specialised .rc-srtable-wrap--specialised { display: block; }
.radar-chart-tw .rc-srtable { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
</style>

react

export function RadarChart({
  profile = "balanced",
  showGrid = true,
  showValues = true,
  strokeWidth = 2,
  accent = "#D9C8F0",
}) {
  const axes = [
    { label: "Speed", lx: 70, ly: 8 },
    { label: "Quality", lx: 131.8, ly: 49.9 },
    { label: "Cost", lx: 108.2, ly: 126 },
    { label: "Reach", lx: 31.8, ly: 126 },
    { label: "Ease", lx: 8.2, ly: 49.9 },
  ];
  const profiles = {
    balanced: {
      points: "70,31.5 104,58.95 94.25,103.37 48.01,100.26 32.34,57.76",
      values: [["70", "20", "70"], ["112", "55", "65"], ["100", "110", "75"], ["40", "107", "68"], ["24", "55", "72"]],
      caption: "Balanced profile scores out of 100",
    },
    specialised: {
      points: "70,17.75 90.92,63.2 79.70,83.35 42.51,107.82 46.47,62.35",
      values: [["70", "6", "95"], ["98", "60", "40"], ["82", "90", "30"], ["34", "114", "85"], ["36", "58", "45"]],
      caption: "Specialised profile scores out of 100",
    },
  };
  const active = profiles[profile] ?? profiles.balanced;
  return (
    <div style={{ maxWidth: 320, padding: 12, font: "11px system-ui, sans-serif", color: "#241430", background: "#FAF7F2", boxSizing: "border-box" }}>
      <svg viewBox="0 0 140 135" style={{ width: "100%", height: "auto", display: "block" }}
        role="img" aria-label={`Radar chart of a ${profile} profile across 5 metrics: speed, quality, cost, reach and ease`}>
        {showGrid && (
          <g fill="none" stroke="#24143022" strokeWidth={1}>
            <polygon points="70,15 122.3,53 102.3,114.5 37.7,114.5 17.7,53" />
            <polygon points="70,42.5 96.15,61.5 86.15,92.25 53.85,92.25 43.85,61.5" />
            {axes.map((a, i) => {
              const outer = ["70,15", "122.3,53", "102.3,114.5", "37.7,114.5", "17.7,53"][i].split(",");
              return <line key={i} x1={70} y1={70} x2={outer[0]} y2={outer[1]} />;
            })}
          </g>
        )}
        {axes.map((a, i) => (
          <text key={i} x={a.lx} y={a.ly} fontSize={8.5} textAnchor="middle" fontWeight={600} fill="#241430">{a.label}</text>
        ))}
        <polygon points={active.points} fill={accent} fillOpacity={0.38} stroke={accent} strokeWidth={strokeWidth} strokeLinejoin="round" />
        {showValues && active.values.map(([x, y, v], i) => (
          <text key={i} x={x} y={y} fontSize={7.5} textAnchor="middle" fontWeight={700} fill="#241430">{v}</text>
        ))}
      </svg>
    </div>
  );
}