ai-loading-ux
Design AI loading, thinking, and progress indicator UX. Use when explicitly asked to improve AI waiting states, add thinking indicators, or design loading UX for AI interfaces. Covers reasoning display (chain-of-thought), progress steps, streaming states, and the "elevator mirror effect" for reducing perceived wait time.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install rohunvora-cool-claude-skills-ai-loading-ux
Repository
Skill path: skills/ai-loading-ux
Design AI loading, thinking, and progress indicator UX. Use when explicitly asked to improve AI waiting states, add thinking indicators, or design loading UX for AI interfaces. Covers reasoning display (chain-of-thought), progress steps, streaming states, and the "elevator mirror effect" for reducing perceived wait time.
Open repositoryBest for
Primary workflow: Design Product.
Technical facets: Full Stack, Data / AI, Designer.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: rohunvora.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install ai-loading-ux into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/rohunvora/cool-claude-skills before adding ai-loading-ux to shared team environments
- Use ai-loading-ux for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: ai-loading-ux
description: Design AI loading, thinking, and progress indicator UX. Use when explicitly asked to improve AI waiting states, add thinking indicators, or design loading UX for AI interfaces. Covers reasoning display (chain-of-thought), progress steps, streaming states, and the "elevator mirror effect" for reducing perceived wait time.
---
# AI Loading UX
Design patterns for showing users what's happening while waiting for AI output.
## Decision Framework
First, identify which pattern category applies:
| User is waiting for... | Pattern Category | Key Goal |
|------------------------|------------------|----------|
| AI reasoning/thinking | **Reasoning Display** | Build trust through transparency |
| Multi-step task completion | **Progress Steps** | Show advancement toward goal |
| Content generation/streaming | **Streaming States** | Reduce perceived wait time |
| Background processing | **Status Indicators** | Confirm work is happening |
## Core Principles
### 1. The Elevator Mirror Effect
Users waiting for AI feel time pass slower. Give them something to watch/read—animated indicators reduce *perceived* wait time even when actual time is unchanged.
### 2. Progressive Disclosure
- Show condensed indicator by default ("Thinking...")
- Make details *available* but not forced
- Let curious users expand; don't burden everyone
### 3. More Transparency ≠ Better UX
Balance visibility with cognitive load. Users want answers, not reasoning—but they want to *trust* the answer came from good reasoning.
### 4. Signal Completion Clearly
Users must know when processing ends. Ambiguous end states frustrate users.
## Pattern Quick Reference
### Reasoning Display (Chain-of-Thought)
When AI is "thinking" through a problem. See [references/reasoning-patterns.md](references/reasoning-patterns.md).
**Best approach (Claude-style):**
- Hidden by default, expandable on demand
- Structured bullets when expanded
- Time counter or progress indicator
- Clear "done" state
**Anti-patterns:**
- Wall of streaming text (overwhelming)
- Scrolling too fast to read
- No expand option (feels opaque)
- No clear end state
### Progress Steps
When AI completes sequential tasks. See [references/progress-patterns.md](references/progress-patterns.md).
**Best approach:**
- Show current step + total steps
- Mark completed steps visually
- Show what's actively happening
- Allow step-level details on expand
### Streaming States
When content generates token-by-token. See [references/streaming-patterns.md](references/streaming-patterns.md).
**Best approach:**
- Typing cursor or text animation
- Smooth token appearance (not jarring)
- Skeleton for expected content shape
- "Stop generating" escape hatch
### Status Indicators
When background work happens. See [references/status-patterns.md](references/status-patterns.md).
**Best approach:**
- Subtle but visible animation
- Brief description of current action
- Don't block user from other actions
- Notify on completion
## Implementation Checklist
When implementing any AI loading state:
1. [ ] **Identify pattern category** from decision framework above
2. [ ] **Choose visibility level**: always visible, expandable, or minimal
3. [ ] **Add motion**: animation reduces perceived wait (but keep it subtle)
4. [ ] **Show progress**: time elapsed, steps completed, or content streamed
5. [ ] **Signal completion**: clear visual/state change when done
6. [ ] **Provide escape**: stop/cancel for long operations
7. [ ] **Handle errors**: don't leave user in permanent loading state
8. [ ] **Test on slow connections**: ensure graceful degradation
## Product Comparisons (Reference)
| Product | Approach | Strength | Weakness |
|---------|----------|----------|----------|
| Claude | Hidden reasoning, expandable, structured bullets | Low cognitive load | Can feel opaque |
| ChatGPT | Brief labels, auto-collapse | Unobtrusive | Less transparent |
| DeepSeek | Full streaming reasoning | Maximum transparency | Overwhelming |
| Gemini | User-scrolled, numbered steps | Clear structure | Unclear completion |
## Usage
Read the relevant reference file for your pattern category:
- [references/reasoning-patterns.md](references/reasoning-patterns.md) - Chain-of-thought, thinking indicators
- [references/progress-patterns.md](references/progress-patterns.md) - Step sequences, task completion
- [references/streaming-patterns.md](references/streaming-patterns.md) - Token streaming, content generation
- [references/status-patterns.md](references/status-patterns.md) - Background processing, polling states
---
## Referenced Files
> The following files are referenced in this skill and included for context.
### references/reasoning-patterns.md
```markdown
# Reasoning Display Patterns
For AI interfaces that show chain-of-thought or "thinking" processes.
## Table of Contents
- [When to Use](#when-to-use)
- [Anatomy of a Thinking Indicator](#anatomy-of-a-thinking-indicator)
- [Visibility Levels](#visibility-levels)
- [State Machine](#state-machine)
- [Implementation Patterns](#implementation-patterns)
- [Accessibility](#accessibility)
## When to Use
Use reasoning display when:
- AI is solving complex problems (math, logic, planning)
- Users benefit from understanding *how* the answer was reached
- Trust-building is important (financial, medical, legal domains)
- The process takes >3 seconds
Skip reasoning display when:
- Simple Q&A or lookups
- Speed is more important than transparency
- Content speaks for itself (creative writing, summaries)
## Anatomy of a Thinking Indicator
```
┌─────────────────────────────────────────┐
│ 🧠 Thinking... 12s │ ← Header: icon + label + timer
├─────────────────────────────────────────┤
│ ▸ Analyzing the problem │ ← Expandable reasoning steps
│ ▸ Considering edge cases │
│ • Checking mathematical constraints │ ← Current step (animated)
└─────────────────────────────────────────┘
```
**Components:**
1. **Icon** - Animated to show activity (brain, sparkles, dots)
2. **Label** - Changes based on phase ("Thinking...", "Analyzing...", "Finalizing...")
3. **Timer** - Elapsed time (reduces anxiety about stuck states)
4. **Reasoning list** - Structured bullets, progressive reveal
5. **Expand/collapse** - User control over detail level
## Visibility Levels
### Level 1: Minimal (ChatGPT-style)
```
Thinking...
```
- Just a label + animation
- Collapses when done
- Best for: Fast responses, simple queries
### Level 2: Summary (Claude-style)
```
▼ Thinking (8s)
• Analyzing request
• Searching knowledge
• Formulating response
```
- Collapsed by default, expandable
- Shows high-level steps
- Best for: Balance of transparency and simplicity
### Level 3: Verbose (DeepSeek-style)
```
Thinking...
First, I need to understand what the user is asking.
They want to know about X, which involves Y and Z.
Let me consider the implications...
[continues streaming]
```
- Full reasoning visible
- Streams in real-time
- Best for: Power users, debugging, research contexts
## State Machine
```
┌─────────┐ start ┌──────────┐ complete ┌──────────┐
│ Idle │ ─────────→ │ Thinking │ ─────────→ │ Done │
└─────────┘ └──────────┘ └──────────┘
│ │
│ error │ dismiss
▼ ▼
┌──────────┐ ┌──────────┐
│ Error │ │ Hidden │
└──────────┘ └──────────┘
```
**State behaviors:**
- **Idle**: No indicator visible
- **Thinking**: Animated indicator, timer counting, steps appearing
- **Done**: Animation stops, checkmark or fade, then transition to answer
- **Error**: Red state, error message, retry option
- **Hidden**: Collapsed but accessible via "Show thinking"
## Implementation Patterns
### Pattern A: Collapsible Accordion
```
// Pseudocode - adapt to your framework
ThinkingIndicator:
state: collapsed | expanded
steps: string[]
currentStep: number
elapsedTime: number
render:
if collapsed:
<Row onClick={expand}>
<AnimatedIcon />
<Label>"Thinking..."</Label>
<Timer>{elapsedTime}s</Timer>
<ChevronDown />
</Row>
if expanded:
<Column>
<Header onClick={collapse}>
<AnimatedIcon />
<Label>"Thinking..."</Label>
<Timer>{elapsedTime}s</Timer>
<ChevronUp />
</Header>
<StepList>
{steps.map((step, i) =>
<Step
complete={i < currentStep}
active={i === currentStep}
>
{step}
</Step>
)}
</StepList>
</Column>
```
### Pattern B: Inline Badge
```
// For chat interfaces where thinking appears inline
<Message>
<ThinkingBadge status={thinking ? "active" : "complete"}>
{thinking ? "Thinking..." : `Thought for ${time}s`}
</ThinkingBadge>
{!thinking && <Content>{response}</Content>}
</Message>
```
### Pattern C: Side Panel
```
// For complex reasoning that shouldn't interrupt main flow
<Layout>
<MainContent>
{response}
</MainContent>
<SidePanel visible={showReasoning}>
<ReasoningTrace steps={steps} />
</SidePanel>
</Layout>
```
## Accessibility
- **Screen readers**: Announce state changes ("Now thinking", "Response ready")
- **Reduced motion**: Replace animations with static indicators
- **Keyboard**: Expand/collapse with Enter/Space
- **Color**: Don't rely on color alone for state (use icons too)
## Common Mistakes
1. **No end state** - Users don't know thinking finished
2. **Too verbose** - Reasoning overwhelms the answer
3. **No escape** - Can't stop or skip long thinking
4. **Frozen UI** - Blocking interaction during thinking
5. **Lost on error** - No recovery path when thinking fails
```
### references/progress-patterns.md
```markdown
# Progress Step Patterns
For AI interfaces that complete multi-step tasks.
## Table of Contents
- [When to Use](#when-to-use)
- [Anatomy of Progress Steps](#anatomy-of-progress-steps)
- [Step States](#step-states)
- [Layout Patterns](#layout-patterns)
- [Implementation Patterns](#implementation-patterns)
- [Edge Cases](#edge-cases)
## When to Use
Use progress steps when:
- Task has 3+ discrete steps
- Steps complete sequentially
- Users need to know what's happening at each stage
- Total time exceeds 10 seconds
Examples:
- "Analyzing document → Extracting data → Formatting report"
- "Searching → Filtering → Ranking → Generating response"
- "Uploading → Processing → Saving"
## Anatomy of Progress Steps
```
┌─────────────────────────────────────────────────────┐
│ Processing your request │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━░░░░░░░░ 75% │
├─────────────────────────────────────────────────────┤
│ ✓ Analyzing document 2.3s │
│ ✓ Extracting key information 4.1s │
│ ● Generating summary... 1.2s │
│ ○ Formatting output │
└─────────────────────────────────────────────────────┘
```
**Components:**
1. **Header** - Overall task description
2. **Progress bar** - Visual completion indicator
3. **Step list** - Individual steps with status
4. **Timing** - Per-step or total elapsed time
## Step States
| State | Visual | Behavior |
|-------|--------|----------|
| Pending | ○ (empty circle) | Grayed out, waiting |
| Active | ● (filled, pulsing) | Highlighted, animated |
| Complete | ✓ (checkmark) | Green, shows duration |
| Error | ✗ (x mark) | Red, shows error message |
| Skipped | — (dash) | Gray, strikethrough |
## Layout Patterns
### Vertical List (Default)
```
✓ Step 1: Completed
● Step 2: In progress...
○ Step 3: Pending
○ Step 4: Pending
```
Best for: 3-7 steps, detailed descriptions
### Horizontal Stepper
```
[1]───[2]───[3]───[4]
✓ ● ○ ○
```
Best for: 3-5 steps, compact UI, wizards
### Compact Badge
```
Step 2 of 4: Processing...
```
Best for: Minimal UI, mobile, inline display
### Timeline
```
2:00 PM ─● Started analysis
2:01 PM ─✓ Extracted 234 records
2:02 PM ─● Generating report...
○ Finalizing
```
Best for: Audit trails, long-running tasks
## Implementation Patterns
### Pattern A: Linear Steps with Substeps
```
// Pseudocode
ProgressSteps:
steps: Step[]
currentStep: number
Step:
label: string
status: pending | active | complete | error
substeps?: string[]
duration?: number
render:
<Container>
<ProgressBar percent={currentStep / steps.length * 100} />
<StepList>
{steps.map((step, i) =>
<StepRow status={step.status}>
<StatusIcon status={step.status} />
<Label>{step.label}</Label>
{step.duration && <Duration>{step.duration}s</Duration>}
{step.status === "active" && step.substeps && (
<Substeps>
{step.substeps.map(sub => <SubstepRow>{sub}</SubstepRow>)}
</Substeps>
)}
</StepRow>
)}
</StepList>
</Container>
```
### Pattern B: Streaming Steps
```
// Steps appear as they're discovered (not predetermined)
StreamingProgress:
steps: Step[] = []
onNewStep(step):
steps.push(step)
onStepComplete(stepId):
steps.find(s => s.id === stepId).status = "complete"
render:
<Container>
{steps.map(step => <StepRow {...step} />)}
{isProcessing && <PendingIndicator />}
</Container>
```
### Pattern C: Action Plan Preview
```
// Show planned steps before execution (builds trust)
ActionPlan:
plannedSteps: string[]
executedSteps: Step[]
render:
<Container>
<Header>I'll do the following:</Header>
{plannedSteps.map((step, i) => {
const executed = executedSteps[i]
return (
<StepRow
status={executed?.status || "pending"}
label={step}
actual={executed?.label} // May differ from planned
/>
)
})}
</Container>
```
## Edge Cases
### Unknown Step Count
When you don't know how many steps upfront:
```
Processing... (Step 3)
✓ Analyzed structure
✓ Extracted entities
● Resolving references...
```
- Show completed count, not percentage
- Add "..." to indicate more may come
### Step Failure with Recovery
```
✓ Step 1: Complete
✗ Step 2: Failed - Retrying (attempt 2/3)
○ Step 3: Waiting
```
- Show retry status
- Don't reset the whole progress
- Offer skip option for non-critical steps
### Parallel Steps
```
● Processing (3 tasks running)
├─ ● Analyzing images (12/50)
├─ ● Extracting text (34/50)
└─ ● Classifying content (8/50)
```
- Show each parallel task's progress
- Complete parent when all children complete
### Long-Running Steps
```
● Generating report... (2m 34s)
Last update: Processing section 4 of 12
[Show Details] [Cancel]
```
- Provide sub-progress or last action
- Always offer cancel for long steps
- Consider timeout warnings
## Accessibility
- Use `aria-live="polite"` for step announcements
- Progress bar needs `role="progressbar"` with `aria-valuenow`
- Ensure color isn't only indicator (icons matter)
- Support keyboard navigation between steps
```
### references/streaming-patterns.md
```markdown
# Streaming Content Patterns
For AI interfaces that display content as it generates token-by-token.
## Table of Contents
- [When to Use](#when-to-use)
- [Streaming Behaviors](#streaming-behaviors)
- [Visual Indicators](#visual-indicators)
- [Implementation Patterns](#implementation-patterns)
- [Performance Considerations](#performance-considerations)
- [Edge Cases](#edge-cases)
## When to Use
Use streaming display when:
- Content generates incrementally (LLM token streaming)
- Showing progress reduces perceived wait time
- Users can start reading before generation completes
- Response length is unpredictable
Skip streaming when:
- Response is very short (<50 tokens)
- Content needs validation before display
- Structured output must be complete to render (JSON, tables)
## Streaming Behaviors
### Text Streaming
```
The quick brown fox jum|
The quick brown fox jumps over|
The quick brown fox jumps over the lazy dog.|
```
- Append tokens as received
- Cursor indicates active generation
### Block Streaming
```
┌──────────────────────────┐
│ Generating... │
│ ████████░░░░░░░░ 50% │
└──────────────────────────┘
↓
┌──────────────────────────┐
│ Here is your summary: │
│ │
│ The document discusses...│
└──────────────────────────┘
```
- Show placeholder until enough content
- Reveal in chunks for smoother reading
### Skeleton → Content
```
┌──────────────────────────┐ ┌──────────────────────────┐
│ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ │ │ Introduction │
│ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ │ → │ This report covers... │
│ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ │ │ Key findings include: │
└──────────────────────────┘ └──────────────────────────┘
```
- Pre-render expected shape
- Replace skeleton with real content
- Reduces layout shift
## Visual Indicators
### Cursor Styles
```
█ Block cursor (classic terminal)
| Line cursor (text editor)
● Dot cursor (minimal)
▋ Half-block cursor (modern)
```
### Animation States
- **Typing**: Cursor visible, blinking or solid
- **Paused**: Cursor visible, no blink, "paused" indicator
- **Complete**: Cursor hidden, content stable
- **Error**: Red indicator, error message appended
### Container Indicators
```
┌─ Generating ─────────────────┐
│ Content streams here... │
│ ... │ ← Activity dots
└──────────────────────────────┘
```
## Implementation Patterns
### Pattern A: Simple Token Append
```
// Pseudocode
StreamingText:
content: string = ""
isGenerating: boolean = false
onToken(token):
content += token
render:
<Container>
<Text>{content}</Text>
{isGenerating && <Cursor />}
</Container>
```
### Pattern B: Smooth Token Rendering
```
// Batch tokens to reduce render frequency
StreamingText:
displayContent: string = ""
buffer: string = ""
onToken(token):
buffer += token
// Flush buffer on interval (e.g., 50ms) or buffer size
flushBuffer():
displayContent += buffer
buffer = ""
render:
<Container>
<Text>{displayContent}</Text>
{isGenerating && <Cursor />}
</Container>
```
### Pattern C: Markdown Streaming
```
// Handle markdown as it streams
MarkdownStream:
rawContent: string = ""
onToken(token):
rawContent += token
render:
// Parse complete markdown blocks, hold incomplete ones
const { complete, partial } = parseMarkdown(rawContent)
<Container>
<Markdown>{complete}</Markdown>
<RawText>{partial}</RawText>
{isGenerating && <Cursor />}
</Container>
```
### Pattern D: Code Block Streaming
```
// Special handling for code blocks
CodeStream:
content: string = ""
inCodeBlock: boolean = false
onToken(token):
content += token
inCodeBlock = detectCodeBlock(content)
render:
if inCodeBlock:
<CodeEditor
value={content}
language={detectLanguage(content)}
streaming={true}
/>
else:
<Text>{content}</Text>
```
## Performance Considerations
### Token Batching
Don't render on every single token—batch updates:
```
// Bad: renders 100 times for 100 tokens
tokens.forEach(token => setContent(c => c + token))
// Good: batches into ~20 renders
const BATCH_INTERVAL = 50 // ms
let batch = ""
tokens.forEach(token => batch += token)
setInterval(() => {
if (batch) {
setContent(c => c + batch)
batch = ""
}
}, BATCH_INTERVAL)
```
### Virtual Scrolling
For very long outputs:
```
// Only render visible portion
<VirtualList
items={contentLines}
renderItem={(line) => <Line>{line}</Line>}
autoScroll={isGenerating}
/>
```
### Debounced Parsing
For rich content (markdown, code highlighting):
```
// Don't re-parse on every token
const parsedContent = useDebouncedMemo(
() => parseMarkdown(rawContent),
[rawContent],
100 // ms
)
```
## Edge Cases
### Stop Generation
```
<Container>
<StreamingContent content={content} />
{isGenerating && (
<StopButton onClick={stop}>
■ Stop generating
</StopButton>
)}
</Container>
```
- Always provide escape hatch
- Clearly indicate what will happen (content preserved)
### Connection Loss
```
<Container>
<Text>{content}</Text>
{connectionLost && (
<Warning>
Connection lost.
<Button onClick={resume}>Resume</Button>
<Button onClick={retry}>Retry from start</Button>
</Warning>
)}
</Container>
```
- Preserve partial content
- Offer resume if possible
### Rate Limiting
```
<Container>
<Text>{content}</Text>
{rateLimited && (
<Notice>
Slowing down... will continue in {countdown}s
</Notice>
)}
</Container>
```
- Don't lose content on rate limit
- Show clear status and countdown
### Empty Response
```
if (isComplete && !content) {
<EmptyState>
No response generated. This might mean:
• The query was too vague
• Content was filtered
<Button onClick={retry}>Try again</Button>
</EmptyState>
}
```
## Accessibility
- Announce streaming start: "Generating response"
- Announce completion: "Response complete"
- Don't auto-scroll aggressively (disorienting for screen readers)
- Provide "skip to end" option for long streams
- Ensure stop button is keyboard accessible
```
### references/status-patterns.md
```markdown
# Status Indicator Patterns
For AI interfaces with background processing or polling states.
## Table of Contents
- [When to Use](#when-to-use)
- [Indicator Types](#indicator-types)
- [Placement Strategies](#placement-strategies)
- [Implementation Patterns](#implementation-patterns)
- [Notification Patterns](#notification-patterns)
- [Edge Cases](#edge-cases)
## When to Use
Use status indicators when:
- Work happens in the background
- User can continue other tasks while waiting
- Process duration is unpredictable
- User needs awareness without blocking interaction
Examples:
- "Syncing your data..."
- "Indexing files in background"
- "Waiting for API response"
- "Training model (this may take a while)"
## Indicator Types
### Spinner / Loader
```
⟳ Processing...
```
- Simple, universally understood
- No progress information
- Best for: Unknown duration, quick tasks
### Pulsing Dot
```
● Syncing
```
- Subtle, non-intrusive
- Shows activity without demanding attention
- Best for: Ambient background status
### Progress Ring / Bar
```
◐ 45% complete
━━━━━━━━░░░░░░░░
```
- Shows measurable progress
- Best for: Known duration/size, file uploads
### Status Badge
```
[Processing] Document.pdf
```
- Inline with content
- Best for: Per-item status in lists
### Toast / Banner
```
┌──────────────────────────────────┐
│ ● Background sync in progress... │
└──────────────────────────────────┘
```
- Temporary, dismissible
- Best for: Temporary states, notifications
## Placement Strategies
### Global Status Bar
```
┌────────────────────────────────────────┐
│ App Header ● Sync │ ← Top-right badge
├────────────────────────────────────────┤
│ │
│ Main Content │
│ │
└────────────────────────────────────────┘
```
- Always visible
- Doesn't interrupt flow
- Click to expand details
### Contextual Inline
```
┌─ Files ────────────────────────────────┐
│ 📄 Report.pdf ✓ Ready │
│ 📄 Analysis.xlsx ⟳ Processing │ ← Per-item status
│ 📄 Summary.doc ✓ Ready │
└────────────────────────────────────────┘
```
- Status next to affected item
- Clear relationship
- Good for lists/tables
### Floating Indicator
```
┌───────────────────┐
│ ● 3 tasks running │
│ View details → │
└───────────────────┘
Main Content
```
- Doesn't take layout space
- Can be minimized
- Good for non-blocking background tasks
### Full-Screen Overlay (Use Sparingly)
```
┌────────────────────────────────────────┐
│ │
│ ⟳ Preparing workspace... │
│ │
│ This may take a minute │
│ │
└────────────────────────────────────────┘
```
- Blocks interaction
- Only for critical initialization
- Must have timeout/escape
## Implementation Patterns
### Pattern A: Background Task Manager
```
// Pseudocode
TaskManager:
tasks: Task[] = []
Task:
id: string
label: string
status: pending | running | complete | error
progress?: number
addTask(task):
tasks.push(task)
updateTask(id, updates):
tasks.find(t => t.id === id).merge(updates)
render:
<FloatingPanel>
<Header>
{runningCount} task(s) running
<CollapseButton />
</Header>
{expanded && (
<TaskList>
{tasks.map(task => (
<TaskRow status={task.status}>
<Label>{task.label}</Label>
{task.progress && <ProgressBar value={task.progress} />}
{task.status === "error" && <RetryButton />}
</TaskRow>
))}
</TaskList>
)}
</FloatingPanel>
```
### Pattern B: Polling Status
```
// For async operations that require polling
PollingStatus:
status: idle | polling | success | error
lastCheck: timestamp
startPolling(interval = 2000):
status = "polling"
poll()
poll():
result = await checkStatus()
lastCheck = now()
if result.complete:
status = "success"
onComplete(result)
else if result.error:
status = "error"
else:
setTimeout(poll, interval)
render:
switch status:
case "polling":
<Badge>
<Spinner />
Checking... (last: {timeAgo(lastCheck)})
</Badge>
case "success":
<Badge success>Complete</Badge>
case "error":
<Badge error>
Failed <RetryButton onClick={startPolling} />
</Badge>
```
### Pattern C: Optimistic UI with Background Sync
```
// Show immediate feedback, sync in background
OptimisticAction:
localState: any
syncStatus: synced | syncing | error
performAction(action):
// Update UI immediately
localState = applyAction(localState, action)
syncStatus = "syncing"
// Sync in background
try:
await syncToServer(action)
syncStatus = "synced"
catch:
syncStatus = "error"
// Optionally revert localState
render:
<Container>
<Content data={localState} />
<SyncIndicator status={syncStatus} />
</Container>
```
## Notification Patterns
### Completion Notifications
```
// When background task completes
onTaskComplete(task):
if document.hidden:
// User not looking - use system notification
showSystemNotification(`${task.label} complete`)
else if !taskPanelVisible:
// User in app but not watching - use toast
showToast(`${task.label} complete`, { action: "View" })
else:
// User watching - just update status
updateTaskStatus(task.id, "complete")
```
### Error Notifications
```
// Errors need more attention than success
onTaskError(task, error):
// Always show prominent error
showToast({
type: "error",
title: `${task.label} failed`,
message: error.message,
actions: [
{ label: "Retry", onClick: () => retryTask(task) },
{ label: "Dismiss", onClick: () => dismissTask(task) }
],
persistent: true // Don't auto-dismiss errors
})
```
## Edge Cases
### Multiple Concurrent Tasks
```
<StatusBar>
{tasks.length === 1 ? (
<SingleTaskView task={tasks[0]} />
) : (
<MultiTaskSummary count={tasks.length} onClick={showDetails} />
)}
</StatusBar>
```
### Stale Status
```
// Detect stuck states
if (task.status === "running" && now() - task.lastUpdate > STALE_THRESHOLD):
<TaskRow>
<Label>{task.label}</Label>
<Warning>
No updates for {timeAgo(task.lastUpdate)}
<Button onClick={checkStatus}>Check</Button>
<Button onClick={cancel}>Cancel</Button>
</Warning>
</TaskRow>
```
### Reconnection
```
// After connection loss
<ConnectionStatus>
{isReconnecting && (
<Banner>
<Spinner /> Reconnecting...
</Banner>
)}
{justReconnected && (
<Banner success>
✓ Reconnected. Syncing {pendingChanges} changes...
</Banner>
)}
</ConnectionStatus>
```
### Long-Running Background Tasks
```
// Tasks that take minutes/hours
<LongTaskStatus task={task}>
<Title>{task.label}</Title>
<Progress value={task.progress} />
<Estimate>~{task.estimatedRemaining} remaining</Estimate>
<Options>
<Checkbox checked={notifyOnComplete}>
Notify me when complete
</Checkbox>
<Button onClick={runInBackground}>
Continue in background
</Button>
</Options>
</LongTaskStatus>
```
## Accessibility
- Use `aria-live="polite"` for status updates
- `aria-busy="true"` on elements being processed
- Don't use only color to indicate status
- Ensure spinners have accessible labels
- Provide text alternatives for all indicators
- Allow users to pause/hide animations
```