Progress Bar Examples
The examples below assume that you have imported the component and set up the theme.
Basic
Set value within the min/max range, and the fill animates to every assigned value. The bar takes the full width of its container.
<div class="bar-basic"> <sv-progress-bar aria-label="Upload progress" value="65"></sv-progress-bar></div>
<style> .bar-basic { display: flex; justify-content: center; padding-block: 1em;
sv-progress-bar { inline-size: min(100%, 20rem); } }</style>Indeterminate
Without a value, the bar is indeterminate and a short fill slides along the track until one is assigned.
Your system is set to reduce motion, so the fill sits still in the middle of the track.
<div class="bar-indeterminate"> <sv-progress-bar aria-label="Loading"></sv-progress-bar> <sv-progress-bar aria-label="Loading" vertical></sv-progress-bar></div>
<style> .bar-indeterminate { display: flex; flex-wrap: wrap; gap: 2em; align-items: center; justify-content: center; padding-block: 1em;
sv-progress-bar { inline-size: min(100%, 16rem); }
sv-progress-bar[vertical] { inline-size: auto; block-size: 8rem; } }</style>Label
Slotted content sits in a row before the track, with the first and last element pushed to opposite ends. A child with data-percentage or data-value renders the current number, and the attribute's number sets the decimal places. A data-value-text element supplies aria-valuetext, since slotted content is hidden from assistive technology. Set flex-direction: column-reverse on the bar to move the content below the track.
<div class="bar-label"> <sv-progress-bar aria-label="Upload progress" value="50"> <span>Uploading</span> <span class="value" data-value-text> <span data-percentage="0"></span> <span>%</span> </span> </sv-progress-bar>
<sv-progress-bar class="labels-below" aria-label="Upload progress" value="50"> <span>Uploading</span> <span class="value" data-value-text> <span data-percentage="0"></span> <span>%</span> </span> </sv-progress-bar></div>
<style> .bar-label { display: grid; gap: 2em; justify-items: center; padding-block: 1em;
sv-progress-bar { inline-size: min(100%, 20rem); font-size: 0.875rem; }
.labels-below { flex-direction: column-reverse; }
.value { display: flex; } }</style>Fill Content
Content in the fill slot is centered inside the filled portion. Make the track and the fill thick enough for the text.
<div class="bar-fill-content"> <sv-progress-bar aria-label="Upload progress" value="50"> <span class="value" data-value-text slot="fill"> <span data-percentage="0"></span> <span>%</span> </span> </sv-progress-bar></div>
<style> .bar-fill-content { display: flex; justify-content: center; padding-block: 1em;
sv-progress-bar { --sv-progress-bar-thickness: 1.25rem;
inline-size: min(100%, 20rem); }
.value { display: flex; font-size: 0.75rem; color: var(--sv-accent-content); } }</style>Buffer
buffer-value shows a second, fainter fill behind the first one, for content that loads ahead of the current position. It uses the same range as value, and buffer-percentage sets it as a percentage of that range.
<div class="bar-buffer"> <sv-progress-bar aria-label="Video playback" buffer-value="60" value="30"></sv-progress-bar></div>
<style> .bar-buffer { display: flex; justify-content: center; padding-block: 1em;
sv-progress-bar { inline-size: min(100%, 20rem); } }</style>Vertical
vertical turns the bar upright and fills it from the bottom. A vertical bar has no height of its own. Give it one. With flex-direction: row and align-items: stretch, the content sits beside the track instead of above it.
<div class="bar-vertical"> <sv-progress-bar aria-label="Battery level" value="70" vertical> <span class="value" data-value-text> <span data-percentage="0"></span> <span>%</span> </span> </sv-progress-bar>
<sv-progress-bar class="label-side" aria-label="Battery level" value="70" vertical> <span class="value" data-value-text> <span data-percentage="0"></span> <span>%</span> </span> </sv-progress-bar></div>
<style> .bar-vertical { display: flex; gap: 3em; justify-content: center; padding-block: 1em;
sv-progress-bar { block-size: 10rem; font-size: 0.875rem; }
.value { display: flex; }
.label-side { flex-direction: row; align-items: stretch;
.value { writing-mode: sideways-lr; } } }</style>Download
Assign value from JavaScript whenever progress arrives, and the fill animates to it on its own.
<div class="bar-download"> <sv-progress-bar class="download-bar" aria-label="Download progress" value="0"> <span>Downloading</span> <span class="value" data-value-text> <span data-percentage="0"></span> <span>%</span> </span> </sv-progress-bar></div>
<script type="application/x-typescript" module> const bar = document.querySelector<ProgressBar>(".download-bar")!;
let speed = 100;
function tick() { const current = bar.value ?? 0; const next = current >= bar.max ? 0 : current + 1;
bar.value = next;
if (Math.random() < 0.1) { speed = 20 + Math.random() * 380; }
setTimeout(tick, next >= bar.max ? 1500 : speed); }
tick();</script>
<style> .bar-download { display: flex; justify-content: center; padding-block: 1em;
sv-progress-bar { inline-size: min(100%, 20rem); font-size: 0.875rem; }
.value { display: flex; } }</style>Styling
Custom properties control the thickness, corner radius, and colors of the track and the fill.
<div class="bar-styling"> <div class="variant"> <sv-progress-bar class="thick" aria-label="Thick bar" value="60"></sv-progress-bar> <span>--sv-progress-bar-thickness</span> </div>
<div class="variant"> <sv-progress-bar class="thin-fill" aria-label="Thin fill on a thick track" value="60"></sv-progress-bar> <span>--sv-progress-bar-fill-thickness</span> </div>
<div class="variant"> <sv-progress-bar class="thick-fill" aria-label="Thick fill on a thin track" value="60"></sv-progress-bar> <span>--sv-progress-bar-track-thickness</span> </div>
<div class="variant"> <sv-progress-bar class="square" aria-label="Square bar" value="60"></sv-progress-bar> <span>--sv-progress-bar-rad</span> </div>
<div class="variant"> <sv-progress-bar class="tinted" aria-label="Amber bar" value="60"></sv-progress-bar> <span>color tokens</span> </div></div>
<style> .bar-styling { display: grid; gap: 1.5em; justify-items: center; padding-block: 1em;
.variant { display: grid; gap: 0.4em; justify-items: center; inline-size: min(100%, 20rem);
span { font-size: 0.8em; color: var(--sv-gray-3); } }
sv-progress-bar { inline-size: 100%; }
.thick { --sv-progress-bar-thickness: 12px; }
.thin-fill { --sv-progress-bar-track-thickness: 12px; --sv-progress-bar-fill-thickness: 4px; }
.thick-fill { --sv-progress-bar-track-thickness: 4px; --sv-progress-bar-fill-thickness: 12px; }
.square { --sv-progress-bar-rad: 0; }
.tinted { --sv-progress-bar-fill-clr: #f59e0b; --sv-progress-bar-track-clr: color-mix(in srgb, #f59e0b 20%, transparent); } }</style>