Skip to content

Progress

Progress shows that work is happening: a spinner when the wait has no end in sight, a bar or a ring when it does, and three dots when a reply is being written.

On this page
  1. Preview
  2. Spinner
  3. Progress Bar
  4. Progress Ring
  5. Typing Indicator
  6. Properties
  7. Tokens
  8. iOS and Material 3
  9. Spinner or bar

Preview

Two ways to say the app is busy. Spinner when you cannot tell how long, bar when you can. On iOS both are ProgressView, on Android CircularProgressIndicator and LinearProgressIndicator.

Spinner base 24, Progress Bar at 50both on brand/base over bg/soft-100

The spinner has 8 frames at 80 ms, so a full turn takes 640 ms. In code that is one rotating arc, and Reduce Motion stops it.

Progress Ring shows a known total as a circle and Typing Indicator shows three dots for a reply that is being written. Both have their own section below.

Source: the Progress page of the Appetite UI Figma file, read on October 2, 2026. Four sets sit on it, one per section.

Which sizes does Spinner come in?

Spinner has four sizes crossed with eight frames, 32 variants. The arc is identical in all of them and only the box changes.

sm 16, md 20, base 24, lg 32ring is 28 percent of the radius: 2.24, 2.8, 3.36, 4.48
Frame 1 to 8, held still45 degrees apart, eight of them close the circle

Every frame is a track and a 270 degree indicator, both at an inner radius of 0.72, so the ring is 28 percent of the radius and scales with the box.

The frames exist so the animation can live in Figma. In code, rotate one arc and never expose Frame.

How big is a Progress Bar?

Progress Bar is 320 by 8, clipping, with an indicator inside it. Five variants set the width and nothing else changes.

Value 0, 25, 50, 75, 100indicator widths 0.01, 80, 160, 240, 320

At Value 0 the indicator is a real node 0.01 wide, so it rounds to nothing but is still there.

Value is a five stop axis, so real progress means overriding the width on the instance. Bind it to the real number and never animate it for decoration.

What is Progress Ring for?

The bar as a circle, for one figure that deserves room. A 48 box, five values and an optional percent label.

Value 0, 25, 50, 75, 10048 x 48, track and indicator 40 at stroke 4
0%25%50%75%100%

Progress Ring is a 48 box with a 40 circle drawn at stroke 4: the track in bg/soft-100 and the indicator in brand/base with round caps, starting at 12 o'clock. The Show label boolean prints the percent in the middle in xs/emphasized and is on by default.

At Value 0 the indicator is hidden. The ring resizes freely, so draw it at the size the screen needs and keep the label only where it still fits.

Show label off, at 24 and 32boxes 24 and 32, the stroke stays 4

What is Typing Indicator for?

Three dots that say a reply is being written. It loops with no value, for chat and assistant replies.

Typing Indicator, running, then Frame 1 to 4 held still40 x 14, dots 8 on a pitch of 16, one cycle 1200 ms

The set is 40 by 14: three dots of 8 on a pitch of 16, in fg/soft-500. The active dot stands at the top at full opacity and the other two rest 3 lower at 35 percent.

Frame 1 raises dot 1, Frame 2 dot 2, Frame 3 dot 3 and Frame 4 none. Each frame is 100 ms of dwell and 200 ms of travel on linear easing, so one cycle takes 1.2 seconds.

Pair it with AI / Message set to Streaming, described on the AI component page. With Reduce Motion on, the first stage specimen rests on Frame 1.

Properties

Four separate sets. Spinner and Typing Indicator animate through a Frame axis, Progress Bar and Progress Ring through Value.

Properties, property, type, default
PropertyTypeDefault
Sizesm, md, base, lg, on Spinnersm
Frame1, 2, 3, 4, 5, 6, 7, 8, on Spinner1
Value0, 25, 50, 75, 100, on Progress Bar0
Value0, 25, 50, 75, 100, on Progress Ring0
Show labelBoolean, on Progress Ringtrue
Frame1, 2, 3, 4, on Typing Indicator1
Properties, set, variants, size
SetVariantsSize
Spinner32, Size crossed with Frame16, 20, 24, 32 square
Progress Bar5, on Value320 x 8
Progress Ring5, on Value48 x 48
Typing Indicator4, on Frame40 x 14

Frame is a timeline. Expose Size and Value in code and keep Frame inside the animation.

No set carries a buffer track or an indeterminate bar.

Tokens

Four colors across the four sets.

Tokens, token, light, dark, applied to
TokenLightDarkApplied to
bg/soft-100#E9E9EA#48484ASpinner track, bar track, ring track
brand/base#155DFC#2B7FFFSpinner arc, bar indicator, ring indicator
text/strong-950#0E0E0F#FFFFFFRing label
fg/soft-500#8E8E93#AEAEB2Typing Indicator dots

In light the track resolves to colors/gray/100, in dark to colors/gray/700. Brand goes one step lighter in dark.

No set has a success, warning or error color.

How does Progress differ on iOS and Material 3?

Both platforms ship a spinner and a bar and animate them natively. Appetite UI draws the same two, adds a ring and a typing indicator, and the file has no Material 3 progress page to compare.

Progress against its iOS and Material 3 counterparts, component, iOS, Material 3, difference
Appetite UIiOS counterpartMaterial 3 counterpartWhat differs
SpinneriOS / Spinner in the Apple pages, Small 14, Regular 20 and Large 35. In code SwiftUI ProgressView() or UIKit UIActivityIndicatorViewCircularProgressIndicator, indeterminateAppetite UI holds eight frames at 80 ms on a 270 degree arc. The platforms animate it natively, so in code rotate one arc.
Progress BariOS / Progress Bar in the Apple pages, 370 x 4 at Value 0 to 100 percent. In code ProgressView(value:total:)LinearProgressIndicatorAppetite UI is 320 by 8 at radius full with five Values to design against. Bind the real number in code.
Progress RingNo built in ring. A Gauge or a custom progress style draws one.CircularProgressIndicator with a progress valueAppetite UI draws a 48 box with a stroke of 4, round caps and an optional percent label.
Typing IndicatorNone. Messages draws its own typing bubble.NoneA loop of three dots, 1.2 seconds a cycle, for chat and assistant replies.

None of the four is a control, so none has a touch target. Anything a person can tap next to them, such as a cancel button, keeps the minimum of 44 pt on iOS and 48 dp on Android, set by platform/min-touch-target in the Platform collection.

When should I use a spinner and when a bar?

Ask one question: do you know the total? If you do, use the bar or the ring bound to the real number. If you do not, use the spinner.

Do

Let the spinner promise nothing but that something is happening. Sign in, a network call, a sync with no total: that is exactly the wait it was drawn for.

Don't

Never put a percentage next to the spinner. If you can count it, the bar shows it properly. If you cannot, the number is a guess and people will hold you to it.

68%

For a flow with a known number of steps, use Progress Steps. Uploads, downloads and multi step forms know the total, so they get the bar or the ring.

Everything else gets the spinner. Use the smallest size the surface allows, keep one on screen at a time, and past a few seconds say what is taking so long.

Motion alone says nothing to a screen reader. In code give a bar or ring a progressbar role with its value and a name, and announce that a spinner means loading.

With Reduce Motion on, swap the spin for a static arc or a line of text, and let the typing dots rest on Frame 1.