달성률 링 (또는 원형 진행률 표시줄)은 작업 상태에 관한 시각적 피드백을 제공합니다. 선형 진행률 표시줄과 달리 원형 모양은 대시보드, 카드 구성요소 또는 공간이 제한된 모든 위치에 적합합니다.
이 가이드에서는 다음을 통해 달성률 링을 구현합니다.
- 기본
<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"],
});
}