Feedback
Spinner
Shows that something is on its way when you cannot say how long it will take.
Installation
npx sinaui add spinnerCopy src/lib/ui/Spinner.svelte and tokens.css. No dependencies. When
you know how far along it is, use Progress instead.
Examples
Basic
A spinner on its own needs a label.
<script lang="ts">
import Spinner from '#lib/ui/Spinner.svelte';
</script>
<Spinner label="Loading" />Large
size sets the width in pixels.
<script lang="ts">
import Spinner from '#lib/ui/Spinner.svelte';
</script>
<Spinner size={32} label="Loading" />Color
It takes the text color around it.
<script lang="ts">
import Spinner from '#lib/ui/Spinner.svelte';
</script>
<!-- It takes the text color around it. -->
<span class="accent"><Spinner size={32} label="Loading" /></span>
<style>
.accent {
display: contents;
color: var(--ui-accent);
}
</style>Beside words
Next to its own words it needs no label.
Fetching manuscripts…
<script lang="ts">
import Spinner from '#lib/ui/Spinner.svelte';
</script>
<!-- Beside its own words, it's decoration: the text says what's happening. -->
<p class="status" role="status"><Spinner /> Fetching manuscripts…</p>
<style>
.status {
display: flex;
align-items: center;
gap: 0.5rem;
margin: 0;
color: var(--ui-muted);
}
</style>Only when it takes a while
With a delay, a quick load never flashes a spinner; a slow one still shows it.
<script lang="ts">
import Button from '#lib/ui/Button.svelte';
import Spinner from '#lib/ui/Spinner.svelte';
let loading = $state(false);
let result = $state('');
function load(ms: number) {
loading = true;
result = '';
setTimeout(() => {
loading = false;
result = `Loaded in ${new Intl.NumberFormat('en', { style: 'unit', unit: 'millisecond' }).format(ms)}.`;
}, ms);
}
</script>
<div class="stack">
<div class="actions">
<Button variant="secondary" disabled={loading} onclick={() => load(150)}>Quick load</Button>
<Button variant="secondary" disabled={loading} onclick={() => load(2000)}>Slow load</Button>
</div>
<!-- Fixed height: the spinner and the result swap without moving anything. -->
<div class="out" aria-busy={loading}>
{#if loading}<Spinner size={20} delay={300} label="Loading" />{:else}{result}{/if}
</div>
</div>
<style>
.stack {
display: grid;
gap: 1rem;
}
.actions {
display: flex;
gap: 0.5rem;
}
.out {
display: flex;
align-items: center;
block-size: 1.5rem;
color: var(--ui-muted);
}
</style>Props
| Prop | Type | Default |
|---|---|---|
size | number: px | 16 |
label | string: what is loading | |
delay | number: ms before it appears | 0 |
Accessibility
- With a
labelit is a progress indicator with no value, read as “Loading” (or your words). - Without one it is hidden, for when text beside it already says what is happening.
- Mark the area that is loading with
aria-busy="true". - With reduced motion it keeps turning, slowly: it is the only sign that something is happening.