프로그레스 바

Progress bar

작업이 얼마나 끝났는지를 채워지는 막대 길이로 보여주는, 읽기 전용 표시기.

다른 이름: Progress indicatorLoading bar
···
html
<div class="pbw">
  <div class="pbtop"><span>업로드 중</span><b id="pbv">0%</b></div>
  <div class="pb" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"><div class="pbfill" id="pbf"></div></div>
  <div class="pb2"><div class="pb2fill"></div></div>
</div>
css
.pbw{width:min(230px,88%);display:grid;gap:16px}
.pbtop{display:flex;justify-content:space-between;font-size:12px;color:var(--muted)}
.pbtop b{color:var(--fg)}
.pb{height:8px;border-radius:5px;background:var(--line);overflow:hidden}
.pbfill{height:100%;width:0%;background:var(--accent);border-radius:5px;transition:width .3s linear}
.pb2{height:4px;border-radius:3px;background:var(--line);overflow:hidden;position:relative}
.pb2fill{position:absolute;top:0;bottom:0;width:35%;background:var(--accent-2,#f25c8a);border-radius:3px;animation:indet 1.3s ease-in-out infinite}
@keyframes indet{0%{left:-35%}100%{left:100%}}
js
const pbf = document.getElementById('pbf'), pbv = document.getElementById('pbv'), pb = document.querySelector('.pb');
let v = 0;
setInterval(() => {
  v = (v + 4) % 104;
  const cv = Math.min(v, 100);
  pbf.style.width = cv + '%'; pbv.textContent = cv + '%'; pb.setAttribute('aria-valuenow', String(cv));
}, 140);

role="progressbar"에 aria-valuenow/min/max를 실시간으로 갱신합니다. 정확한 퍼센트를 알 수 없는 작업이라면 aria-valuenow를 아예 생략하고 줄무늬가 계속 움직이는 "비확정(indeterminate)" 형태로 보여줍니다 — 이땐 사실상 스피너와 역할이 겹칩니다.

스피너와의 구분 기준은 "얼마나 걸릴지 아는가"입니다. 파일 업로드처럼 남은 바이트를 알 수 있는 확정적 작업엔 프로그레스 바가 정보량이 많아 낫고, 서버 응답을 기다리는 것처럼 언제 끝날지 전혀 모르는 작업엔 스피너가 더 정직합니다. 스켈레톤과의 차이는, 스켈레톤은 곧 나올 콘텐츠의 형태를 흉내 내 체감 속도를 높이는 쪽이고 프로그레스 바는 숫자로 된 진행률 자체를 알려주는 쪽입니다.

100%에 도달하면 즉시 사라지기보다 완료 상태를 짧게라도 보여준 뒤 없어지는 편이 "끝났다"는 확신을 줍니다.