Apache ECharts レーダーチャート入門|JavaScriptで作る複数系列の比較グラフ
この記事は、Apache EChartsを使ったレーダーチャートの実装解説です。ECharts自体の概要やセットアップ方法は、過去の折れ線グラフ記事でまとめています。EChartsが初めての方は、先にそちらをお読みください。



この記事では、レーダーチャートの実装を中心に、デザイン調整・レスポンシブ対応・ボタン切り替え、そして複数系列を重ねた比較表示まで一通り解説します。
完成デモを触ってみよう
まずは完成デモを見て、最終的にどんな形を目指すか確認してみましょう。ボタンを押すと、ページ別のUX/UI評価レーダーチャート(リニューアル前後の比較・※数値はサンプル)を表示・切り替えできます。
ボタンを押すと
グラフが表示されます
PC幅で見ると、レーダーがコンテナの中央に大きく配置され、6つの評価軸(使いやすさ・視認性・情報整理・デザイン性・アクセシビリティ・レスポンシブ対応)が等間隔に並びます。「リニューアル前(青)」と「リニューアル後(ピンク)」の2つの多角形が重なって描画され、塗りつぶしの透明度を低めにしているので、両方の形状が判別できる見た目になっています。
スマホ幅で見ると、レーダーは小さめに表示されます。また、長めの項目名(「アクセシビリティ」「レスポンシブ対応」)は画面外にはみ出さないよう、PC用とは別に改行入りの軸ラベルを用意しています。
コード全体(HTML / CSS / JavaScript)
ここからは実際にコードを見ていきます。HTML・CSSはコードとポイントをあわせて解説します。JavaScriptは全体を掲載してから、次のセクションで詳しく解説します。
HTML
<!-- 切り替えボタンのエリア -->
<div class="chart-switcher">
<button id="serviceChartBtn" class="chart-tab" type="button">サービス紹介ページ</button>
<button id="contactChartBtn" class="chart-tab" type="button">お問い合わせページ</button>
</div>
<!-- グラフ表示エリア -->
<div id="echartsContainer" class="echarts-container">
<!-- グラフが描画される前に表示するプレースホルダー -->
<div class="chart-placeholder">
<p class="chart-placeholder-text">ボタンを押すと<br>グラフが表示されます</p>
</div>
</div>
HTMLはシンプルな2つのブロックで構成されています。
切り替えボタンのエリアには、id を付けた2つの button を並べています。この id をJavaScript側で取得して、クリックイベントに紐づけます。
グラフ表示エリアは id=”echartsContainer” の div で、EChartsはこの要素をコンテナとしてグラフを描画します。初期状態ではプレースホルダー(「ボタンを押すとグラフが表示されます」)を表示しておき、ボタンを押したタイミングでグラフに置き換わります。
EChartsのグラフ描画に必須なのは、コンテナとなる div(id=”echartsContainer”)だけです。ボタンやプレースホルダーは今回のデモ用の実装なので、ただグラフを表示したいだけであれば、コンテナのみで問題ありません。
CSS
/* ===============================
切り替えボタンエリア
=============================== */
.chart-switcher {
display: flex;
justify-content: center;
}
@media (min-width: 768px) {
.chart-switcher {
gap: 30px;
}
}
@media (max-width: 767px) {
.chart-switcher {
gap: 15px;
}
}
/* ボタンの基本スタイル */
.chart-tab {
display: block;
position: relative;
border: none;
border-radius: 5px;
padding: 15px 0;
background: #c11a51;
color: #fff;
font: inherit;
font-weight: 700;
line-height: 1.4;
text-align: center;
cursor: pointer;
appearance: none;
transition: background-color 300ms, color 300ms;
}
@media (min-width: 768px) {
.chart-tab {
width: 200px;
font-size: 16px;
}
}
@media (max-width: 767px) {
.chart-tab {
flex: 1;
width: 100%;
font-size: 15px;
}
}
/* 選択中ボタンの下に表示する三角形 */
.chart-tab::before {
content: "";
display: block;
position: absolute;
left: 50%;
bottom: -12px;
width: 15px;
height: 12px;
background: #f9e8ed;
clip-path: polygon(0% 0%, 100% 0%, 50% 100%);
transform: translateX(-50%);
opacity: 0;
transition: opacity 300ms;
}
/* ホバー時(PCのみ) */
@media (min-width: 768px) {
.chart-tab:hover {
background: #890631;
}
}
/* 選択中のボタン */
.chart-tab.is-active {
background: #f9e8ed;
color: #c11a51;
pointer-events: none;
}
/* アクティブ時だけ三角を表示 */
.chart-tab.is-active::before {
opacity: 1;
}
/* ===============================
グラフエリア
=============================== */
@media (min-width: 768px) {
.echarts-container {
height: 450px; /* PCのチャート高さ */
margin-top: 40px;
}
}
@media (max-width: 767px) {
.echarts-container {
height: 380px; /* SPのチャート高さ */
margin-top: 30px;
}
}
/* プレースホルダー(グラフ描画前の表示) */
.chart-placeholder {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
border-radius: 5px;
background-color: rgba(68, 102, 206, 0.09);
text-align: center;
}
.chart-placeholder-text {
color: #31345e;
font-weight: 700;
line-height: 1.6;
opacity: 0.5;
}
@media (min-width: 768px) {
.chart-placeholder-text {
font-size: 26px;
}
}
@media (max-width: 767px) {
.chart-placeholder-text {
font-size: 20px;
}
}
CSSも大きく2つのブロックに分かれています。
ボタンエリアは display: flex で横並びにしています。is-active クラスが付いたボタンは背景色と文字色が反転し、::before 擬似要素で下向きの三角形を表示します。pointer-events: none を合わせて指定することで、選択中のボタンを押せない状態にしています。
グラフエリアはEChartsの仕様上、高さをCSSで指定する必要があります。高さを指定しないとグラフが表示されないため、必ず設定しておきましょう。
HTMLと同様に、EChartsのグラフ描画に必須なCSSはコンテナの高さ指定だけです。ボタンやプレースホルダーのスタイルは今回のデモ用の実装です。
JavaScript
document.addEventListener('DOMContentLoaded', () => {
const btnService = document.getElementById('serviceChartBtn');
const btnContact = document.getElementById('contactChartBtn');
const chartDom = document.getElementById('echartsContainer');
// コンテナが存在しない場合は処理を止める(エラー防止)
if (!chartDom) return;
let myChart = null;
// 評価項目(両グラフ共通)。レーダー軸の名前と最大値を定義
// SPでは長い項目名が画面外にはみ出すため、SP用は改行入りで別途用意
const indicatorsPC = [
{ name: '使いやすさ', max: 10 },
{ name: '視認性', max: 10 },
{ name: '情報整理', max: 10 },
{ name: 'デザイン性', max: 10 },
{ name: 'アクセシビリティ', max: 10 },
{ name: 'レスポンシブ対応', max: 10 }
];
const indicatorsSP = [
{ name: '使いやすさ', max: 10 },
{ name: '視認性', max: 10 },
{ name: '情報整理', max: 10 },
{ name: 'デザイン性', max: 10 },
{ name: 'アクセシ\nビリティ', max: 10 },
{ name: 'レスポンシブ\n対応', max: 10 }
];
// サービス紹介ページのリニューアル前後スコア(indicatorsと同じ順序)
const serviceBefore = [6, 5, 5, 6, 7, 7];
const serviceAfter = [8, 9, 9, 9, 8, 8];
// お問い合わせページのリニューアル前後スコア(indicatorsと同じ順序)
const contactBefore = [5, 6, 7, 6, 4, 5];
const contactAfter = [9, 7, 8, 7, 9, 9];
// 2つのグラフで共通する設定をまとめたベースoption
const baseOption = {
// 系列の色(data配列の順番に適用される)
// 0: リニューアル前(青)/1: リニューアル後(ピンク)
color: ['#4466ce', '#c11a51'],
// グラフ全体の文字スタイル
textStyle: {
color: '#333',
fontFamily: '"Noto Sans JP", sans-serif',
fontSize: 14,
fontWeight: 300
},
// タイトルの共通設定(タイトル本文はページごと、フォントサイズはmediaで切り替え)
title: {
top: 0,
left: 'center',
textStyle: {
lineHeight: 26
}
},
// 凡例の共通設定(itemGapはmedia側で切り替え)
legend: {
bottom: 0,
left: 'center',
orient: 'horizontal',
itemWidth: 16,
itemHeight: 12,
selectedMode: false
},
// レーダー軸の共通設定(indicator・センター・半径・フォントサイズはmedia側で切り替え)
radar: {
// 軸ラベル(項目名)のスタイル
axisName: {
lineHeight: 17
},
// 軸線(中心から外側に伸びる線)
axisLine: {
lineStyle: { color: 'rgba(0, 0, 0, 0.15)' }
},
// 分割線(同心多角形のライン)
splitLine: {
lineStyle: { color: 'rgba(0, 0, 0, 0.15)' }
},
// 分割エリア(同心多角形の塗り)。薄い縞模様にして背景を整える
splitArea: {
show: true,
areaStyle: {
color: ['rgba(248, 248, 252, 0.5)', 'rgba(255, 255, 255, 0.5)']
}
}
},
// チャートの幅に応じてレイアウトを切り替え
media: [
{
// PC幅:各要素を標準サイズで表示
query: { minWidth: 740 },
option: {
title: { textStyle: { fontSize: 18 } },
legend: {
itemGap: 30
},
radar: {
indicator: indicatorsPC, // 軸の項目(PC用)
center: ['50%', '230px'], // 中心位置 [水平, 垂直]
radius: '140px', // 半径(レーダーの大きさ)
axisName: { fontSize: 14 } // 軸ラベルのフォントサイズ
}
}
},
{
// スマホ幅:各要素を縮小して表示
query: { maxWidth: 739 },
option: {
title: { textStyle: { fontSize: 16 } },
legend: {
itemGap: 20
},
radar: {
indicator: indicatorsSP, // 軸の項目(SP用)
center: ['50%', '205px'], // 中心位置 [水平, 垂直]
radius: '95px', // 半径(レーダーの大きさ)
axisName: { fontSize: 12 } // 軸ラベルのフォントサイズ
}
}
}
]
};
// サービス紹介ページのグラフoption(baseOptionに差分を追加)
const optionService = {
...baseOption,
series: [{
type: 'radar',
lineStyle: { width: 1 },
areaStyle: { opacity: 0.2 },
symbolSize: 5,
data: [
{
value: serviceBefore,
name: 'リニューアル前',
},
{
value: serviceAfter,
name: 'リニューアル後',
}
]
}],
// タイトル本文はmediaで媒体ごとに指定(SP→PC切り替え時に改行が戻るよう両方を明示)
media: [
...baseOption.media,
{
query: { minWidth: 740 },
option: {
title: { text: 'サービス紹介ページ UX/UI評価(リニューアル前後)' }
}
},
{
query: { maxWidth: 739 },
option: {
title: { text: 'サービス紹介ページ UX/UI評価\n(リニューアル前後)' }
}
}
]
};
// お問い合わせページのグラフoption(baseOptionに差分を追加)
const optionContact = {
...baseOption,
series: [{
type: 'radar',
lineStyle: { width: 1 },
areaStyle: { opacity: 0.2 },
symbolSize: 5,
data: [
{
value: contactBefore,
name: 'リニューアル前',
},
{
value: contactAfter,
name: 'リニューアル後',
}
]
}],
// タイトル本文はmediaで媒体ごとに指定(SP→PC切り替え時に改行が戻るよう両方を明示)
media: [
...baseOption.media,
{
query: { minWidth: 740 },
option: {
title: { text: 'お問い合わせページ UX/UI評価(リニューアル前後)' }
}
},
{
query: { maxWidth: 739 },
option: {
title: { text: 'お問い合わせページ UX/UI評価\n(リニューアル前後)' }
}
}
]
};
// グラフを描画し、押したボタンをアクティブにする関数
function showChart(optionToSet, activeBtn) {
// まだ初期化していない場合のみecharts.init()を呼ぶ(二重初期化防止)
if (!myChart) {
myChart = echarts.init(chartDom);
}
// 第2引数のtrueで前の設定を残さず新しいoptionに切り替える
myChart.setOption(optionToSet, true);
// ボタンのアクティブ状態を切り替える
[btnService, btnContact].forEach(btn => btn.classList.remove('is-active'));
activeBtn.classList.add('is-active');
}
btnService.addEventListener('click', () => showChart(optionService, btnService));
btnContact.addEventListener('click', () => showChart(optionContact, btnContact));
// 画面サイズ変更時にグラフを再描画(描画済みの場合のみ)
window.addEventListener('resize', () => {
if (myChart) myChart.resize();
});
});
JavaScript のポイント解説
JavaScript の全体構造
このコードは、大きく4つのブロックで構成されています。
| ブロック | 役割 |
|---|---|
| baseOption | 2つのグラフで共通する設定をまとめたベース |
| optionService と optionContact | ページごとのグラフ設定(baseOption に差分を追加) |
| showChart と addEventListener | グラフを描画してボタンのアクティブ状態を切り替える関数と、ボタンのクリックイベントの紐付け |
| resize | 画面サイズ変更時にグラフを再描画する処理 |
baseOption を土台にして、ページごとの差分(タイトルテキスト・データ)を乗せた optionService / optionContact を用意し、ボタンを押したときに showChart で切り替える——これがコード全体の基本的な流れです。
1)baseOption:共通設定をまとめる
const baseOption = {
color: [ ... ],
textStyle: { ... },
title: { ... },
legend: { ... },
radar: { ... },
media: [ ... ]
};2つのグラフで共通する設定——系列の色・文字スタイル・タイトル設定・凡例・レーダー軸の見た目・レスポンシブ——をすべて baseOption にまとめています。タイトルテキストとデータ(series)はページごとに異なるため、baseOption には含めていません。グラフの数が増えても、baseOption を変えれば全体に反映されるので管理が楽になります。
baseOption の中身を項目ごとに見ていきます。
color
color: ['#4466ce', '#c11a51']レーダーの各系列に使う色を、配列で順番に指定します。series.data 配列の順序に対応して、最初の系列に #4466ce(青)、次の系列に #c11a51(ピンク)が割り当てられます。
今回は series.data に「リニューアル前 → リニューアル後」の順で並べているため、リニューアル前が青、リニューアル後がピンクで描画されます。
textStyle
textStyle: {
color: '#333',
fontFamily: '"Noto Sans JP", sans-serif',
fontSize: 14,
fontWeight: 300
}グラフ内の文字(タイトル・凡例・軸ラベルなど)に共通で効く文字スタイルです。色・フォント・サイズ・ウェイトをサイトのデザインに合わせて指定します。
title
title: {
top: 0,
left: 'center',
textStyle: {
lineHeight: 26
}
}タイトルの共通設定です。top: 0 でコンテナの上端、left: 'center' で水平方向の中央に配置します。textStyle.lineHeight: 26 は、スマホ幅でタイトルが2行になったときの行間です。
タイトルテキスト(text プロパティ)とフォントサイズは別の場所で指定します。タイトルテキストはページごとに違うため optionService / optionContact 側の media で、フォントサイズはPC/SPで違うため baseOption.media 側で指定します。
legend
legend: {
bottom: 0,
left: 'center',
orient: 'horizontal',
itemWidth: 16,
itemHeight: 12,
selectedMode: false
}凡例の設定です。bottom: 0、left: 'center' でコンテナの下中央に配置し、orient: 'horizontal' で横並びにします。itemWidth / itemHeight は凡例アイコンのサイズです。
selectedMode: false は、凡例クリックによる項目の表示・非表示の切り替えを無効にする設定です。今回は2系列とも常に見えていてほしいので無効にしています。
凡例どうしの間隔(itemGap)はPC/SPで変えたいため、media 側で指定しています。
radar
radar: {
axisName: {
lineHeight: 17
},
axisLine: {
lineStyle: { color: 'rgba(0, 0, 0, 0.15)' }
},
splitLine: {
lineStyle: { color: 'rgba(0, 0, 0, 0.15)' }
},
splitArea: {
show: true,
areaStyle: {
color: ['rgba(248, 248, 252, 0.5)', 'rgba(255, 255, 255, 0.5)']
}
}
}レーダー軸の共通設定です。
axisName は軸ラベル(項目名)のスタイルです。lineHeight: 17 はスマホで項目名が2行に改行されたときの行間です。
axisLine は中心から外側に伸びる軸線、splitLine は同心多角形の分割線です。どちらも控えめな薄いグレーで指定しています。
splitArea は分割エリアの塗りです。show: true で表示を有効にして、color に2色の配列を渡すことで、同心多角形が交互に色違いの帯になります。今回はほぼ白の2色を指定して、目立たない縞模様にしてレーダー全体の背景を整えています。
軸の項目自体(indicator)、レーダーの位置(center)、大きさ(radius)、軸ラベルのフォントサイズはPC/SPで変えたいため、media 側で指定しています。
media
media: [
{
query: { minWidth: 740 },
option: {
title: { textStyle: { fontSize: 18 } },
legend: { itemGap: 30 },
radar: {
indicator: indicatorsPC,
center: ['50%', '230px'],
radius: '140px',
axisName: { fontSize: 14 }
}
}
},
{
query: { maxWidth: 739 },
option: {
title: { textStyle: { fontSize: 16 } },
legend: { itemGap: 20 },
radar: {
indicator: indicatorsSP,
center: ['50%', '205px'],
radius: '95px',
axisName: { fontSize: 12 }
}
}
}
]media は、チャートの幅に応じてレイアウトを切り替えるための仕組みです。CSSのメディアクエリに近い感覚で使えますが、判定するのは「画面幅」ではなく「チャート(コンテナ)の幅」です。
PC幅とスマホ幅で、それぞれ次の要素を切り替えています。
- タイトルのフォントサイズ(PC:18px、SP:16px)
- 凡例の項目間隔(PC:30px、SP:20px)
- レーダーの軸の項目(indicator:PC用・SP用)
- レーダーの中心位置(center)
- レーダーの半径(radius)
- 軸ラベルのフォントサイズ(PC:14px、SP:12px)
軸の項目自体にPC/SPで違いがあるのは、長めの項目名がスマホ幅で画面外にはみ出すのを防ぐためです。SP用は次のように、長い項目名に改行を入れています。
const indicatorsPC = [
{ name: '使いやすさ', max: 10 },
...
{ name: 'アクセシビリティ', max: 10 },
{ name: 'レスポンシブ対応', max: 10 }
];
const indicatorsSP = [
{ name: '使いやすさ', max: 10 },
...
{ name: 'アクセシ\nビリティ', max: 10 },
{ name: 'レスポンシブ\n対応', max: 10 }
];indicator は「軸の項目と最大値」を定義する配列です。{ name, max } のオブジェクトを並べることで、各軸の項目名と上限値(今回は全軸10点満点)を指定します。ECharts は name 内の \n を改行として描画してくれるため、これで2行表示にできます。
2)optionService と optionContact:差分だけを上書きする
const optionService = {
...baseOption,
series: [ ... ],
media: [ ... ]
};スプレッド構文(...baseOption)で共通設定を継承して、その上から series と media だけ書き換えるパターンです。サービス紹介ページ用とお問い合わせページ用で、それぞれ違う部分だけ差分として書きます。
optionService の中身を項目ごとに見ていきます。
series
series: [{
type: 'radar',
lineStyle: { width: 1 },
areaStyle: { opacity: 0.2 },
symbolSize: 5,
data: [
{ value: serviceBefore, name: 'リニューアル前' },
{ value: serviceAfter, name: 'リニューアル後' }
]
}]実際に描画するレーダーチャートの設定です。
type: 'radar' でレーダーチャートを描画することを示します。
lineStyle は外周線のスタイル、areaStyle は内部の塗りつぶし、symbolSize は各頂点のドットのサイズです。これらを series レベル(data の外側)に書くと、data 配列のすべての系列に共通で適用されます。
data は実際の値です。{ value: 値の配列, name: 系列名 } のオブジェクトを、系列の数だけ並べます。value の配列は indicator と同じ順序で値を入れます。
ここで意識しておきたいのが、data 配列の順序です。
レーダーチャートで複数系列を重ね描画したとき、重なり順は data 配列の順序で決まります。先に書いたものが下層、後に書いたものが上層に描画されます。今回は「リニューアル前 → リニューアル後」の順なので、リニューアル後が手前に描画されます。
リニューアル後のほうが面積の大きい多角形になりますが、areaStyle.opacity を 0.2 と低めにしているため、奥にあるリニューアル前の形状も判別できる見た目になります。
media
media: [
...baseOption.media,
{
query: { minWidth: 740 },
option: {
title: { text: 'サービス紹介ページ UX/UI評価(リニューアル前後)' }
}
},
{
query: { maxWidth: 739 },
option: {
title: { text: 'サービス紹介ページ UX/UI評価\n(リニューアル前後)' }
}
}
]タイトルテキストを、PC幅とSP幅で切り替えるための media 設定です。
...baseOption.media で共通設定を展開した後、ページごとのタイトルテキスト上書きを2つ追加しています。PC幅では1行のタイトル、SP幅では「(リニューアル前後)」の直前で改行を入れたタイトルにします。タイトルテキストは ECharts でも \n で改行できます。
スマホ幅でタイトルを2行にしているのは、横幅の狭い画面で1行に収まらないからです。改行位置を明示的にコントロールしておくと、長いタイトルが画面外にはみ出すのを防げます。
optionContact も同じ構造で、data と title.text をお問い合わせページ用のものに差し替えただけです。
3)showChart と addEventListener:ボタン操作で表示・切り替える
let myChart = null;
function showChart(optionToSet, activeBtn) {
if (!myChart) {
myChart = echarts.init(chartDom);
}
myChart.setOption(optionToSet, true);
[btnService, btnContact].forEach(btn => btn.classList.remove('is-active'));
activeBtn.classList.add('is-active');
}
btnService.addEventListener('click', () => showChart(optionService, btnService));
btnContact.addEventListener('click', () => showChart(optionContact, btnContact));ボタンが押されたときに、対応するグラフを表示する処理です。先に「ボタンを押すとどう動くか」を addEventListener で押さえてから、呼ばれる側の showChart の中身を見ていきます。
addEventListener
btnService.addEventListener('click', () => showChart(optionService, btnService));
btnContact.addEventListener('click', () => showChart(optionContact, btnContact));2つのボタンそれぞれに click イベントを紐づけて、押されたら showChart に対応する option とボタン要素を渡しています。showChart の中身は次で見ます。
showChart
function showChart(optionToSet, activeBtn) {
if (!myChart) {
myChart = echarts.init(chartDom);
}
myChart.setOption(optionToSet, true);
[btnService, btnContact].forEach(btn => btn.classList.remove('is-active'));
activeBtn.classList.add('is-active');
}グラフを描画して、押されたボタンをアクティブ状態にする関数です。グラフの描画には、EChartsの次の2つの関数を使います。
| 関数 | 役割 |
|---|---|
echarts.init(domElement) | チャートを操作するための「チャートインスタンス」を作ってHTML要素に紐づける |
setOption(option) | チャートインスタンスにグラフの設定を渡して実際に描画する |
まず echarts.init(chartDom) でコンテナにチャートインスタンスを作ります。myChart は関数の外で確保しておき、if (!myChart) で初回のみ初期化します(二重初期化の防止)。
次に myChart.setOption(optionToSet, true) で設定を反映します。第2引数の true は「前の設定を残さず完全に置き換える」意味で、ページ切り替え時の混在を防ぎます。
最後に forEach と classList で、押されたボタンだけに is-active を付け直します。
4)resize:画面サイズに追従する
画面サイズが変わったとき(ブラウザの幅を変えたり、端末を回転させたり)に、EChartsに再描画させます。
window.addEventListener('resize', () => {
if (myChart) myChart.resize();
});myChart.resize() を呼ぶと、EChartsはコンテナの新しいサイズを取り直して、media クエリも再評価して、レイアウトを更新してくれます。if (myChart) のチェックは「まだボタンを押していない(=グラフが描画されていない)状態」で resize が走ったときに、null に対して .resize() を呼ばないようにするための保険です。
まとめ
この記事では、Apache EChartsを使ってレーダーチャートを実装し、デザイン調整・レスポンシブ対応・ボタン切り替え・複数系列の重ね比較まで一通り解説しました。
ポイントは下記です。
- レーダーチャートは
series.type: 'radar'で描画する。系列の色は option レベルの color 配列で一括指定する - 軸の項目と最大値は
radar.indicatorで定義する。スマホで項目名が長くてはみ出す場合は、name 内に\nを入れて2行表示にできる - 複数系列を重ねるには
series.dataに複数のオブジェクトを並べる。重なり順は配列の順で決まる(先=下、後=上) - media は同じ画面幅にマッチするルールを複数置けて、後ろに書いたものが優先されつつ個別プロパティでマージされる。共通設定とページ固有の上書きを分けて書けて便利
レーダーチャートは多軸の複数項目で比較するときに向いた表現です。今回のコードはデータや評価項目を差し替えるだけで、UX/UI評価以外にも商品比較・アンケート結果・能力評価・パフォーマンスベンチマークなどにそのまま使えます。ぜひご自分の案件に合わせてカスタマイズしてみてください。
押していただけると励みになります!
