ContentProgress

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>

Acceptance criteria3/4

<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 direct 0–100 value 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>
60%
<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:

displayOutput
fraction (default with value/max)12/20
percent60%
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>

Funding goal80%

<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="{&quot;sentiment&quot;:&quot;positive&quot;}" 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="{&quot;sentiment&quot;:&quot;caution&quot;}" 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="{&quot;sentiment&quot;:&quot;negative&quot;}" 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>

Default6/10

Positive9/10

Caution4/10

Negative2/10

<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

AttributeTypeDefaultDescription
valuenumberCompleted amount (paired with max).
maxnumberTotal amount (paired with value).
percentnumberDirect percentage 0–100, when there's no count.
displayfraction | percent | nonefraction (with value/max)The numeric readout.
sentimentpositive | caution | negativeFill 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 progress with a per-item template to show a bar per entity.