進捗状況グラフ(円形の進行状況バー)は、タスクのステータスを視覚的にフィードバックします。線形の進行状況バーとは異なり、円形の形状はダッシュボード、カード コンポーネント、スペースが限られている場所に適しています。
このガイドでは、次の方法で進捗状況グラフを実装します。
- ネイティブの
<progress>要素をセマンティック基盤として使用し、スクリーン リーダーやキーボード ユーザーがコンポーネントにすぐにアクセスできるようにします。 conic-gradient()とmask-imageを使用してコンポーネントのスタイルを設定することで、SVG パス操作の複雑さを伴わずに、完全にレスポンシブでテーマ設定可能なリングを実現できます。- CSS カスタム プロパティと
@propertyを活用して、GPU で高速化されたスムーズな進行状況バーの塗りつぶしを実現しています。
このアプローチは、ARIA ではなくセマンティックな <progress> 要素を使用し、既存のレイアウト、デザイン システム、タイポグラフィと簡単に統合できるため、SVG のみのソリューションよりも推奨されます。
不確定な読み込み状態の処理については、読み込みスピナーをご覧ください。
実装
1. マークアップ
ラッパーを使用して、ビジュアル リングとオプションの中央コンテンツの両方を保持します。<progress> 要素は、引き続きセマンティックな信頼できる情報源となります。ユーティリティ クラスを使用して、ネイティブの進行状況バーを視覚的に非表示にしつつ、アクセス可能な状態を維持します。
<div class="ring-wrapper">
<progress value="75" max="100" aria-label="Task progress" class="progress-ring"></progress>
<!-- Optional: Content to display in the center -->
<div class="ring-content">
75%
</div>
</div>
2. スタイル
ネイティブ UI を非表示にする
<progress> 要素を進行状況リングとしてスタイル設定するには、まず、進行状況バーのデフォルトのブラウザ スタイルを非表示にします。
/* Hide native bars */
progress.loading-spinner:indeterminate::-webkit-progress-bar {
display: none;
background: none;
}
progress.loading-spinner:indeterminate::-webkit-progress-value {
display: none;
background: none;
}
progress.loading-spinner:indeterminate::-moz-progress-bar {
display: none;
background: none;
}
progress.loading-spinner:indeterminate::slider-fill {
display: none;
background: none;
}
コンテナとリング
ラッパーは、位置コンテキストを提供します。<progress> 要素は、視覚的なグラデーションを処理します。
.progress-ring-wrapper {
position: relative;
display: grid;
place-items: center;
}
progress.progress-ring {
--size: 150px;
--thickness: 16px;
--track-color: #f1f5f9;
--fill-color: #3b82f6;
--value: attr(value type(<number>));
width: var(--size);
height: var(--size);
transition: --value 0.4s cubic-bezier(0.4, 0, 0.2, 1);
border-radius: 50%;
background: conic-gradient(
var(--fill-color) calc(var(--value) * 1%),
var(--track-color) 0
);
/* MANDATORY: Clip the background to the border-area. */
background-clip: border-area;
border: var(--thickness) solid transparent;
background-origin: border-box;
}
.progress-ring-content {
/* Positioned in the center of the wrapper */
position: absolute;
}
radial-gradient を使用して、丸みを帯びたエンドキャップを作成することもできます。
@property でスムーズな遷移を可能にする
値が変更されたときに進捗状況グラフをスムーズにアニメーション化するには、--value を数値のカスタム プロパティとして登録します。
動きに敏感なユーザーは、値の切り替えに戸惑うことがあります。デフォルトで 0 秒(即時)の期間を設定し、設定のないユーザーにはより長い時間を設定することで、prefers-reduced-motion メディアクエリを尊重します。
@property --value {
syntax: '<number>';
inherits: true;
initial-value: 0;
}
progress.progress-ring {
transition: --value 0s ease-in-out;
@media (prefers-reduced-motion: no-preference) {
transition-duration: 0.4s;
}
}
3. 進捗状況の更新
値が変更されるたびに、<progress> 要素の value 属性と .progress-ring-content のテキスト コンテンツ(パーセンテージを表示する場合)を更新します。
4. オプションの成功状態
CSS 属性セレクタを使用すると、タスクが完了したときにリングの外観を自動的に更新できます(色を緑に変更するなど)。
/* Change the fill color to green when the progress reaches 100% */
progress.progress-ring[value="100"] {
--fill-color: #10b981;
}
代替戦略
<progress> 要素内にフォールバック値を追加しないでください。支援技術では使用されず、最新のブラウザではすべて無視されます。
アニメーションの代替
@property がサポートされていない場合、リングはスムーズに移行するのではなく、新しい値にすぐにジャンプします。ほとんどの場合、これは問題なく、機能が損なわれることはありません。
どうしても遷移が必要な場合は、@property のサポートを確認し、requestAnimationFrame() ループを使用して古いブラウザで --value を補間できます。
background-clip: border-area をまだサポートしていないブラウザの場合は、mask-image にフォールバックして <progress> 要素の中央をくり抜きます。
@supports not (background-clip: border-area) {
mask-image: radial-gradient(
transparent calc(50% - var(--thickness)),
black calc(50% - var(--thickness) + 0.5px)
);
border: 0;
}
attr() CSS 関数をサポートしていないブラウザでは、MutationObserver を使用して value 属性を --value カスタム プロパティに自動的に同期します。
if (!CSS.supports("width: attr(value type(<number>))")) {
const observer = new MutationObserver(() => {
progress.style.setProperty("--value", progress.getAttribute("value"));
});
observer.observe(progress, {
attributes: true,
attributeFilter: ["value"],
});
}