進度環 (或圓形進度列) 可提供工作狀態的視覺回饋。與線狀進度列不同,圓形進度列適合用於資訊主頁、資訊卡元件或任何空間有限的地方。
本指南會透過以下方式實作進度環:
- 使用原生
<progress>元素做為語意基礎,確保螢幕閱讀器和鍵盤使用者能直接使用該元件。 - 使用
conic-gradient()和mask-image設定元件樣式,即可建立完全回應式且可設定主題的圓環,不必進行複雜的 SVG 路徑操作。 - 運用 CSS 自訂屬性和
@property,讓進度填滿效果流暢地進行 GPU 加速轉換。
這種做法比僅使用 SVG 的解決方案更理想,因為它使用語意 <progress> 元素而非 ARIA,且更容易與現有的版面配置、設計系統和字體排版整合。
如要處理不確定的載入狀態,請參閱載入旋轉圖示。
導入作業
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"],
});
}