WavyProgress
Wavy progress
The Material 3 Expressive traveling-wave/rippling-ring treatment, determinate at a few fractions (watch the amplitude ramp down near 0% and 100%) and self-animating indeterminate.
Linear — determinate
Linear — custom max
Linear — indeterminate
Circular
import { Surface, Text, WavyProgress } from '@language-lit/material3-expressive'
export function WavyProgressExample() {
return (
<Surface
as="section"
aria-labelledby="wavy-progress-example-title"
color="surface-container-low"
shape="extra-large"
className="wavy-progress-example"
>
<Text as="h2" id="wavy-progress-example-title" variant="titleLarge" emphasis="emphasized">
Wavy progress
</Text>
<Text as="p" variant="bodyMedium">
The Material 3 Expressive traveling-wave/rippling-ring treatment,
determinate at a few fractions (watch the amplitude ramp down near
0% and 100%) and self-animating indeterminate.
</Text>
<Text as="p" variant="labelLarge">Linear — determinate</Text>
<WavyProgress aria-label="Upload progress, empty" value={0} />
<WavyProgress aria-label="Upload progress, low" value={0.05} />
<WavyProgress aria-label="Upload progress, mid" value={0.45} />
<WavyProgress aria-label="Upload progress, high" value={0.98} />
<WavyProgress aria-label="Upload progress, complete" value={1} />
<Text as="p" variant="labelLarge">Linear — custom max</Text>
<WavyProgress aria-label="Upload progress, custom max" value={150} max={200} />
<Text as="p" variant="labelLarge">Linear — indeterminate</Text>
<WavyProgress aria-label="Syncing" />
<Text as="p" variant="labelLarge">Circular</Text>
<div className="wavy-progress-example__row">
<WavyProgress aria-label="Progress, empty" shape="circular" value={0} />
<WavyProgress aria-label="Progress, low" shape="circular" value={0.05} />
<WavyProgress aria-label="Progress, mid" shape="circular" value={0.45} />
<WavyProgress aria-label="Progress, high" shape="circular" value={0.98} />
<WavyProgress aria-label="Progress, complete" shape="circular" value={1} />
<WavyProgress aria-label="Progress, custom max" shape="circular" value={150} max={200} />
<WavyProgress aria-label="Syncing" shape="circular" />
</div>
</Surface>
)
}WavyProgress renders the Material 3 Expressive traveling-wave treatment:
a smooth quadratic wave along a bar (shape="linear", the default) or a
rounded nine-point ripple around a ring (shape="circular"). Pass value
for determinate progress, or omit it entirely for a continuously animating
indeterminate wave — the same contract a native <progress> element uses.
import { WavyProgress } from '@language-lit/material3-expressive'
import '@language-lit/material3-expressive/styles.css'
<WavyProgress aria-label="Upload progress" value={0.4} />
<WavyProgress aria-label="Progress" shape="circular" value={0.4} />
<WavyProgress aria-label="Syncing" />Contract
shape: 'linear' | 'circular'(default'linear') selects the orientation.value(a number in[0, max]) renders determinate progress with an accuratearia-valuenow; near0%/100%progress the wave's amplitude ramps down to a nearly flat line/ring. Omittingvaluerenders indeterminate mode (noaria-valuenow), always at full amplitude.maxdefaults to1.aria-label/aria-labelledbyprovide the required accessible name.shape="linear"'sinline-sizedefaults to100%;shape="circular"has a fixed48pxintrinsic size — the same width-flexibility splitLinearProgress/CircularProgressuse.- The linear wave reserves half of its
4pxstroke at both edges of the10pxcontainer, so crests and troughs remain complete. The circular wave uses matched circle/star cubic paths inside a stroke-safe44pxdrawing area and rotates around the fixed24pxview-box center. A synchronized dash shift keeps the circular sweep endpoints in place while its lobes travel. - At the sourced amplitude thresholds, determinate geometry morphs between flat and full-wave paths while preserving stroke width; no SVG scaling or opacity cross-fade is used.
- Circular indeterminate mode keeps Material's visible circle track, rotating it with the active sweep and continuously preserving the cap-aware gap.
- For the plain (non-wavy) treatment, see
LinearProgress/CircularProgress.
Tokens and boundaries
Colors, geometry, and motion timing come entirely from --m3e-comp-wavy- progress-* CSS custom properties, scoped by Material3Provider.
WavyProgress injects no runtime styles, uses no requestAnimationFrame
loop (every animation — wave travel, indeterminate motion, amplitude
ramp — is pure CSS transform/@keyframes/transition), and imports no Next.js, Vite, router, animation library, or private application
code.