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:

  1. Skeleton first. Show the structural shape of the page immediately. Users orient faster when they see where content will appear.
  2. 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.
  3. 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

dots2dots14 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. arc and rolling_line for neutral feel; dqpb and noise when 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. moon and earth are 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-md for text blocks, rounded-xl for cards, rounded-full for 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

  1. Next.js loading.tsx route files -- braille spinner overlay + skeleton layout matching the page structure.
  2. Button loading states -- inline braille spinner with label, replacing the button text while isLoading.
  3. Card/section loading -- skeleton blocks matching the card dimensions.
  4. 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-spin on 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