Loading Text
The LoadingText component provides animated shimmer text effects with cycling messages and color variants, creating engaging loading states that communicate progress to users.
Overview
Loading text serves as a lightweight alternative to spinners and progress bars, providing contextual feedback through animated text that shimmers and cycles through relevant messages. It’s particularly effective for operations where specific progress cannot be measured.
Interactive Demo
Interactive Loading Text
Customize size, variant, and animation behavior
Message Cycling
Default cycling messages when animation is enabled
Auto-Cycling Messages
Automatically cycles through loading messages
<LoadingText animate=true />API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
text | string | 'Loading...' | Loading message text |
size | 'sm' \| 'md' \| 'lg' \| 'xl' | 'md' | Text size variant |
variant | 'default' \| 'jasper' \| 'info' | 'default' | Color variant |
animate | boolean | true | Enable animation and message cycling |
class | string | '' | Additional CSS classes |
Usage Examples
Basic Loading Text
<script>
import LoadingText from '$lib/loading/components/LoadingText.svelte';
let isLoading = $state(false);
async function fetchData() {
isLoading = true;
try {
await api.getData();
} finally {
isLoading = false;
}
}
</script>
{#if isLoading}
<LoadingText
text="Fetching latest data..."
size="md"
variant="default"
animate={true}
/>
{/if} Custom Loading Messages
<script>
let uploadStatus = $state('idle'); // 'idle' | 'uploading' | 'processing'
let fileName = $state('document.pdf');
$: loadingMessage = uploadStatus === 'uploading'
? `Uploading ${fileName}...`
: uploadStatus === 'processing'
? `Processing ${fileName}...`
: 'Ready to upload';
</script>
{#if uploadStatus !== 'idle'}
<LoadingText
text={loadingMessage}
variant="info"
size="lg"
animate={uploadStatus === 'processing'}
/>
{/if} Form Integration
<script>
let submissionState = $state('idle'); // 'idle' | 'validating' | 'submitting' | 'success'
const stateMessages = {
validating: 'Validating form data...',
submitting: 'Submitting your information...',
success: 'Form submitted successfully!'
};
async function handleSubmit() {
submissionState = 'validating';
// Validate
await new Promise(resolve => setTimeout(resolve, 1000));
submissionState = 'submitting';
// Submit
await new Promise(resolve => setTimeout(resolve, 2000));
submissionState = 'success';
// Reset after success message
setTimeout(() => submissionState = 'idle', 2000);
}
</script>
<form onsubmit|preventDefault={handleSubmit}>
<!-- Form fields -->
{#if submissionState !== 'idle'}
<LoadingText
text={stateMessages[submissionState]}
variant={submissionState === 'success' ? 'jasper' : 'default'}
size="md"
animate={submissionState !== 'success'}
/>
{:else}
<button type="submit">Submit Form</button>
{/if}
</form> Inline Button Loading
<script>
let buttonStates = $state({
save: false,
delete: false,
export: false
});
async function handleAction(action) {
buttonStates[action] = true;
try {
await performAction(action);
} finally {
buttonStates[action] = false;
}
}
</script>
<div class="action-buttons">
<button
onclick={() => handleAction('save')}
disabled={buttonStates.save}
>
{#if buttonStates.save}
<LoadingText text="Saving..." size="sm" animate={false} />
{:else}
Save Changes
{/if}
</button>
<button
onclick={() => handleAction('delete')}
disabled={buttonStates.delete}
>
{#if buttonStates.delete}
<LoadingText text="Deleting..." size="sm" variant="info" animate={false} />
{:else}
Delete Item
{/if}
</button>
<button
onclick={() => handleAction('export')}
disabled={buttonStates.export}
>
{#if buttonStates.export}
<LoadingText text="Exporting..." size="sm" variant="jasper" animate={true} />
{:else}
Export Data
{/if}
</button>
</div> With Loading States
<script>
import LoadingText from '$lib/loading/components/LoadingText.svelte';
let dataState = $state('loading'); // 'loading' | 'error' | 'empty' | 'loaded'
let retryCount = $state(0);
$: loadingMessage = retryCount > 0
? `Retrying connection (${retryCount}/3)...`
: 'Loading your dashboard...';
async function loadData() {
dataState = 'loading';
try {
const data = await api.fetchDashboard();
if (data.length === 0) {
dataState = 'empty';
} else {
dataState = 'loaded';
}
} catch (error) {
dataState = 'error';
}
}
async function retryLoad() {
retryCount++;
await loadData();
}
</script>
<div class="dashboard">
{#if dataState === 'loading'}
<LoadingText
text={loadingMessage}
variant="default"
size="lg"
animate={true}
/>
{:else if dataState === 'error'}
<div class="error-state">
<p>Failed to load dashboard</p>
<button onclick={retryLoad}>
Try Again
</button>
</div>
{:else if dataState === 'empty'}
<div class="empty-state">
<p>No data available</p>
</div>
{:else}
<!-- Dashboard content -->
{/if}
</div> Message Cycling
When animate is true and using the default “Loading…” text, the component automatically cycles through these messages:
- “Loading…”
- “Preparing content…”
- “Almost there…”
- “Fetching data…”
Each message displays for 3 seconds before transitioning to the next. This cycling only occurs with the default text - custom messages remain static.
// Built-in cycling messages
const loadingMessages = [
'Loading...',
'Preparing content...',
'Almost there...',
'Fetching data...'
];
// Cycles every 3000ms when animate=true and text="Loading..." Size Guidelines
Small (sm)
- Font size: Muted typography size
- Use for: Inline elements, button states, subtle indicators
- Best in: Navigation items, form controls, small cards
Medium (md) - Default
- Font size: Caption typography size
- Use for: Standard loading states, form sections
- Best in: Content areas, modal dialogs, panels
Large (lg)
- Font size: Subheading typography size
- Use for: Primary loading states, section headers
- Best in: Main content areas, page sections, large components
Extra Large (xl)
- Font size: Heading typography size
- Use for: Full-page loading, application initialization
- Best in: Splash screens, initial app loading, major transitions
Color Variants
Default Shimmer
- Colors: Foreground color gradients (tertiary → secondary → primary)
- Use for: General loading states, neutral contexts
- Accessibility: High contrast across all themes
Jasper Accent
- Colors: Jasper color gradients (600 → 500 → 400)
- Use for: Primary actions, important operations, brand-focused loading
- Context: Payment processing, premium features, key workflows
Info Accent
- Colors: Sky blue gradients (600 → 500 → 400)
- Use for: Information retrieval, data processing, neutral actions
- Context: Data fetching, API calls, background operations
Animation Details
Shimmer Effect
- Duration: 3 seconds per cycle
- Easing: Linear for smooth, continuous motion
- Direction: Left to right shimmer sweep
- Hardware acceleration: Uses
transformfor optimal performance
Text Cycling
- Interval: 3 seconds per message
- Transition: Instant text change with continuous shimmer
- Loop: Cycles through all messages repeatedly
- Pause: Shimmer continues during text changes
Accessibility Features
Screen Reader Support
role="status": Indicates dynamic content updatesaria-live="polite": Announces text changes without interrupting- Clear messaging: Uses descriptive, action-oriented text
Visual Accessibility
- High contrast: Shimmer effect maintains readable contrast ratios
- Scalable text: Respects user font size preferences
- Motion consideration: Shimmer is subtle and not overwhelming
Reduced Motion
The component respects prefers-reduced-motion settings:
@media (prefers-reduced-motion: reduce) {
.loading-text-shimmer {
animation: none;
background: var(--foreground);
-webkit-text-fill-color: inherit;
}
} Best Practices
When to Use Loading Text
Use loading text for:
- Short operations (2-10 seconds)
- Indeterminate progress operations
- Inline loading states
- Button loading states
- Contextual loading feedback
Don’t use loading text for:
- Long operations (use progress bars)
- Operations with measurable progress
- Critical system operations (use modal overlays)
- Silent background operations
Message Guidelines
Good loading messages:
- “Saving your changes…”
- “Connecting to server…”
- “Processing payment…”
- “Fetching latest data…”
Avoid:
- Technical jargon: “Executing POST request…”
- Vague messages: “Please wait…”
- Overly long: “We’re currently processing your request and will have results shortly…”
- Negative framing: “This might take a while…”
Size and Context
Small text for:
- Button labels during loading
- Inline status updates
- Navigation items
- Form field validation
Medium text for:
- Card content loading
- Section updates
- Modal dialogs
- Standard feedback
Large/XL text for:
- Primary loading states
- Full-page operations
- Application initialization
- Critical user workflows
Performance Considerations
- Text shimmer uses CSS animations for optimal performance
- Component only renders when needed (conditional rendering recommended)
- Animations use hardware acceleration where possible
- Memory cleanup happens automatically on component unmount
- Message cycling intervals are cleaned up properly