01
File-list skeleton
Skeleton
Skeletons stand in for data with a known layout — the file list, the transfers table. The skeleton mirrors the real row grid (44px rows, same columns) so content doesn’t jump when it lands. Base is --component-bg; the shimmer sweep uses --component-bg-hover and only runs under prefers-reduced-motion: no-preference — the static two-tone is the reduced-motion fallback, not an afterthought. Vary name-bar widths so the list doesn’t look stamped.
02
Which loading state, when
skeleton
Layout known, data pending. Lists, tables, the storage card. Show immediately — no delay-then-skeleton double flash.
spinner
Layout unknown or action-scoped. Button loading (.is-loading), modal submit, player buffering. Sizes 16 / 20 / 28 — see Tasks.
progress bar
Measurable work. Transfers, zips, uploads. Yellow fill; never a spinner when you know the percent.
empty state
Loaded, and there is nothing. Never render an empty state while loading — that reads as data loss. See Brand · Empty state.
Skeleton no more than one screen’s worth of rows; below the fold just loads. If data usually arrives <150ms, skip the skeleton entirely — a flash of placeholder is worse than nothing.