Progress
{% progress %} is a generic, presentational completion bar. You give it numbers — a value/max pair or a direct percent — and it renders a labelled bar. It computes nothing itself: the data is always supplied, either inline or fed from an aggregate (see Feeding from data).
{% progress value=3 max=4 %}Acceptance criteria{% /progress %}<p>
<div data-rune="progress" role="progressbar" aria-valuemin="0" aria-valuenow="3" aria-valuemax="4" aria-label="Acceptance criteria" style="--rf-progress: 75%">
<span data-name="label">Acceptance criteria</span>
<span data-name="value">3/4</span>
<span data-name="track">
<span data-name="fill"></span>
</span>
</div>
</p><p>
<div class="rf-progress" role="progressbar" aria-valuemin="0" aria-valuenow="3" aria-valuemax="4" aria-label="Acceptance criteria" style="--rf-progress: 75%" data-rune="progress" data-density="full">
<span data-name="label" class="rf-progress__label">Acceptance criteria</span>
<span data-name="value" class="rf-progress__value">3/4</span>
<span data-name="track" class="rf-progress__track">
<span data-name="fill" class="rf-progress__fill"></span>
</span>
</div>
</p>Input
Two ways to express the ratio:
value+max(primary) — a count, e.g.value=12 max=20. Yields a "12/20" readout.percent(alternative) — a direct0–100value when there's no count.
If both are given, value/max wins. The percentage is clamped to 0–100; a max of 0 (or absent) renders an empty bar with no numeric readout — never NaN.
{% progress percent=60 /%}<div data-rune="progress" role="progressbar" aria-valuemin="0" aria-valuenow="60" aria-valuemax="100" style="--rf-progress: 60%">
<span data-name="value">60%</span>
<span data-name="track">
<span data-name="fill"></span>
</span>
</div><div class="rf-progress" role="progressbar" aria-valuemin="0" aria-valuenow="60" aria-valuemax="100" style="--rf-progress: 60%" data-rune="progress" data-density="full">
<span data-name="value" class="rf-progress__value">60%</span>
<span data-name="track" class="rf-progress__track">
<span data-name="fill" class="rf-progress__fill"></span>
</span>
</div>Readout — display
display controls the text beside the bar:
display | Output |
|---|---|
fraction (default with value/max) | 12/20 |
percent | 60% |
none | (no readout) |
Label
An optional body is the label — it may hold inline markup, and its text also becomes the bar's accessible name (aria-label):
{% progress percent=80 %}Funding goal{% /progress %}<p>
<div data-rune="progress" role="progressbar" aria-valuemin="0" aria-valuenow="80" aria-valuemax="100" aria-label="Funding goal" style="--rf-progress: 80%">
<span data-name="label">Funding goal</span>
<span data-name="value">80%</span>
<span data-name="track">
<span data-name="fill"></span>
</span>
</div>
</p><p>
<div class="rf-progress" role="progressbar" aria-valuemin="0" aria-valuenow="80" aria-valuemax="100" aria-label="Funding goal" style="--rf-progress: 80%" data-rune="progress" data-density="full">
<span data-name="label" class="rf-progress__label">Funding goal</span>
<span data-name="value" class="rf-progress__value">80%</span>
<span data-name="track" class="rf-progress__track">
<span data-name="fill" class="rf-progress__fill"></span>
</span>
</div>
</p>Sentiment
sentiment tints the fill (positive / caution / negative), reusing the theme's sentiment colors. When it's absent the bar uses the neutral primary fill. There is no automatic threshold coloring — the sentiment is always explicit.
{% progress value=6 max=10 %}Default{% /progress %}
{% progress value=9 max=10 sentiment="positive" %}Positive{% /progress %}
{% progress value=4 max=10 sentiment="caution" %}Caution{% /progress %}
{% progress value=2 max=10 sentiment="negative" %}Negative{% /progress %}<p>
<div data-rune="progress" role="progressbar" aria-valuemin="0" aria-valuenow="6" aria-valuemax="10" aria-label="Default" style="--rf-progress: 60%">
<span data-name="label">Default</span>
<span data-name="value">6/10</span>
<span data-name="track">
<span data-name="fill"></span>
</span>
</div>
</p>
<p>
<div data-rune="progress" data-rune-fields="{"sentiment":"positive"}" role="progressbar" aria-valuemin="0" aria-valuenow="9" aria-valuemax="10" aria-label="Positive" style="--rf-progress: 90%">
<span data-name="label">Positive</span>
<span data-name="value">9/10</span>
<span data-name="track">
<span data-name="fill"></span>
</span>
</div>
</p>
<p>
<div data-rune="progress" data-rune-fields="{"sentiment":"caution"}" role="progressbar" aria-valuemin="0" aria-valuenow="4" aria-valuemax="10" aria-label="Caution" style="--rf-progress: 40%">
<span data-name="label">Caution</span>
<span data-name="value">4/10</span>
<span data-name="track">
<span data-name="fill"></span>
</span>
</div>
</p>
<p>
<div data-rune="progress" data-rune-fields="{"sentiment":"negative"}" role="progressbar" aria-valuemin="0" aria-valuenow="2" aria-valuemax="10" aria-label="Negative" style="--rf-progress: 20%">
<span data-name="label">Negative</span>
<span data-name="value">2/10</span>
<span data-name="track">
<span data-name="fill"></span>
</span>
</div>
</p><p>
<div class="rf-progress" role="progressbar" aria-valuemin="0" aria-valuenow="6" aria-valuemax="10" aria-label="Default" style="--rf-progress: 60%" data-rune="progress" data-density="full">
<span data-name="label" class="rf-progress__label">Default</span>
<span data-name="value" class="rf-progress__value">6/10</span>
<span data-name="track" class="rf-progress__track">
<span data-name="fill" class="rf-progress__fill"></span>
</span>
</div>
</p>
<p>
<div class="rf-progress rf-progress--positive" role="progressbar" aria-valuemin="0" aria-valuenow="9" aria-valuemax="10" aria-label="Positive" style="--rf-progress: 90%" data-sentiment="positive" data-rune="progress" data-density="full">
<span data-name="label" class="rf-progress__label">Positive</span>
<span data-name="value" class="rf-progress__value">9/10</span>
<span data-name="track" class="rf-progress__track">
<span data-name="fill" class="rf-progress__fill"></span>
</span>
</div>
</p>
<p>
<div class="rf-progress rf-progress--caution" role="progressbar" aria-valuemin="0" aria-valuenow="4" aria-valuemax="10" aria-label="Caution" style="--rf-progress: 40%" data-sentiment="caution" data-rune="progress" data-density="full">
<span data-name="label" class="rf-progress__label">Caution</span>
<span data-name="value" class="rf-progress__value">4/10</span>
<span data-name="track" class="rf-progress__track">
<span data-name="fill" class="rf-progress__fill"></span>
</span>
</div>
</p>
<p>
<div class="rf-progress rf-progress--negative" role="progressbar" aria-valuemin="0" aria-valuenow="2" aria-valuemax="10" aria-label="Negative" style="--rf-progress: 20%" data-sentiment="negative" data-rune="progress" data-density="full">
<span data-name="label" class="rf-progress__label">Negative</span>
<span data-name="value" class="rf-progress__value">2/10</span>
<span data-name="track" class="rf-progress__track">
<span data-name="fill" class="rf-progress__fill"></span>
</span>
</div>
</p>Feeding from data
progress reads only what you pass it, so a live value comes from a variable — typically an aggregate a plugin writes onto an entity. For example, a milestone page fed its completion rollup:
{% progress value=$item.data.progressDone max=$item.data.progressTotal %}Completion{% /progress %}
The rune stays generic; computing progressDone/progressTotal is the data layer's job.
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
value | number | — | Completed amount (paired with max). |
max | number | — | Total amount (paired with value). |
percent | number | — | Direct percentage 0–100, when there's no count. |
display | fraction | percent | none | fraction (with value/max) | The numeric readout. |
sentiment | positive | caution | negative | — | Fill tone cue. Absent → neutral primary fill. |
Output contract
<div class="rf-progress" data-rune="progress"
role="progressbar" aria-valuenow="3" aria-valuemin="0" aria-valuemax="4"
aria-label="Acceptance criteria" style="--rf-progress: 75%">
<span class="rf-progress__label">Acceptance criteria</span>
<span class="rf-progress__value">3/4</span>
<span class="rf-progress__track"><span class="rf-progress__fill"></span></span>
</div>
The fill width is driven by the --rf-progress custom property, so themes restyle the bar without touching the markup.
See also
- collection — pair
progresswith a per-item template to show a bar per entity.