Progress

ui-progress renders a native <progress> element styled as a thin rounded bar. Set value, and optionally max, to show how much of a task is done. The bar is as wide as its container, so size it with a wrapper.

Previewindex.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Progress demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-narrow {
      width: min(100%, calc(var(--spacing) * 88));
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/progress.html"></import-element>

  <div class="demo-narrow">
    <ui-progress value="60" aria-label="Progress"></ui-progress>
  </div>
</body>
</html>

Examples

Custom maximum

max defaults to 100. Set it to the total so value can be a plain count.

Previewindex.html

3 of 8 steps

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Progress demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-narrow {
      width: min(100%, calc(var(--spacing) * 88));
    }
    .demo-stack-tight {
      display: grid;
      gap: calc(var(--spacing) * 2);
    }
    .demo-text {
      margin: 0;
      font-size: var(--text-sm);
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/progress.html"></import-element>

  <div class="demo-narrow demo-stack-tight">
    <p class="demo-text">3 of 8 steps</p>
    <ui-progress value="3" max="8" aria-label="Steps completed"></ui-progress>
  </div>
</body>
</html>

With a label

Show the task name and percentage as text next to the bar.

Previewindex.html

Uploading report.pdf

45%

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Progress demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-narrow {
      width: min(100%, calc(var(--spacing) * 88));
    }
    .demo-stack-tight {
      display: grid;
      gap: calc(var(--spacing) * 2);
    }
    .demo-row-tight {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      gap: calc(var(--spacing) * 2);
    }
    .demo-text {
      margin: 0;
      font-size: var(--text-sm);
    }
    .demo-muted {
      margin: 0;
      font-size: var(--text-sm);
      color: var(--muted-foreground);
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/progress.html"></import-element>

  <div class="demo-narrow demo-stack-tight">
    <div class="demo-row-tight">
      <p class="demo-text">Uploading report.pdf</p>
      <p class="demo-muted">45%</p>
    </div>
    <ui-progress value="45" aria-label="Uploading report.pdf"></ui-progress>
  </div>
</body>
</html>

Driven by state

Bind value with _value. The bar animates between values.

Previewindex.htmlprogress-upload-demo.html
% Advance
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Progress demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/button.html"></import-element>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/progress.html"></import-element>

  <import-element from="./progress-upload-demo.html"></import-element>
  <progress-upload-demo></progress-upload-demo>
</body>
</html>
<custom-element name="progress-upload-demo">
  <script>
    const upload = useState({ percent: 13 });

    function advance() {
      upload.percent = upload.percent >= 100 ? 0 : Math.min(100, upload.percent + 22);
    }
  </script>
  <style>
    :host {
      display: flex;
      align-items: center;
      gap: calc(var(--spacing) * 4);
      width: 70%;
    }
    ui-progress {
      flex: 1;
    }
    span {
      min-width: calc(var(--spacing) * 10);
      font-size: var(--text-sm);
      font-variant-numeric: tabular-nums;
    }
  </style>
  <ui-progress _value="#upload.percent"></ui-progress>
  <span><slot name="#upload.percent"></slot>%</span>
  <ui-button size="sm" variant="outline" onclick="advance()">Advance</ui-button>
</custom-element>

Custom colour

The bar uses var(--primary). Set --primary on a wrapping element to recolour the bar and its track.

Previewindex.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Progress demo</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
  <script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
  <style>
    .demo-narrow {
      width: min(100%, calc(var(--spacing) * 88));
    }
    .demo-stack {
      display: grid;
      gap: calc(var(--spacing) * 4);
    }
  </style>
</head>
<body>
  <import-element from="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/components/progress.html"></import-element>

  <div class="demo-narrow demo-stack">
    <div style="--primary: var(--chart-2)">
      <ui-progress value="80" aria-label="Storage used"></ui-progress>
    </div>
    <div style="--primary: var(--destructive)">
      <ui-progress value="30" aria-label="Battery"></ui-progress>
    </div>
  </div>
</body>
</html>

API

Attributes

Attribute Type Default Description
value number none Current value, minimum 0. When it is left out the bar is indeterminate.
max number 100 Value that means complete, minimum 1.
aria-label string Accessible name, passed to the inner <progress>. Property name: label.

Parts

ui-progress has no child parts. Its default slot takes fallback text inside the native <progress>, for example 45%, which browsers that support <progress> do not render.

CSS parts

Part Description
bar The native <progress> element. Its height is calc(var(--spacing) * 2).

Accessibility

The inner <progress> has the native progressbar role, with its value and maximum exposed to assistive technology. Name it with aria-label on ui-progress, which is passed to the inner bar. A <label for> outside cannot reach into the shadow root. Without value the bar is indeterminate and exposes no value.

Edit this page on GitHub · View as Markdown