ウェブ開発において、データ可視化は必須の要素です。特に円グラフは比率を直感的に示すツールとして頻繁に使用されます。しかし、多くの開発者が手軽さから重いJavaScriptチャートライブラリを導入しています。これはページの読み込み速度に悪影響を与えるだけでなく、アクセシビリティの観点でも問題となる可能性があります。この記事では、CSSのconic-gradient()と最新のCSS関数を活用し、セマンティックなマークアップとアクセシビリティの両方を満たす円グラフを作成する方法を段階的に詳しく説明します。基礎となる考え方は参考資料でご確認いただけます。

セマンティックなHTML構造の設計
まず最初に、スクリーンリーダーが理解できる意味論的なマークアップを構築する必要があります。figureとulを活用してデータを構造化します。
<figure class="pie-chart">
<figcaption>先月販売されたキャンディ</figcaption>
<ul>
<li data-percentage="30" data-color="#4A90E2">
<strong>チョコレート</strong>
</li>
<li data-percentage="25" data-color="#50E3C2">
<strong>グミ</strong>
</li>
<li data-percentage="20" data-color="#F5A623">
<strong>ハードキャンディ</strong>
</li>
<li data-percentage="25" data-color="#BD10E0">
<strong>バブルガム</strong>
</li>
</ul>
</figure>
CSSで各スライスを描画
各li要素は、自身のdata-percentageとdata-color属性を読み取り、独自のスライスを描画します。最新のattr()関数を使用して、HTML属性をCSS変数として解析します。
.pie-chart {
display: grid;
place-items: center;
}
.pie-chart li {
--radius: 20vmin;
--percentage: calc(attr(data-percentage number) * 1%);
--bg-color: attr(data-color color);
--weighing: calc(attr(data-percentage number) / 100);
width: calc(var(--radius) * 2);
aspect-ratio: 1;
border-radius: 50%;
grid-row: 1;
grid-column: 1;
/* conic-gradientでスライスを生成 */
background: conic-gradient(
from var(--offset, 0deg),
var(--bg-color) 0% var(--percentage),
transparent var(--percentage) 100%
);
}
JavaScriptによる累積値の計算
CSSだけでは、前のスライスのパーセンテージを累積することができません。簡単なJavaScriptで--accum変数を計算し、各スライスの開始角度を設定します。
const pieChartItems = document.querySelectorAll('.pie-chart li');
let accum = 0;
pieChartItems.forEach((item) => {
item.style.setProperty('--accum', accum);
accum += parseFloat(item.getAttribute('data-percentage'));
item.style.setProperty('--offset', `${accum * 3.6}deg`);
});

ラベルの円形配置
cos()とsin()のCSS関数を利用して、各スライスの中点にラベルを配置します。これにより、数学的な座標計算をCSS内で直接可能にします。
.pie-chart li {
/* ... 既存のスタイル ... */
/* ラベル位置の計算 */
--theta: calc((360deg * var(--weighing)) / 2 + var(--offset) - 90deg);
--gap: 4rem;
--pos-x: calc(cos(var(--theta)) * (var(--radius) + var(--gap)));
--pos-y: calc(sin(var(--theta)) * (var(--radius) + var(--gap)));
display: grid;
place-items: center;
}
.pie-chart li strong,
.pie-chart li::after {
grid-row: 1;
grid-column: 1;
transform: translateX(var(--pos-x)) translateY(var(--pos-y));
}
/* パーセント表示 */
.pie-chart li::after {
content: attr(data-percentage) '%';
--pos-y: calc(sin(var(--theta)) * (var(--radius) + var(--gap)) + 1lh);
}
注意点と制限
この方法は純粋なCSSに近いですが、スライスの累積角度を計算するために最小限のJavaScriptが依然として必要です。また、cos()とsin()関数のサポートはすべてのブラウザで完了していません。代替案としてCSSのtransform: rotate()を組み合わせることもできますが、計算がより複雑になります。色はdata-color属性でハードコーディングする必要があり、動的な色生成にはcolor-mix()関数または追加のJavaScriptが必要です。

次の学習ステップ
この基本的な実装を基に、さまざまな高度な機能を追加してみることができます。
- 生データからのパーセンテージ自動計算:
data-value属性に生の値を入力すると、CSSまたはJSが合計に対するパーセンテージを自動計算するように改良します。 - さまざまなチャートタイプへの適用: 同じセマンティックマークアップの原理を応用して、棒グラフやドーナツグラフを実装してみてください。
- インタラクションの追加:
:hover時にスライスが強調表示されたりツールチップが表示される効果を適用すると、UXが大幅に向上します。 - CSS Houdiniの検討: 将来的に
CSS Properties and Values APIのサポートが広まれば、JavaScriptなしでCSS内で状態を累積できるようになるかもしれません。
アクセシビリティとパフォーマンスを考慮したデータ可視化は、現代のウェブ開発における核心的なスキルです。このチュートリアルが、重いライブラリへの依存から脱却し、基本技術の力で問題を解決する一助となれば幸いです。