Progress .mv-progress
ネイティブの <progress> 上のプログレスバー(光沢付きの確定型、スイープする不確定型、セグメント型)、アニメーションするカウンター付きで --value で制御する円形の SVG リング、しきい値に応じて色が変わる <meter> ゲージです。
| カテゴリー | フィードバック |
|---|---|
| タイプ | CSS のみ(.mv-progress) |
| ステータス | 安定版 |
| Keywords | progress, 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/marvelousMarvelous 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 | タイプ | Default | Description |
|---|---|---|---|
data-size | xs | sm | lg | (md) | Bar thickness or ring size (sm | lg). |
data-color | success | warning | danger | info | neutral | (accent) | Fill color. |
data-shine | boolean | Shine that runs across an in-progress determinate bar. | |
data-variant | segmented | Splits the bar / gauge into segments. | |
data-indeterminate | boolean | On .mv-progress-ring: continuously spinning arc. |
CSS classes
| Name | Description |
|---|---|
mv-progress | On <progress value max>; without value = indeterminate (sweep). |
mv-meter | On <meter min max low high optimum value>: accent when optimal, then warning and danger based on the native thresholds. |
mv-progress-ring | Ring: 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 / -bar | Track and value circles of the ring. |
mv-progress-ring-label | Center label; empty = automatically animated percentage (CSS counter). |
mv-progress-field | Label + value grid above the bar, hint text below. |
mv-progress-label / -value / -hint | Field parts. |
CSS variables
| Name | Default | Description |
|---|---|---|
--value | 0 | Ring value (0 to 100), with a smooth transition and animated counter on every change. |
--mv-progress-color | var(--mv-accent) | Fill color. |
--mv-progress-track | Track color. | |
--mv-progress-segments | 10 | Number of segments (data-variant="segmented"). |
--mv-progress-ring-size | 4rem | Ring diameter. |
--mv-progress-ring-stroke | 3.2 | Stroke width (in units of the 36 viewBox). |
--mv-meter-suboptimum / --mv-meter-bad | Colors 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.