Documentation
Patterns that stay smooth.
Every pattern ships as a web component with a React wrapper, plus a plain-CSS theme. One install, 26 compositor-friendly animations.
Installation
npm install loading-state-zooThree entry points, same package. Importing any of them auto-registers every custom element exactly once — never call defineAll() yourself.
| Purpose | Import |
|---|---|
Framework-agnostic web components | import "loading-state-zoo"; |
React components | import { BouncingDots } from "loading-state-zoo/react"; |
Plain CSS + hand-written markup | link "loading-state-zoo/theme.css" |
Usage
Drop a tag or component anywhere. Web components are server-render safe — on the server the tags stay inert and never throw.
<!-- Plain HTML / any framework -->
<lz-wave-dots size="12" color="#0a84ff"></lz-wave-dots>
<!-- Or React -->
import { WaveDots, ArcSpinner } from "loading-state-zoo/react";
export default () => <WaveDots size={12} color="#0a84ff" />;Multi-word attributes are camelCased in React: stroke-width → strokeWidth, track-color → trackColor, bar-width → barWidth, color-weak → colorWeak.
Theming
Styles live inside each element‘s shadow DOM, so you theme with CSS variables on the host or any ancestor — never by reaching in.
| Variable | Default | Purpose |
|---|---|---|
--lz-color | #f5f5f7 | Foreground color on every pattern |
--lz-accent | #0a84ff | Accent color (morphing square, progress bar) |
--lz-speed | 1 | Duration multiplier (0.5 = faster, 2 = slower) |
--lz-stroke | 4px | Ring stroke width |
--lz-surface | #1c1c1e | Track / skeleton background |
--lz-track | rgba(255,255,255,0.15) | Ring and bar track color |
<!-- Theme a single element -->
<lz-arc-spinner style="--lz-color:#0a84ff; --lz-speed:0.8"></lz-arc-spinner>
<!-- React: pass vars via style -->
<ArcSpinner style={{ "--lz-color": "#0a84ff", "--lz-speed": 0.8 } as React.CSSProperties} />The patterns
26 patterns in five families. All animate only transform and opacity, so they stay at 60fps on phones.
Dots
BouncingDots
lz-bouncing-dotsThree round dots that hop up and down with a stagger. loading a feed · small section refresh
| Attribute | Type | Default |
|---|---|---|
size | number | 8 |
color | string | #f5f5f7 |
speed | number | 1 |
| Variable | Default | Purpose |
|---|---|---|
--lz-bounce | -10px | Dot hop height |
<lz-bouncing-dots size="8"></lz-bouncing-dots>import { BouncingDots } from "loading-state-zoo/react";
<BouncingDots size={8} />TypingDots
lz-typing-dotsThree dots pulsing in a typing rhythm. chat · messaging · typing indicator
| Attribute | Type | Default |
|---|---|---|
size | number | 8 |
color | string | #f5f5f7 |
speed | number | 1 |
| Variable | Default | Purpose |
|---|---|---|
--lz-typing-min | 0.35 | Resting dot scale |
--lz-typing-peak | 1.15 | Peak dot scale |
<lz-typing-dots size="8"></lz-typing-dots>import { TypingDots } from "loading-state-zoo/react";
<TypingDots size={8} />WaveDots
lz-wave-dotsFour dots swelling across like a wave. refresh · sync in progress
| Attribute | Type | Default |
|---|---|---|
size | number | 8 |
color | string | #f5f5f7 |
speed | number | 1 |
| Variable | Default | Purpose |
|---|---|---|
--lz-wave-min | 0.35 | Resting dot scale |
--lz-wave-peak | 1.1 | Peak dot scale |
<lz-wave-dots size="8"></lz-wave-dots>import { WaveDots } from "loading-state-zoo/react";
<WaveDots size={8} />HelixDots
lz-helix-dotsA row of dots bobbing like a travelling wave (helix). data processing · playful waits
| Attribute | Type | Default |
|---|---|---|
size | number | 6 |
count | number | 10 |
color | string | #f5f5f7 |
speed | number | 1 |
| Variable | Default | Purpose |
|---|---|---|
--lz-helix | -12px | Dot bob distance |
<lz-helix-dots size="6" count="10"></lz-helix-dots>import { HelixDots } from "loading-state-zoo/react";
<HelixDots size={6} count={10} />DotGridWave
lz-dot-grid-waveA grid of dots rippling outward from the top-left corner. dashboards · brick layouts · empty panels
| Attribute | Type | Default |
|---|---|---|
size | number | 6 |
rows | number | 4 |
cols | number | 5 |
gap | number | 12 |
color | string | #f5f5f7 |
speed | number | 1 |
| Variable | Default | Purpose |
|---|---|---|
--lz-grid-min | 0.3 | Resting dot scale |
--lz-grid-fade | 0.15 | Resting dot opacity |
<lz-dot-grid-wave size="6" rows="4" cols="5" gap="12"></lz-dot-grid-wave>import { DotGridWave } from "loading-state-zoo/react";
<DotGridWave size={6} rows={4} cols={5} gap={12} />Ellipsis
lz-ellipsisThree periods appearing in a typing rhythm. chat · messaging · text queues
| Attribute | Type | Default |
|---|---|---|
size | number | 8 |
color | string | #f5f5f7 |
speed | number | 1 |
| Variable | Default | Purpose |
|---|---|---|
--lz-ellipsis-min | 0.8 | Resting dot scale |
--lz-ellipsis-fade | 0.15 | Resting dot opacity |
<lz-ellipsis size="8"></lz-ellipsis>import { Ellipsis } from "loading-state-zoo/react";
<Ellipsis size={8} />Spinners
ArcSpinner
lz-arc-spinnerOne spinning arc ring (classic circular loader). any circular wait
| Attribute | Type | Default |
|---|---|---|
size | number | 44 |
strokeWidth (web: stroke-width) | number | 4 |
color | string | #f5f5f7 |
speed | number | 1 |
<lz-arc-spinner size="44" stroke-width="4"></lz-arc-spinner>import { ArcSpinner } from "loading-state-zoo/react";
<ArcSpinner size={44} strokeWidth={4} />MaterialDoubleArc
lz-material-double-arcTwo arcs spinning in opposite directions (Material look). android-style apps · processing
| Attribute | Type | Default |
|---|---|---|
size | number | 44 |
strokeWidth (web: stroke-width) | number | 4 |
color | string | #f5f5f7 |
colorWeak (web: color-weak) | string | 95% tint of color |
speed | number | 1 |
<lz-material-double-arc size="44" stroke-width="4"></lz-material-double-arc>import { MaterialDoubleArc } from "loading-state-zoo/react";
<MaterialDoubleArc size={44} strokeWidth={4} />ScanSweep
lz-scan-sweepConcentric rings with crosshair ticks, a counter-rotating dial and a sweeping radar beam. scanning · searching · syncing
| Attribute | Type | Default |
|---|---|---|
size | number | 44 |
color | string | #f5f5f7 |
colorWeak (web: color-weak) | string | 80% tint of color |
colorSweep (web: color-sweep) | string | 8% tint of color |
speed | number | 1 |
<lz-scan-sweep size="44"></lz-scan-sweep>import { ScanSweep } from "loading-state-zoo/react";
<ScanSweep size={44} />OrbitDots
lz-orbit-dotsThree dots gliding around an invisible circle. light floaty waits
| Attribute | Type | Default |
|---|---|---|
size | number | 44 |
color | string | #f5f5f7 |
speed | number | 1 |
<lz-orbit-dots size="44"></lz-orbit-dots>import { OrbitDots } from "loading-state-zoo/react";
<OrbitDots size={44} />OrbitRingDots
lz-orbit-ring-dotsEight dots around a ring chasing each other with fading opacity. elegant circular waits · brand moments
| Attribute | Type | Default |
|---|---|---|
size | number | 44 |
dotSize (web: dot-size) | number | 0.18 x size |
color | string | #f5f5f7 |
speed | number | 1 |
| Variable | Default | Purpose |
|---|---|---|
--lz-orbit-fade | 0.12 | Resting dot opacity |
<lz-orbit-ring-dots size="44" dot-size="7.92"></lz-orbit-ring-dots>import { OrbitRingDots } from "loading-state-zoo/react";
<OrbitRingDots size={44} dotSize={7.92} />Sonar
lz-sonarA pulsing core emitting expanding sonar rings. radar pings · location detection · motion waits
| Attribute | Type | Default |
|---|---|---|
size | number | 44 |
count | number | 3 |
accent | string | #0a84ff |
color | string | #f5f5f7 |
speed | number | 1 |
| Variable | Default | Purpose |
|---|---|---|
--lz-sonar-fade | 0.9 | Ring start opacity |
<lz-sonar size="44" count="3"></lz-sonar>import { Sonar } from "loading-state-zoo/react";
<Sonar size={44} count={3} />Shapes
Eyes
lz-eyesTwo cartoon eyes that look side to side and blink. fun apps · kids' stuff · watching moments
| Attribute | Type | Default |
|---|---|---|
size | number | 44 |
color | string | #f5f5f7 |
speed | number | 1 |
| Variable | Default | Purpose |
|---|---|---|
--lz-gaze-from | -3px | Pupil start offset |
--lz-gaze-to | 3px | Pupil end offset |
--lz-blink | 0.08 | Blink closure scale |
<lz-eyes size="44"></lz-eyes>import { Eyes } from "loading-state-zoo/react";
<Eyes size={44} />MorphingSquare
lz-morphing-squareA glowing square that swells, rounds and spins. modern splash states · brand colors
| Attribute | Type | Default |
|---|---|---|
size | number | 44 |
accent | string | #0a84ff |
speed | number | 1 |
| Variable | Default | Purpose |
|---|---|---|
--lz-morph | 0.72 | Smallest scale |
--lz-morph-radius | 17% | Corner roundness at peak |
<lz-morphing-square size="44"></lz-morphing-square>import { MorphingSquare } from "loading-state-zoo/react";
<MorphingSquare size={44} />BreathingPulse
lz-breathing-pulseA soft circle that gently grows and shrinks. subtle alive waits · background states
| Attribute | Type | Default |
|---|---|---|
size | number | 36 |
color | string | #f5f5f7 |
speed | number | 1 |
| Variable | Default | Purpose |
|---|---|---|
--lz-breath | 1.14 | Growth scale |
<lz-breathing-pulse size="36"></lz-breathing-pulse>import { BreathingPulse } from "loading-state-zoo/react";
<BreathingPulse size={36} />Bars & rings
Equalizer
lz-equalizerFour bars dancing like a music equalizer. audio · music · waveform
| Attribute | Type | Default |
|---|---|---|
size | number | 22 |
barWidth (web: bar-width) | number | 3 |
gap | number | 3 |
color | string | #f5f5f7 |
speed | number | 1 |
| Variable | Default | Purpose |
|---|---|---|
--lz-eq-min | 0.3 | Shortest bar scale |
<lz-equalizer size="22" bar-width="3" gap="3"></lz-equalizer>import { Equalizer } from "loading-state-zoo/react";
<Equalizer size={22} barWidth={3} gap={3} />ProgressRing
lz-progress-ringA ring that fills to a driven percentage. Static — set percent. uploads · downloads · known percent tasks
| Attribute | Type | Default |
|---|---|---|
size | number | 44 |
strokeWidth (web: stroke-width) | number | 4 |
percent | number | 0 |
color | string | #f5f5f7 |
trackColor (web: track-color) | string | rgba(255,255,255,0.15) |
speed | number | 1 |
<lz-progress-ring size="44" stroke-width="4" percent="0"></lz-progress-ring>import { ProgressRing } from "loading-state-zoo/react";
<ProgressRing size={44} strokeWidth={4} percent={0} />ProgressBar
lz-progress-barA determinate linear progress bar filling to a driven percentage. uploads · downloads · known percent tasks
| Attribute | Type | Default |
|---|---|---|
width | number | 160 |
height | number | 6 |
percent | number | 0 |
color | string | #f5f5f7 |
trackColor (web: track-color) | string | rgba(255,255,255,0.15) |
speed | number | 1 |
<lz-progress-bar width="160" height="6" percent="0"></lz-progress-bar>import { ProgressBar } from "loading-state-zoo/react";
<ProgressBar width={160} height={6} percent={0} />IndeterminateProgressBar
lz-indeterminate-progress-barA bar with a slider sliding back and forth (unknown progress). long unknown tasks
| Attribute | Type | Default |
|---|---|---|
width | number | 160 |
height | number | 6 |
accent | string | #0a84ff |
trackColor (web: track-color) | string | #1c1c1e |
speed | number | 1 |
| Variable | Default | Purpose |
|---|---|---|
--lz-slide-from | -100% | Slider start |
--lz-slide-to | 300% | Slider end |
<lz-indeterminate-progress-bar width="160" height="6"></lz-indeterminate-progress-bar>import { IndeterminateProgressBar } from "loading-state-zoo/react";
<IndeterminateProgressBar width={160} height={6} />ActivityRings
lz-activity-ringsThree Apple Watch-style activity rings spinning at different speeds and directions. fitness apps · health dashboards · workout sync
| Attribute | Type | Default |
|---|---|---|
size | number | 44 |
accent | string | #fa114f |
color | string | #30d158 |
colorWeak (web: color-weak) | string | #32d7ff |
speed | number | 1 |
<lz-activity-rings size="44" accent="#fa114f" color="#30d158" color-weak="#32d7ff"></lz-activity-rings>import { ActivityRings } from "loading-state-zoo/react";
<ActivityRings size={44} accent={"#fa114f"} color={"#30d158"} colorWeak={"#32d7ff"} />Music
Waveform
lz-waveformA mirrored audio waveform pulsing around a center line. audio apps · playing states · podcast waits
| Attribute | Type | Default |
|---|---|---|
size | number | 32 |
count | number | 11 |
color | string | #f5f5f7 |
accent | string | #0a84ff |
speed | number | 1 |
| Variable | Default | Purpose |
|---|---|---|
--lz-wf-min | 0.22 | Resting bar scale |
<lz-waveform size="32" count="11"></lz-waveform>import { Waveform } from "loading-state-zoo/react";
<Waveform size={32} count={11} />Vinyl
lz-vinylA spinning record with a light sheen, label, spindle and a gently bobbing tonearm. music apps · retro waits · album screens
| Attribute | Type | Default |
|---|---|---|
size | number | 48 |
accent | string | #0a84ff |
color | string | #f5f5f7 |
speed | number | 1 |
<lz-vinyl size="48"></lz-vinyl>import { Vinyl } from "loading-state-zoo/react";
<Vinyl size={48} />MusicNote
lz-music-noteDrawn musical notes with heads, stems and flags bobbing in a sing-song rhythm. music players · karaoke · playful waits
| Attribute | Type | Default |
|---|---|---|
size | number | 32 |
count | number | 2 |
color | string | #f5f5f7 |
speed | number | 1 |
| Variable | Default | Purpose |
|---|---|---|
--lz-note-bob | 0.35em | Note bob height |
<lz-music-note size="32" count="2"></lz-music-note>import { MusicNote } from "loading-state-zoo/react";
<MusicNote size={32} count={2} />Skeletons
SkeletonShimmer
lz-skeleton-shimmerA rounded block with a light sweeping across it. image/profile/card placeholders
| Attribute | Type | Default |
|---|---|---|
width | number | 120 |
height | number | 28 |
trackColor (web: track-color) | string | #1c1c1e |
speed | number | 1 |
| Variable | Default | Purpose |
|---|---|---|
--lz-sheen-from | -120% | Sheen start |
--lz-sheen-to | 220% | Sheen end |
<lz-skeleton-shimmer width="120" height="28"></lz-skeleton-shimmer>import { SkeletonShimmer } from "loading-state-zoo/react";
<SkeletonShimmer width={120} height={28} />SkeletonCard
lz-skeleton-cardA composite card skeleton with an avatar circle and text lines under one shared sheen. profile cards · feed items · user rows
| Attribute | Type | Default |
|---|---|---|
width | number | 200 |
height | number | 64 |
trackColor (web: track-color) | string | #1c1c1e |
speed | number | 1 |
| Variable | Default | Purpose |
|---|---|---|
--lz-sheen-from | -120% | Sheen start |
--lz-sheen-to | 220% | Sheen end |
<lz-skeleton-card width="200" height="64"></lz-skeleton-card>import { SkeletonCard } from "loading-state-zoo/react";
<SkeletonCard width={200} height={64} />SkeletonList
lz-skeleton-listRepeated list rows with avatars and text lines under one shared sheen. chat lists · inbox rows · search results
| Attribute | Type | Default |
|---|---|---|
width | number | 180 |
rows | number | 4 |
trackColor (web: track-color) | string | #1c1c1e |
speed | number | 1 |
| Variable | Default | Purpose |
|---|---|---|
--lz-sheen-from | -120% | Sheen start |
--lz-sheen-to | 220% | Sheen end |
<lz-skeleton-list width="180" rows="4"></lz-skeleton-list>import { SkeletonList } from "loading-state-zoo/react";
<SkeletonList width={180} rows={4} />Deterministic progress
ProgressRing and ProgressBar are deterministic. Feed them your real upload, download, or task value via percent (clamped 0–100). Re-rendering updates the fill and aria-valuenow, so screen readers get live progress.
<ProgressRing percent={65} size={56} strokeWidth={6} color="#0a84ff" />
<ProgressBar percent={65} width={220} height={8} color="#0a84ff" />Motion guidelines
- Animate transforms and opacity only — never layout properties.
- Spinners for actions under 5s; skeletons for content fetches.
- Determinate progress only when you actually know the value.
- Keep cycle times between 0.8s and 1.4s.