Loading & Empty States
GospeLib uses an optimistic UI philosophy — the interface should always feel immediate, even when data is in transit. When loading is unavoidable, it must be beautiful and informative. Errors are gentle and recover gracefully.
Core Philosophy
"Never show a spinner where a skeleton will do. Never show a skeleton where cached content will do. Never show cached content where fresh content is already available."
Priority waterfall:
- Cached content (instant, from SQLite/MMKV)
- Optimistic update (immediate UI change, reconcile on server response)
- Skeleton (if no cache exists)
- Spinner (never for content — only for discrete actions like "saving note…")
Scripture Page Skeleton
When a chapter loads without cached content:
Shimmer Pattern
| Element | Skeleton Treatment |
|---|---|
| Chapter header | Centered block, corpus color at 15% opacity |
| Verse numbers | Small square pulses at left margin |
| Scripture text | Length-accurate line blocks matching expected content layout |
| Footnote indicators | Not shown in skeleton state |
"Length-accurate" means the skeleton lines match the approximate line count and line widths of the actual content. This prevents layout shift when content appears.
Animation
@keyframes shimmer {
0% {
background-position: -200% 0;
}
100% {
background-position: 200% 0;
}
}
.skeleton-line {
background: linear-gradient(
90deg,
var(--color-tan-100) 25%,
var(--color-tan-50) 37%,
var(--color-tan-100) 63%
);
background-size: 200% 100%;
animation: shimmer 1.5s ease-in-out infinite;
}
The shimmer uses tan tones (not gray) to match the warm paper surface. It should feel like light moving across paper, not a digital loading indicator.
Search Progressive Loading
| Phase | Timing | Content |
|---|---|---|
| Instant | 0ms | Text index results (Typesense) — fast fuzzy matches |
| Fast | 200–500ms | Graph-enriched results (FalkorDB) — cross-references, topics |
| Streaming | 500ms+ | AI semantic results — stream in as they're computed |
Results from each phase append to the list. No full-screen loading state — results appear progressively as they become available.
AI Token Streaming
AI responses (passage explanations, study questions) stream token-by-token:
| Property | Value |
|---|---|
| Cursor | Subtle blinking bar at the end of streamed text |
| Animation | Each token fades in (opacity 0→1, 60ms) |
| Paragraph breaks | Rendered immediately when detected |
| Citations | Scripture references become tappable links as they complete |
| Interruption | User can tap "Stop" to halt generation |
The One Loading Animation
GospeLib has exactly one loading animation — a breathing app icon:
| Property | Value |
|---|---|
| Icon | GospeLib logo (simplified) |
| Animation | Gentle scale pulse (0.95→1.05→0.95) |
| Duration | 2s per cycle |
| Easing | smoothInOut |
| Usage | Full-page loading only (app launch, deep link resolution) |
No other spinners, progress circles, or animated indicators. Every other loading state uses skeletons or progressive content.
Error States
Philosophy
"Errors are amber, not red. Inline, not modal. Recoverable, not terminal."
| Principle | Implementation |
|---|---|
| Amber, not red | Warning color #C47A2A — red is reserved for destructive confirmations only |
| Inline, not modal | Error messages appear contextually near the failed action |
| Recoverable | Every error message includes a recovery action |
| Brief | One sentence max. Technical details in expandable "Details" section |
Error Message Pattern
[Amber ⚠ icon] [Brief message] [Recovery action button]
Example:
⚠ Couldn't load cross-references. [Try again]
Offline Errors
| Scenario | Message | Recovery |
|---|---|---|
| No network, cached content available | No indicator (reads from cache silently) | Auto-sync when reconnected |
| No network, no cache | "You're offline. This content will load when you reconnect." | Auto-retry on reconnection |
| Sync conflict | "Your note was edited on another device. [Keep this version] [Keep other]" | User choice |
Success Confirmations
"Wordless whenever possible."
| Action | Confirmation |
|---|---|
| Highlight applied | Color washes in (visual-only, no toast) |
| Bookmark set | Icon materializes with micro-animation |
| Note saved | Checkmark briefly appears on the note card |
| Download complete | Progress bar fills, then fades away |
No toast messages for routine actions. The animation is the confirmation.
For significant actions (export complete, account changes), a brief inline message appears for 3 seconds then fades.
Empty States
When a section has no content, the empty state follows a consistent template:
Template
| Element | Content |
|---|---|
| Illustration | Gentle line-art illustration (matching the icon set style) |
| Heading | Descriptive, not apologetic ("Start your study journal" not "No entries yet") |
| Body | One sentence explaining what this section is for |
| CTA | Primary action button to create first item |
Empty State Examples
| Section | Heading | CTA |
|---|---|---|
| Journal | "Start your study journal" | "Write your first entry" |
| Collections | "Organize your study" | "Create a collection" |
| Bookmarks | "Save passages for later" | "Bookmark a verse by double-tapping" |
| Highlights | "Mark what matters" | "Highlight a verse to begin" |
| Search history | "Find anything in scripture" | (Search field focused automatically) |
Related Pages
- Motion & Haptics — animation curves for loading states
- Colors — functional colors for error/warning states
- Accessibility — screen reader announcements for loading states
- Reader Experience — chapter loading behavior