Progress .mv-progress

네이티브 <progress> 기반의 진행률 막대(광택이 있는 확정형, 쓸고 지나가는 불확정형, 분할형), 애니메이션 카운터와 함께 --value로 제어되는 원형 SVG 링, 임계값에 따라 색이 바뀌는 <meter> 게이지를 제공합니다.

카테고리피드백
유형CSS 전용 (.mv-progress)
상태안정
Keywordsprogress, progression, progressbar, ring, circular, meter, jauge, gauge, loading, upload

When to use

  • A task with a known completion percentage, such as an upload or import, must show how far along it is
  • A compact circular ring shows a percentage inside a tile, card or profile completeness widget
  • A gauge shows a level against good and bad thresholds, like storage quota or password strength

Avoid when

  • The wait is short and its duration unknown, and a small inline indicator is enough → use Spinner instead
  • Progress follows named steps the user moves through rather than a percentage → use Stepper instead
  • The goal is to show how far the reader has scrolled through a page → use Scroll Progress instead

설치

node scripts/add.mjs progress --out ./src/marvelous

Marvelous UI MCP 서버를 사용하는 AI 에이전트: install_components({ slugs: ["progress"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).

복사되는 파일(의존성 포함): tokens/tokens.css, core/base.css, components/progress/progress.css.

사용법

기본 마크업입니다. 여기서 시작해 속성, data-*, CSS 변수로 커스터마이즈하세요:

<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,19rem),1fr));gap:2.5rem 3rem;width:100%;align-items:start">

  <!-- Linear bars -->
  <div style="display:grid;gap:1.5rem">
    <div class="mv-progress-field">
      <label class="mv-progress-label" for="pr-demo-upload">annual-report-2026.pdf</label>
      <output class="mv-progress-value" for="pr-demo-upload" id="pr-demo-upload-value">68%</output>
      <progress class="mv-progress" id="pr-demo-upload" max="100" value="68" data-shine></progress>
      <span class="mv-progress-hint" id="pr-demo-upload-hint">2.4 MB of 3.5 MB · 12 s left</span>
    </div>

    <div class="mv-progress-field">
      <label class="mv-progress-label" for="pr-demo-sync">Syncing contacts…</label>
      <progress class="mv-progress" id="pr-demo-sync" data-size="sm" aria-describedby="pr-demo-sync-hint"></progress>
      <span class="mv-progress-hint" id="pr-demo-sync-hint">Importing from Google Contacts</span>
    </div>

    <div style="display:grid;gap:.75rem">
      <progress class="mv-progress" data-size="xs" data-color="success" max="100" value="100" aria-label="Backup complete"></progress>
      <progress class="mv-progress" data-size="sm" data-color="warning" max="100" value="64" aria-label="Credits used"></progress>
      <progress class="mv-progress" data-color="danger" max="100" value="92" aria-label="Disk space"></progress>
      <progress class="mv-progress" data-size="lg" data-variant="segmented" max="100" value="40" aria-label="Profile 40% complete"></progress>
    </div>
  </div>

  <!-- Meters -->
  <div style="display:grid;gap:1.5rem">
    <div class="mv-progress-field">
      <label class="mv-progress-label" for="pr-demo-storage">Storage</label>
      <span class="mv-progress-value">7.2 GB of 10 GB</span>
      <meter class="mv-meter" id="pr-demo-storage" min="0" max="10" low="6" high="8.5" optimum="2" value="7.2"></meter>
      <span class="mv-progress-hint">Above 8.5 GB, syncing slows down.</span>
    </div>
    <div class="mv-progress-field">
      <label class="mv-progress-label" for="pr-demo-api">Monthly API quota</label>
      <span class="mv-progress-value">94%</span>
      <meter class="mv-meter" id="pr-demo-api" min="0" max="100" low="70" high="90" optimum="10" value="94"></meter>
    </div>
    <div class="mv-progress-field">
      <label class="mv-progress-label" for="pr-demo-pwd">Password strength</label>
      <span class="mv-progress-value" style="color:var(--mv-success)">Strong</span>
      <meter class="mv-meter" id="pr-demo-pwd" data-variant="segmented" data-color="success" data-size="sm" style="--mv-progress-segments:4" min="0" max="100" low="40" high="70" optimum="100" value="85"></meter>
    </div>
  </div>

  <!-- Rings -->
  <div style="grid-column:1/-1;display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:2.5rem">
    <div class="mv-progress-ring" id="pr-demo-ring" role="progressbar" aria-label="Monthly goal" aria-valuemin="0" aria-valuemax="100" aria-valuenow="72" style="--value: 72">
      <svg viewBox="0 0 36 36" aria-hidden="true"><circle class="mv-progress-ring-track" cx="18" cy="18" r="16"/><circle class="mv-progress-ring-bar" cx="18" cy="18" r="16" pathLength="100"/></svg>
      <span class="mv-progress-ring-label"></span>
    </div>
    <div class="mv-progress-ring" data-size="lg" data-color="success" role="progressbar" aria-label="Tasks completed" aria-valuemin="0" aria-valuemax="100" aria-valuenow="67" aria-valuetext="8 of 12 tasks" style="--value: 67">
      <svg viewBox="0 0 36 36" aria-hidden="true"><circle class="mv-progress-ring-track" cx="18" cy="18" r="16"/><circle class="mv-progress-ring-bar" cx="18" cy="18" r="16" pathLength="100"/></svg>
      <span class="mv-progress-ring-label" style="display:grid;justify-items:center;gap:4px">8/12<small style="font-size:.6875rem;font-weight:500;color:var(--mv-fg-muted);letter-spacing:0">tasks</small></span>
    </div>
    <div class="mv-progress-ring" data-size="sm" data-color="warning" role="progressbar" aria-label="Battery" aria-valuemin="0" aria-valuemax="100" aria-valuenow="28" style="--value: 28">
      <svg viewBox="0 0 36 36" aria-hidden="true"><circle class="mv-progress-ring-track" cx="18" cy="18" r="16"/><circle class="mv-progress-ring-bar" cx="18" cy="18" r="16" pathLength="100"/></svg>
      <span class="mv-progress-ring-label"></span>
    </div>
    <div class="mv-progress-ring" data-indeterminate role="progressbar" aria-label="Analyzing">
      <svg viewBox="0 0 36 36" aria-hidden="true"><circle class="mv-progress-ring-track" cx="18" cy="18" r="16"/><circle class="mv-progress-ring-bar" cx="18" cy="18" r="16" pathLength="100"/></svg>
    </div>
  </div>
</div>
<script type="module">
  // Simulated upload on a 6.5 s loop, so the bar transition is always on screen
  // (the first state matches the markup, so a capture at any time looks settled).
  const bar = document.getElementById("pr-demo-upload");
  const out = document.getElementById("pr-demo-upload-value");
  const hint = document.getElementById("pr-demo-upload-hint");
  const steps = [
    [0, 68, "2.4 MB of 3.5 MB · 12 s left"],
    [1300, 86, "3.0 MB of 3.5 MB · 4 s left"],
    [2400, 100, "3.5 MB uploaded"],
    [3900, 12, "0.4 MB of 3.5 MB · estimating time left…"],
    [5100, 41, "1.4 MB of 3.5 MB · 20 s left"],
  ];
  const period = 6500;
  const t0 = performance.now();
  let i = 0;
  const next = () => {
    if (!bar?.isConnected) return; // demo removed or replayed
    const [, value, text] = steps[i % steps.length];
    bar.value = value;
    out.textContent = `${value}%`;
    hint.textContent = text;
    i++;
    const at = Math.floor(i / steps.length) * period + steps[i % steps.length][0];
    setTimeout(next, Math.max(0, t0 + at - performance.now()));
  };
  if (bar) { i = 1; setTimeout(next, steps[1][0]); }
</script>

API

Attributes

Name유형DefaultDescription
data-sizexs | sm | lg(md)Bar thickness or ring size (sm | lg).
data-colorsuccess | warning | danger | info | neutral(accent)Fill color.
data-shinebooleanShine that runs across an in-progress determinate bar.
data-variantsegmentedSplits the bar / gauge into segments.
data-indeterminatebooleanOn .mv-progress-ring: continuously spinning arc.

CSS classes

NameDescription
mv-progressOn <progress value max>; without value = indeterminate (sweep).
mv-meterOn <meter min max low high optimum value>: accent when optimal, then warning and danger based on the native thresholds.
mv-progress-ringRing: a role="progressbar" container + style="--value: 0…100" holding an <svg viewBox="0 0 36 36"> with two r=16 circles (the second with pathLength="100").
mv-progress-ring-track / -barTrack and value circles of the ring.
mv-progress-ring-labelCenter label; empty = automatically animated percentage (CSS counter).
mv-progress-fieldLabel + value grid above the bar, hint text below.
mv-progress-label / -value / -hintField parts.

CSS variables

NameDefaultDescription
--value0Ring value (0 to 100), with a smooth transition and animated counter on every change.
--mv-progress-colorvar(--mv-accent)Fill color.
--mv-progress-trackTrack color.
--mv-progress-segments10Number of segments (data-variant="segmented").
--mv-progress-ring-size4remRing diameter.
--mv-progress-ring-stroke3.2Stroke width (in units of the 36 viewBox).
--mv-meter-suboptimum / --mv-meter-badColors of the suboptimal and bad <meter> zones.

Accessibility

Native <progress> and <meter> expose their roles and values; pair them with a <label for> or aria-label. Ring: role="progressbar", aria-valuenow/min/max (update them along with --value) and aria-valuetext if the label differs. Indeterminate bar: no value, announced as busy. Reduced motion: sweep, shine and spin stop, transitions are instant.

이 페이지는 AI로 번역되었습니다. 번역 문제 신고