Skip to main content
xriptxr

Progress

Info:Feedback
66

A progress bar or capacity meter: a linear bar or circular ring with value thresholds that recolor and pulse.

meter gauge capacity threshold disk battery usage quota score

Live demo

live · @xtyle/astro

Progress

Linear, six tones

accent
64%
neutral
40%
info
52%
success
100%
warn
78%
danger
22%

Caption, reading, and note

Two bars stacked in a panel are told apart by a screen reader and not by eye unless one of them says what it is. label renders the name and announces it; reading is free-form, so it can say things no value format could compute; note explains what the reading means.

The haggle8 of 20 coins
They will come down another two, and no further.
The stallkeep's patiencethinning
Push once more and the price goes back up.
Standing in the marketeven

Standing upright

orient="vertical" grows the indicator up the block axis natively. Rotating a horizontal bar cannot be made correct from outside: a rotated element keeps its pre-rotation layout box, so its visible height is its pre-rotation width and no percentage resolves against the side you can see. The rail takes its length from --xtyle-progress-length, the way a ring takes its diameter from --xtyle-progress-size.

Hold82%
Heat61%
Fuel34%
Hull12%
Mixedramped
Working

Every tone

Accents

accent
accent-2
accent-3
accent-4
neutral

Statuses

success
info
warn
danger

Named hues

red
orange
yellow
green
blue
purple
brown
pink
cyan
gray
white
black

Value readouts

percent
33%
value
123
of max
123/456

Thresholds, tone & pulse

12%
12%
38%
38%
76%
76%

Capacity meter (role="meter")

System 90 GB free of 1 TB
910/1000 GB
Media 1.2 TB free of 2 TB
800/2000 GB
Projects 90 GB free of 500 GB
410/500 GB

Value ramp (ramp)

Solid thermal, colored by value

18%
18%
42%
42%
68%
68%
88%
88%
100%
100%

Gradient sweep (ramp-mode="gradient", pure CSS)

35%
70%
100%

Palettes

intensity
thermal
severity
accents
skittles
26%
26%
58%
58%
90%
90%

Inset readout

66%
92%

Circular ring

64%
accent
40%
neutral
52%
info
100%
success
78%
warn
22%
danger

Groove, weight & size (track, thickness)

A trackless ring as a portrait frame

AL
34%
GH
72%
AT
100%

Grooves: default, dropped (track=), toned

62%
default
62%
none
62%
info
62%
warn

One diameter, four weights (thickness in ring units)

1
2.5
5
9

One weight, three diameters (--xtyle-progress-size with an absolute thickness)

40px
72px
112px

Custom readout (the value slot)

72
done
410 GB of 500 GB

Sizes & indeterminate

Small
Medium
Large
48%
Indeterminate — linear
Indeterminate — circular

In a container that sizes to its content

A linear bar carries its own width, so it fills a table cell or a content-sized grid track rather than collapsing. Nothing here sets a width, and no variant is written: linear is the default and needs no attribute to be true.

Charge
Wear

Progress shows how far along a task is, or how full a capacity is. The variant axis picks the shape (a horizontal linear bar or a circular svg ring) and the tone axis picks the color from the full roster (the six semantic roles, the accent variants, the twelve named hues).

A determinate bar fills to value between min and max; an indeterminate mode animates a moving sweep when the amount of work is unknown. Declarative <threshold below tone pulse> children turn it into a self-coloring meter: each band names a percentage ceiling, and the active band (the first the current value falls under) overrides the tone and can pulse the fill (slow or fast) to flag a critical level, the pulse routed through motion tokens so the reduced-motion base rule stills it. Set meter to report role="meter" (a measurement against a capacity, like disk used) instead of the default role="progressbar" (a task advancing); either way it carries aria-valuenow/aria-valuemin/aria-valuemax. An optional inline readout (show-value) shows the percentage, the raw value, or value/max (value-format), sits at the end or inset over the bar (value-position), and can take the active tone (colorize-value).

When to use

How this component composes with the rest of the set.

Pair with a status Badge or text label to spell out the percentage in words when show-value alone is not enough.
Drive value from an upload/download stream; flip to indeterminate for the indefinite handshake phase.
Use the success tone on completion and danger on a stalled or failed transfer to reinforce state with color.
Wrap a circular ring around an Avatar or Image to make it a frame: track="none" drops the groove, --xtyle-progress-size: 100% sizes it to the container, and thickness sets the band weight independent of the diameter.
Both variants expose ::part(progress), ::part(track), ::part(indicator), and ::part(value), which is the reach an app has into a shadow-rendered element alongside the inherited --xtyle-progress-* properties.

Props

24 props, straight from the manifest.

PropTypeDefaultBindingsDescription
variant ProgressVariant
linear circular
linear
html svelte astro
Shape of the meter: a horizontal bar or a circular ring.
tone FullTone
accent neutral danger success warn info accent-2 accent-3 accent-4 red orange yellow green blue purple brown pink cyan gray white black
accent
html svelte astro
Semantic color of the filled indicator.
size ProgressSize
sm md lg
md
html svelte astro
Meter size; sm thins the bar or shrinks the ring, lg fattens it.
value number 0
html svelte astro
Current amount of work done, clamped between min and max.
min number 0
html svelte astro
Lower bound of the range.
max number 100
html svelte astro
Upper bound of the range.
indeterminate boolean false
html svelte astro
Animates a moving sweep instead of a fixed fill when progress is unknown; drops aria-valuenow.
showValue html: show-value boolean false
html svelte astro
Renders an inline readout beside (or inside) the bar.
valueFormat html: value-format ProgressValueFormat
percent value value-max
percent
html svelte astro
How show-value reads: percent (80%), value (the raw number), or value-max (80/100).
unit string
html svelte astro
A unit appended to the value / value-max readout (e.g. GB); the percent format ignores it (the % is the unit).
valuePosition html: value-position "end" | "inset"
end inset
end
html svelte astro
Where the show-value readout sits: after the bar (end) or laid over the fill (inset).
colorizeValue html: colorize-value boolean false
html svelte astro
Tints the show-value readout with the active tone (the threshold tone when a band is active).
meter boolean false
html svelte astro
Reports role="meter" (a measurement against a capacity, e.g. disk used) instead of role="progressbar" (a task advancing); the visual treatment is unchanged.
ramp Palette | string[]
accents skittles statuses thermal severity intensity
html svelte astro
Colors the fill by its own value along a ramp instead of the flat tone: a built-in palette (intensity, thermal, severity), a JSON array of stop colors (["#00f","#f00"]), or a comma-separated stop list. Reinforces magnitude with temperature, so a fuller bar also reads hotter.
rampMode html: ramp-mode "solid" | "gradient"
solid gradient
solid
html svelte astro
How a ramp paints: solid samples one color at the current value off the live cascade (needs the runtime); gradient paints the whole scale as a pure-CSS sweep clipped to the fill (zero-JS, SSR-safe, linear only, a circular ring falls back to solid).
reverse boolean false
html svelte astro
Flips a ramp end for end (hot-to-cold).
threshold html: <threshold> children <threshold below tone pulse>
html svelte astro
Declarative config children, not a prop: each <threshold below="<pct>" tone="<tone>" pulse="slow|fast"> names a percentage ceiling; the active band (the first the current value falls under, bands sorted ascending) overrides tone and may pulse the fill; place them as direct children of the element.
track boolean | FullTone
none accent neutral danger success warn info accent-2 accent-3 accent-4 red orange yellow green blue purple brown pink cyan gray white black
true
html svelte astro
The unfilled groove behind the indicator: true keeps the default --neutral-bg rail, false (track="none" in HTML) drops it entirely, and a tone paints it that tone's -bg. A ring reporting a window that may not exist reads better trackless, where an always-on groove reads as a border rather than an empty meter.
thickness string | number
html svelte astro
How heavy a circular ring reads, independent of its diameter. A unitless number is in ring units (the ring is a 40-unit viewBox at r=16), so the weight scales with the ring; a CSS length (6px, 0.25rem) is absolute and holds the same apparent weight at any size. Sets --xtyle-progress-stroke, which CSS can set directly instead. Circular only; a linear rail's height still follows size.
label string
html svelte astro
The visible caption above the bar. Distinct from ariaLabel, which names the meter for a screen reader and renders nothing: two bars stacked in a panel are told apart by AT and not by eye unless one of them says what it is. Setting label alone also names the meter, so the string is written once; an explicit ariaLabel still wins.
reading string
html svelte astro
A free-form reading on the caption line, opposite the label. Independent of valueFormat, because a meter's most useful reading is often not a number the component could have computed (8/20, even, yours, 46 left). The built-in showValue readout is a different thing in a different place: it sits on the bar and speaks the number.
note string
html svelte astro
A line of prose under the bar, explaining what the reading means.
orient ProgressOrient
horizontal vertical
horizontal
html svelte astro
Which axis the bar fills along. vertical grows the indicator up the block axis natively, so a standing gauge needs no rotation: a rotated element keeps its pre-rotation layout box, so its visible height is its pre-rotation width and no percentage resolves against the side you can see. The rail takes its length from --xtyle-progress-length, the way a ring takes its diameter from --xtyle-progress-size. Linear only; a ring has no axis to stand up.
ariaLabel html: aria-label string
html svelte astro
Accessible name for the meter, when label is absent or the announced name should differ from the visible one. One of the two is required: a progressbar with no name is not announced.

Appearance

Variants

linear

.xtyle-progress--linear

A horizontal bar that fills left-to-right.

circular

.xtyle-progress--circular

An SVG ring whose stroked arc sweeps clockwise from the top. Its diameter follows size unless --xtyle-progress-size overrides it, and its stroke weight follows thickness / --xtyle-progress-stroke, so a ring can be large and thin or small and heavy.

Sizes

sm

.xtyle-progress--sm

Thin bar / small ring.

md

default
.xtyle-progress

Default.

States

indeterminate

.xtyle-progress--indeterminate

Work of unknown duration; the indicator animates a continuous sweep.

pulse

.xtyle-progress--pulse-slow, .xtyle-progress--pulse-fast

An active <threshold pulse> band; the fill breathes (slow or fast) to flag a critical level, stilled under reduced-motion.

focus-visible

.xtyle-progress:focus-visible

Keyboard focus: a token-colored ring, plus a transparent outline that becomes real in forced-colors mode.

Anatomy

The named parts that make up the component. A ::part() handle is reachable from your own stylesheet; the class beside it is the component's internal selector, which a shadow boundary keeps to itself.

progress

Success:::part(progress) .xtyle-progress

The root element carrying the variant, tone, and size classes. It stacks the caption, the bar, and the note; the role lives on the bar rather than here, so a caption is prose beside the measurement rather than content inside it.

--font-sans --fg-1 --space-1

bar

Success:::part(bar) .xtyle-progress__bar

The measuring element, carrying the progressbar (or meter) role and the ARIA value state, and holding the track, the indicator, and the readout.

--space-2

caption

Success:::part(caption) .xtyle-progress__caption

The line above the bar: the visible label on one side, the free-form reading on the other. Rendered only when one of them is set or slotted.

--text-sm --text-xs --text-body --space-3 --leading-tight

label

Success:::part(label) .xtyle-progress__label

The visible name of the measurement, on the caption line.

--fg-1

reading

Success:::part(reading) .xtyle-progress__reading

The free-form reading opposite the label, tabular so a column of them lines up.

--fg-2

note

Success:::part(note) .xtyle-progress__note

A line of prose under the bar, explaining what the reading means. Rendered only when set or slotted.

--text-xs --fg-2 --leading-normal

track

Success:::part(track) .xtyle-progress__track, .xtyle-progress__track-ring

The unfilled groove the indicator runs along: a bar rail (.xtyle-progress__track) or an SVG ring (.xtyle-progress__track-ring). Both variants expose it as ::part(track), and track can tone it or drop it entirely.

--neutral-bg --radius-full --space-1 --space-2

indicator

Success:::part(indicator) .xtyle-progress__indicator

The filled portion, colored by tone: a bar segment or a stroked arc.

--accent --duration-base --ease-emphasized

value

Success:::part(value) .xtyle-progress__value

The optional inline readout shown when show-value is set.

--text-sm --text-xs --fg-2 --fg-1

Tokens & coverage

What the component consumes, checked live against what the algorithm produces.

Success:fully covered 66/66 consumed tokens produced default register: 310 tokens

Live coverage check against the xtyle-default register (derive(xtyleDefault, { anchors })coverComponent(manifest, register)). Every token this component consumes must be a key the algorithm produces.

--accent --accent-2 --accent-2-bg --accent-3 --accent-3-bg --accent-4 --accent-4-bg --accent-bg --bg-0 --black --black-bg --blue --blue-bg --border-normal --border-thick --brown --brown-bg --cyan --cyan-bg --danger --danger-bg --duration-base --ease-emphasized --ease-standard --fg-0 --fg-1 --fg-2 --font-sans --gray --gray-bg --green --green-bg --info --info-bg --leading-normal --leading-tight --neutral --neutral-bg --neutral-bg --orange --orange-bg --pink --pink-bg --purple --purple-bg --radius-full --radius-sm --red --red-bg --ring --space-1 --space-2 --space-3 --space-6 --space-8 --success --success-bg --text-body --text-sm --text-xs --warn --warn-bg --white --white-bg --yellow --yellow-bg

Slots

value
html svelte astro

Optional custom content for the readout, in place of the built-in show-value text (e.g. a formatted label). The Svelte binding fills it with the readout snippet, since value there is already the numeric prop.

label
html svelte astro

Markup for the caption's name, in place of the plain label string. The Svelte binding fills it with the labelContent snippet.

reading
html svelte astro

Markup for the caption's reading, in place of the plain reading string. The Svelte binding fills it with the readingContent snippet.

note
html svelte astro

Markup for the note under the bar, in place of the plain note string. The Svelte binding fills it with the noteContent snippet.

default
html svelte astro

The <threshold below tone pulse> config children (hidden from view); the bar reads them to recolor and pulse by value.

Accessibility

Exposes role="progressbar" (or role="meter" with the meter attribute) carrying aria-valuemin, aria-valuemax, and aria-valuenow so assistive tech announces the current amount; pick meter for a capacity reading and progressbar for a task in motion.
Threshold recoloring and the pulse animation are presentational; the value the screen reader announces is the same aria-valuenow either way, so a critical level never rides on color or motion alone (pair it with a danger tone word or a label if the level itself must be announced).
indeterminate drops aria-valuenow (the value is unknown) while keeping the min/max bounds.
Requires an accessible name via aria-label; the binding warns at runtime when one is missing.
The visual fill is presentational; progress state lives in the ARIA value attributes, not the geometry.
Focus is shown with a token ring and a transparent outline that the forced-colors base rule promotes to a real system outline.
Motion (the indeterminate sweep) is routed through duration/easing tokens, so the reduced-motion base rule neutralizes it.

Code

Linear and circular

Both shapes carry any tone; add show-value for a readout or indeterminate for unknown work.

<xtyle-progress value="42" aria-label="Upload progress"></xtyle-progress>

<xtyle-progress value="80" tone="success" show-value aria-label="Storage used"></xtyle-progress>

<xtyle-progress variant="circular" value="65" tone="info" show-value aria-label="Sync"></xtyle-progress>

<xtyle-progress variant="circular" indeterminate aria-label="Loading"></xtyle-progress>

A capacity meter with thresholds

Set meter for the measurement role and add <threshold below tone pulse> children: the bar greens under 75%, ambers past it, and reds and pulses once it crosses 90% full, the value/max readout carrying its unit. This is the gauge use once served by a separate Meter.

<xtyle-progress meter value="910" max="1000" show-value value-format="value-max" unit=" GB" colorize-value aria-label="Disk usage">
	<threshold below="75" tone="success"></threshold>
	<threshold below="90" tone="warn"></threshold>
	<threshold below="101" tone="danger" pulse="fast"></threshold>
</xtyle-progress>

Saying what it is, and standing it up

aria-label names a meter for a screen reader and renders nothing, so two bars stacked in a panel are told apart by AT and not by eye. label renders the name and announces it, reading is free-form so it can say what no value format could compute (even, 46 left), and note explains the reading. orient="vertical" grows the fill up the block axis natively; give the host a height and the rail fills it.

<xtyle-progress
	label="The haggle"
	reading="8 of 20 coins"
	note="They will come down another two, and no further."
	value="40"
></xtyle-progress>

<xtyle-progress label="Standing" reading="even" value="50" tone="info"></xtyle-progress>

<div style="--xtyle-progress-length: 9rem">
	<xtyle-progress orient="vertical" label="Fuel" reading="34%" value="34" tone="info"></xtyle-progress>
</div>

A fill that colors by its value

Set ramp to color the fill along a scale by its own value instead of a flat tone: solid (the default) samples one color at the current value, so a busier bar reads hotter; ramp-mode="gradient" sweeps the whole scale as pure CSS. Both track the theme's own hues, so the ramp restyles with the algorithm.

<xtyle-progress ramp="thermal" value="30" aria-label="Load, cool"></xtyle-progress>
<xtyle-progress ramp="thermal" value="70" aria-label="Load, warm"></xtyle-progress>
<xtyle-progress ramp="thermal" value="95" aria-label="Load, hot"></xtyle-progress>

<xtyle-progress ramp="thermal" ramp-mode="gradient" value="80" aria-label="Capacity sweep"></xtyle-progress>

A ring as a frame

A circular meter sized to its container reads as a frame rather than a widget. track="none" drops the groove (an always-on rail around a portrait reads as a border, not an empty meter), --xtyle-progress-size takes the diameter off the size scale, and thickness sets the band weight independent of that diameter, so a large ring can stay thin. Both variants expose ::part(track), so a mod or an app can reach the groove either way.

<style>
	.portrait { position: relative; width: 96px; height: 96px; }
	.portrait xtyle-progress { display: block; position: absolute; inset: 0; --xtyle-progress-size: 100%; }
</style>

<div class="portrait">
	<xtyle-avatar src="/portrait.jpg" alt="Ada" size="lg"></xtyle-avatar>
	<xtyle-progress variant="circular" value="72" track="none" thickness="4px" tone="success" aria-label="Profile complete"></xtyle-progress>
</div>

<xtyle-progress variant="circular" value="40" track="info" thickness="1.5" aria-label="A toned groove, thin band"></xtyle-progress>