Loading Screens
Unicode spinners as the canonical loading indicator - braille by default, ASCII/arrow/emoji variants for thematic use. Skeletons for layout preservation. Never a blank screen. Source: unicode-animations; dab spinner designer; X/@n___vc; Design System
Philosophy
Loading states are part of the interface, not interruptions to it. Three principles:
- Skeleton first. Show the structural shape of the page immediately. Users orient faster when they see where content will appear.
- Braille spinners for active feedback. When a skeleton isn't enough (button submissions, inline status, overlay waits), use animated Unicode braille characters instead of SVG/CSS ring spinners. They're typographic, zero-DOM, and visually distinctive.
- Never spinner-only. A centered spinner on a blank page is a failure. Pair spinners with skeletons or contextual labels.
Expressive Loader Builders
loaders.wtf covers the visual-identity lane: distinctive product launch loaders, agent status cards, and shareable motion experiments. The 2026-06-30 artifact review showed a dark canvas editor with loader presets such as "Thinking", "Reading Docs", "Analysing", and "Debugging"; a right-side control panel for cell shape, animation pattern, speed, style, background, effects, colors, text shimmer, image mask, AI generation, SVG import, share, and export; plus local video evidence of the motion surface. Use it as an inspiration/builder route when the loader is meant to signal craft, but keep the default product system on skeletons plus compact braille spinners. Source: X/@dnaijatechguy, 2026-05-29; Source: loaders.wtf, 2026-06-30
Dot Matrix covers the source-owned component lane: a shadcn registry of 55+
React/Tailwind dot-matrix loaders that install into the app with commands such
as npx shadcn@latest add @dotmatrix/dotm-square-3. Use it when the product
wants a specific animated loader as editable local source code. Do not replace
plain skeletons with animated loader grids in dense operational UI. Source: Dot Matrix usage docs, 2026-07-03
The UI/UX Savior motion catalog is a compact inspiration board for icon-sized loaders: circles, dot clusters, fade circles, balls, clocks/hourglasses, Wi-Fi bars, and common action icons such as search, mail, image, home, upload, download, truck, and book. Use it to pick a loader family that matches the action being awaited. The system rule does not change: pair loaders with layout-preserving skeletons, expose accessible status text, and provide reduced-motion fallbacks. Source: X/@UiSavior and local video contact sheet, 2026-07-04
Braille Spinners
Library: unicode-animations
(npm 1.0.3 as of 2026-07-03). 18 spinners built on the Unicode braille block (U+2800). Each braille character is
a 2x4 dot grid; the library composes them into multi-character animated frames.
Custom spinner designer: dab -- use this to create new braille animation patterns beyond the library defaults.
Original inspiration: @n___vc tweet
Component
import { BrailleSpinner } from "@/components/ui/braille-spinner";
<BrailleSpinner name="braille" className="text-muted-foreground text-2xl" />
<BrailleSpinner name="braille" label="Deploying..." className="text-sm" />
The component cycles through frames on a setInterval matching the spinner's native
interval (80ms for braille). It renders a <span> with role="status" and
aria-label for accessibility.
Sizing
Map old Tailwind icon sizes to font sizes:
| Old pattern | New class |
|---|---|
h-2 w-2 |
text-xs |
h-4 w-4 |
text-sm |
h-5 w-5 |
text-base |
h-6 w-6 |
text-lg |
h-8 w-8 |
text-2xl |
Available Spinners
The name prop accepts any of the 18 built-in animations. Defaults to braille.
| Spinner | Character | Interval | Use case |
|---|---|---|---|
braille |
Classic dot cycle | 80ms | Default for all loading states |
orbit |
Orbital rotation | 100ms | Status polling |
breathe |
Expand/contract | 100ms | Idle waiting |
scan |
Scanning sweep | 70ms | Search/indexing |
cascade |
Cascading dots | 60ms | Multi-step processes |
helix |
DNA-like twist | 80ms | Data processing |
fill-sweep |
Progressive dot fill | package default | Compact progress indication |
columns |
Columnar dot movement | package default | Data/table work |
checkerboard |
Alternating dot grid | package default | Batch jobs |
rain |
Falling dot pattern | package default | Streaming or ingest |
sparkle |
Sparse point flashes | package default | AI generation |
wave-rows |
Row wave | package default | Multi-row processing |
diagonal-swipe |
Diagonal movement | package default | Transition/wait states |
Use braille as the default everywhere. Other spinners are available for thematic
variety in specific contexts (e.g., scan during search, cascade during deployments).
Expanded Catalog
For projects that want more variety than the 18 base unicode-animations spinners,
a 55-spinner superset is catalogued at wiki/assets/spinners-catalog.json. Frames
were extracted from Eronred/expo-agent-spinners
(MIT, originally inspired by vyfor/rattles).
The JSON is framework-agnostic - each entry is { category, frames, interval }.
Any project agent can load it and wire it into a component (React, Next.js,
React Native, CLI, whatever the target runtime is).
import catalog from "@/assets/spinners-catalog.json";
const { frames, interval } = catalog.spinners.moon;
// frames: ["🌑","🌒","🌓","🌔","🌕","🌖","🌗","🌘"] interval: 80
Categories
| Category | Count | When to use |
|---|---|---|
braille |
32 | Default. Typographic, dense, reads as "working." |
ascii |
15 | Terminal/CLI aesthetic, agent streams, code-heavy UIs. |
arrow |
2 | Directional progress (uploads, next-step hints). |
emoji |
6 | Thematic contexts where tone warrants color. Use sparingly. |
Highlights per category
Braille additions (beyond unicode-animations)
| Name | Preview (first frames) | Interval |
|---|---|---|
dots2 |
⣾ ⣽ ⣻ ⢿ ⡿ ⣟ ⣯ ⣷ |
80ms |
dots10 |
⢄ ⢂ ⢁ ⡁ ⡈ ⡐ ⡠ |
80ms |
dots12 |
⢀⠀ ⡀⠀ ⠄⠀ ⢂⠀ ⡂⠀ |
80ms |
sand |
hourglass-style fill (multi-char) | 120ms |
bounce |
vertical dot bounce | 100ms |
dots_circle |
clockwise dot rotation | 100ms |
dots2–dots14 are hand-tuned rhythmic variants of the classic dots cycle - same
character set, different cadences and densities. Pick one per product area for a
subtle consistency tell.
ASCII
| Name | Preview | Interval | Use case |
|---|---|---|---|
arc |
◜ ◠ ◝ ◞ ◡ ◟ |
100ms | Minimal status, inline text |
rolling_line |
- | / - |
80ms | Classic CLI feel |
grow_horizontal |
▏ ▎ ▍ ▌ ▋ ▊ ▉ |
120ms | Progress-like inline |
grow_vertical |
▁ ▂ ▃ ▄ ▅ ▆ ▇ |
120ms | Vertical progress |
dqpb |
d q p b |
100ms | Playful, letter-form rotation |
noise |
▓ ▒ ░ ░ ▒ |
100ms | Static/scan effect |
triangle |
◢ ◣ ◤ ◥ |
100ms | Geometric rotation |
balloon |
.·oO@* |
140ms | Expanding/contracting |
Arrow
| Name | Preview | Interval |
|---|---|---|
arrow |
← ↖ ↑ ↗ → ↘ ↓ ↙ |
100ms |
double_arrow |
⇐ ⇖ ⇑ ⇗ ⇒ ⇘ ⇓ ⇙ |
100ms |
Emoji
| Name | Preview | Use case |
|---|---|---|
moon |
🌑🌒🌓🌔🌕🌖🌗🌘 |
Night/dark-mode themed |
earth |
🌍🌎🌏 |
Global/sync operations |
clock |
🕐🕑🕒…🕛 |
Waiting on scheduled work |
hearts |
🩷🧡💛💚💙🩵💜🤎 |
Playful, warm contexts |
weather |
☀️🌤⛅️🌥☁️🌧⛈ |
Status shifts, ambient |
speaker |
🔈🔉🔊 |
Audio/voice states |
See wiki/assets/spinners-catalog.json for the full 55-entry index with exact
frame arrays.
When to use what
- Braille (default). All standard loading states. Typographic, reads as "working," doesn't compete with content.
- ASCII. Agent streams, terminals, code editors, CLI-adjacent surfaces. Pair with
monospace UIs.
arcandrolling_linefor neutral feel;dqpbandnoisewhen you want character. - Arrow. Directional context only - uploads, next-step nudges. Don't use for generic "loading."
- Emoji. Thematic accents, not primary indicators. Skip in dense dashboards.
moonandearthare the best tone-neutral picks.
Skeletons
Skeletons are animate-pulse divs with bg-muted that match the final content's
dimensions. Rules:
- Match the aspect ratio and position of the real content.
- Use
rounded-mdfor text blocks,rounded-xlfor cards,rounded-fullfor avatars. - Never animate skeleton color -- only opacity pulse.
- Group skeletons to match the visual hierarchy: title skeleton wider than subtitle, card skeletons in the same grid as real cards.
Pattern
import { Skeleton } from "@/components/ui/skeleton";
<Skeleton className="h-8 w-48" /> {/* Title */}
<Skeleton className="h-4 w-96" /> {/* Subtitle */}
<Skeleton className="h-32 w-full" /> {/* Card */}
Loading Screen Hierarchy
- Next.js
loading.tsxroute files -- braille spinner overlay + skeleton layout matching the page structure. - Button loading states -- inline braille spinner with label, replacing the button
text while
isLoading. - Card/section loading -- skeleton blocks matching the card dimensions.
- Inline status -- small braille spinner next to status text.
Anti-Patterns
- Spinner on blank page. Always pair with structural skeletons.
- SVG/CSS ring spinners. Replaced with braille. The ring spinner is generic; braille is typographic and distinctive.
animate-spinon icons. Do not spin Lucide icons as loading indicators. Use BrailleSpinner.- Loading text without animation. "Loading..." as static text provides no feedback. Use a braille spinner.
Timeline
- 2026-07-04 | Reviewed the UI/UX Savior loading-motion catalog and added it as icon-sized loader inspiration. The default remains skeletons plus typographic/braille feedback; the catalog is for contextual inline loader families. Source: X/@UiSavior, 2026-06-25
- 2026-05-29 | External reference: open-source "Mistral loaders" collection by @dnaijatechguy, a visual and animated complement to this typographic spinner system. 1,657 likes, 1,682 bookmarks. Source: X/@dnaijatechguy, 2026-05-29
- 2026-06-12 | Identified the loaders' author: Michael Ajah (@dnaijatechguy, "Design Chief Priest"), a DS engineer at Mistral AI; the collection was a design exploration he open-sourced ("you can build your own tools as a designer"). Source: chiefpriest.design, https://chiefpriest.design/, 2026-06-12
- 2026-06-30 | Deep-reviewed loaders.wtf local image/video artifacts and promoted it into loaders.wtf as the expressive loader-builder route. The default loading-system rule remains skeletons plus typographic spinners; loaders.wtf is for brand/motion moments. Source: X/@dnaijatechguy, 2026-05-29; Source: loaders.wtf, 2026-06-30
- 2026-07-03 | Reviewed the original Gunnar Gray braille-animation video locally and added visible named variants plus the current
unicode-animations@1.0.3snapshot. Source: X/@n___vc, 2026-04-08; Source: npm registry, 2026-07-03 - 2026-07-03 | Added Dot Matrix as the shadcn-registry loader route for editable local React/Tailwind source components. Source: X/@zzzzshawn, 2026-05-03; Source: Dot Matrix docs, 2026-07-03