스타일이 지정된 진행률 표시줄 빌드

<progress> 요소는 정보 다운로드 또는 업로드, 다단계 프로세스의 일부 완료와 같은 작업의 완료 진행 상황을 나타내는 시맨틱 방식입니다. 스크린 리더에는 암시적 role="progressbar"가 있고 값을 읽을 수 있으므로 네이티브 요소를 사용하면 접근성에 도움이 됩니다.

이 가이드에서는 스타일 맞춤설정이 적용된 완전한 스타일의 가로 진행률 표시줄을 구현합니다.

진행률 표시줄 또는 달성률 링

  • 공간이 제한되어 있거나 정사각형 가로세로 비율이 더 적합한 경우 원형 진행률 표시줄 (달성률 링)을 사용하는 것이 좋습니다. 달성률 링
  • 대부분의 불확정 진행 상황 시나리오에서는 스피너를 사용하는 것이 좋습니다 (로딩 스피너). 불확정 상태가 일시적이고 곧 확정 진행률 표시줄로 대체될 경우 불확정 진행률 표시줄을 사용합니다 (예: 진행률 % 를 표시하기 전에 업로드 관련 메타데이터를 기다리는 경우).

마크업

  1. 네이티브 <progress> 요소 사용: 기본 제공 접근성과 플랫폼 일관성 동작을 제공합니다.
  2. 기본 0~1 배율과 다른 배율이 필요한 경우 max를 사용합니다.
  3. 특정 진행률 값의 경우 value 속성을 설정합니다. 불확정 상태 (예: 더 구체적인 피드백이 시작되기 전)에서는 value를 생략합니다.
  4. <label for="...">, aria-labelledby 또는 aria-label를 사용하여 진행률 요소를 라벨과 연결합니다.

어떤 유형의 맞춤설정이 필요한가요?

제한된 스타일 지정의 경우 다음 속성으로 충분합니다.

  • accent-color: 진행률 표시줄의 색상 변경
  • width / inline-size: 진행률 표시줄 너비 변경
  • height/block-size가 일관되지 않게 적용됨 - 먼저 테스트하세요.
  • writing-mode: vertical-lr: 세로 막대
  • 페인팅 후 적용되는 효과: opacity, filter, clip-path, 마스크, 변환, box-shadow 등

제한된 스타일 지정으로 충분하다면 다음 섹션을 건너뜁니다.

경고: 특정 속성 (예: border-radius, background, border)은 기본 진행률 표시줄 스타일 지정을 자동으로 사용 중지합니다. 위 속성 이외의 스타일 지정이 필요한 경우 다음 섹션에 설명된 대로 완전한 맞춤 스타일 지정을 구현해야 합니다. '혹시 모르니'라는 생각으로 더 광범위한 스타일을 적용하지 마세요.

완전한 맞춤 스타일 지정

1. 기본 설정

  • 필수: appearance: none를 사용하여 맞춤 스타일 지정을 사용 설정합니다.
  • border: 0 solid를 사용하여 UA 경계 재설정
  • 활성 막대를 요소의 border-radius에 맞게 자르려면 overflow: clip를 사용하거나 막대에 동일한 border-radius를 적용합니다. Shadow DOM의 특정 요소 구조에 따라 달라지는 border-radius: inherit를 막대에 사용하지 마세요.
progress {
  /* Reset default browser styles */
  appearance: none;
  border: 0 solid;
  /* Clip bar to element’s border-radius  */
  overflow: clip;
}

2. 스타일 지정

  • progress 요소 자체에서 트랙의 스타일을 지정합니다.
  • 적절한 브라우저 타겟팅 가상 요소를 사용하여 진행률 표시줄 자체를 타겟팅합니다. WebKit/Blink의 경우 ::-webkit-progress-value, Firefox의 경우 ::-moz-progress-bar를 사용합니다.
  • 필수: 동일한 규칙에서 -webkit- 및 -moz- 가상 요소를 그룹화하지 마세요. 규칙이 잘못된 것으로 간주되어 무시됩니다. :is() 및 :where()는 의사 요소를 지원하지 않으므로 여기서는 도움이 되지 않습니다.
  • 브라우저 타겟팅 가상 요소는 정확히 한 번만 표시되어야 합니다. 상태별 맞춤설정 (예: 불확정 세그먼트 크기 및 애니메이션)은 progress 요소 자체에 설정된 맞춤 속성을 통해 라우팅해야 합니다.
  • 선택사항: 채우기에 inline-size을 전환하여 확정 값이 점프하는 대신 애니메이션으로 표시되도록 합니다. Firefox는 레이아웃의 채우기 크기를 조정하고 무시하므로 문제가 되지 않습니다.
progress {
  --progress-bar-radius: 1e5px;
  border-radius: var(--progress-bar-radius);
  /* Apply color for custom progress track (background) */
  background: var(--progress-bar-track, var(--color-neutral-90));
  /* Subtle depth on the empty part of the track */
  box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.1);
}

/* Let <progress> styling show through */
progress::-webkit-progress-bar {
  background: transparent;
}

/* Webkit, Chrome */
progress::-webkit-progress-value {
  background: var(--progress-bar-fill, var(--accent-color));
  border-radius: var(--progress-bar-radius);
  transition: inline-size 0.3s;
}

/* Firefox */
progress::-moz-progress-bar {
  background: var(--progress-bar-fill, var(--accent-color));
  border-radius: var(--progress-bar-radius);
  transition: inline-size 0.3s;
}

3. 불확정 상태 스타일 지정

불확정 상태에 대한 맞춤 애니메이션 추가: 채우기 가상 요소를 짧은 세그먼트로 변환하고 트랙을 따라 슬라이드합니다.

  • 채우기 자체의 스타일을 지정합니다. progress에서 배경으로 다시 만들지 마세요.
  • 불확정 채우기는 애니메이션을 통해서만 존재합니다. 키프레임은 실제 inline-size로 길이를 설정하고(Chromium의 UA 인라인 inline-size보다 우수함) translate로 이동합니다. 길이에 scale를 사용하지 마세요. 채우기에 그려진 패턴이 왜곡됩니다.
  • translate 비율은 채우기의 자체 너비에 따라 결정되므로 대신 트랙 단위로 위치를 표현합니다. progress를 인라인 크기 컨테이너로 만들고 키프레임에서 cqi를 사용합니다. from, to에 margin-right: auto를 포함하여 Chromium이 RTL에서도 물리적 왼쪽에 채우기를 배치하도록 합니다.
  • 여백이 아닌 변환을 사용하여 채우기를 이동합니다.
    • 변환은 애니메이션을 컴포지터에 유지합니다(inline-size도 애니메이션화되지 않는 한).
    • RTL에서 여백이 깨지는 이유는 두 가지입니다.
    • Firefox는 실제 왼쪽 여백만 사용하여 불확정 채우기를 배치하므로 margin-inline-start는 RTL에 영향을 미치지 않으며 채우기가 이동하지 않습니다.
    • 채우기를 일반 블록으로 배치하는 Chromium 및 WebKit에서는 여백이 채우기를 트랙 끝을 지나 밀어내면 여백이 트랙보다 더 많이 합산됩니다. 표준 CSS는 RTL에서 margin-left를 무시하여 이 문제를 해결하므로 세그먼트가 가장자리에서 미끄러지는 대신 가장자리로 이동합니다.
    • translate는 레이아웃 후에 적용되므로 두 문제 모두 적용되지 않습니다.
  • Chrome의 기본 표시줄과 같이 RTL에서 모션을 미러링하려면 :dir(rtl)에서 애니메이션을 반전합니다.
progress:dir(rtl) {
  --progress-bar-indeterminate-animation: progress-bar-indeterminate var(--progress-bar-indeterminate-animation-duration) infinite linear reverse;
}
  • 소비자가 :indeterminate에 관해 알지 못해도 재정의할 수 있도록 progress에서 공개 --progress-bar-indeterminate-* 속성을 정의합니다(이름이 이미 상태로 범위가 지정됨).
  • 세그먼트 길이를 변경하려면 (예: 앞쪽 가장자리가 앞서 나가고 뒤쪽 가장자리가 따라잡음) --progress-bar-indeterminate-animation를 두 개의 동시 애니메이션으로 설정합니다. 하나는 키프레임이 cqi (위치)에서 translate에 애니메이션을 적용하고 다른 하나는 키프레임이 inline-size (길이)에 애니메이션을 적용하여 각각 자체 키프레임과 이징을 갖습니다. 단일 키프레임 목록에서 두 가장자리를 모두 형성하려고 시도하지 마세요. 키프레임별 animation-timing-function는 해당 키프레임의 모든 속성에 적용됩니다.
  • --progress-bar-animation는 두 상태 모두에 적용되며, 확정된 반짝임과 같은 효과에 사용할 수 있습니다. revert-rule는 구성 가능성을 극대화하기 위해 설정된 경우에만 재정의하는 데 사용됩니다.
  • 불확정 애니메이션을 사용 중지하지 마세요. Chromium에서 animation: none은 너비가 0인 채우기를 남깁니다. 동작 줄이기의 경우 대신 속도를 늦춥니다.
progress {
  /* Lets the fill position itself in track units (cqi) */
  container-type: inline-size;
  /* Segment length relative to the track */
  --progress-bar-indeterminate-size: 30%;
  --progress-bar-indeterminate-animation-duration: 1.5s;

  /* Respect prefers-reduced-motion */
  @media (prefers-reduced-motion: reduce) {
    --progress-bar-indeterminate-animation-duration: 10s;
  }
}

progress:indeterminate {
  --progress-bar-animation: var(
      --progress-bar-indeterminate-animation,
      progress-bar-indeterminate var(--progress-bar-indeterminate-animation-duration) infinite linear
    );
}

/* Add to the existing pseudo-element rules */
progress::-webkit-progress-value {
  animation: var(--progress-bar-animation, revert-rule);
}

progress::-moz-progress-bar {
  animation: var(--progress-bar-animation, revert-rule);
}

/* Physical: left to right regardless of direction (reverse under :dir(rtl) to mirror). */
@keyframes progress-bar-indeterminate {
  from, to {
    inline-size: var(--progress-bar-indeterminate-size);
    /* Pin to the physical left; Chromium would otherwise lay the fill out at the inline start */
    margin-right: auto;
  }
  from { translate: -100%; } /* Own width: fully outside the left edge */
  to { translate: 100cqi; }  /* Track width: fully outside the right edge */
}

추가 접근성 고려사항

위에 언급된 가이드라인 외에도 다음 사항을 준수해야 합니다.

  • 포커스를 가로채지 않고 완료 알림: <progress>에서 tabindex를 설정하거나 .focus()를 호출하지 마세요. 상호작용이 불가능하며 포커스를 이동하면 키보드 사용자가 방해를 받습니다. 백그라운드 작업이 완료되면 이를 알리기 위해 정중한 상태 요소 (role="status" 또는 aria-live="polite")를 업데이트합니다.
  • <progress> 내부에 대체 텍스트를 추가하지 마세요. <progress> 내부의 텍스트는 최신 브라우저에서 무시되며 보조 기술에서 사용되지 않습니다.
  • 라벨이 지정되지 않은 일반 <div>에서 aria-busy + aria-describedby를 사용하지 마세요. aria-busy에는 일관되지 않은 스크린 리더 지원이 있으며, 역할이 없는 이름이 지정되지 않은 <div>의 aria-describedby는 보조 기술에서 무시됩니다. 로드하는 동안 리전의 콘텐츠가 준비되지 않은 경우 hidden 또는 aria-hidden="true"를 사용하여 숨깁니다.
  • 맞춤 스타일을 추가할 때는 진행률 표시줄과 트랙 간에 적절한 대비를 유지해야 합니다.
  • 불확정 상태를 나타내는 대안 고려: 다른 콘텐츠와 함께 표시되는 불확정 진행률 표시줄 애니메이션은 일부 사용자에게 방해가 될 수 있습니다.

대체 전략

accent-color의 대체 및 브라우저 지원

accent-color 속성은 점진적 개선입니다. 이 속성을 지원하지 않는 브라우저는 이를 무시하고 기본 UI 색상을 사용합니다.

accent-color가 지원되지 않고 진행률 표시줄의 스타일을 지정해야 하는 경우 맞춤설정된 스타일 지정 메서드를 사용하여 진행률 표시줄에 원하는 스타일을 적용합니다.

revert-rule의 대체 및 브라우저 지원

revert-rule가 지원되지 않으면 animation: var(--progress-bar-animation, revert-rule)는 --progress-bar-animation가 설정되지 않은 경우 0s 기간으로 animation-name: revert-rule로 대체됩니다. 이는 @keyframes revert-rule가 없는 경우 무해한 무작업입니다.