Skip to content
Feedback Fancy Stable

StatusMorph

.md

A 1em SVG status icon that morphs between idle, loading, success, and error, closing its spinning ring into a drawn check or cross with a portalled live region announcing each state

Available for Svelte 5, React, and Vue 3.

Preview

Installation

pnpm add fancy-ui-svelte
import { StatusMorph } from "fancy-ui-svelte";

Usage

svelte
<script lang="ts">
	import { Button } from "fancy-ui-svelte";
	import { StatusMorph } from "fancy-ui-svelte";
	import { Switch } from "fancy-ui-svelte";

	type Status = "idle" | "loading" | "success" | "error";

	let status = $state<Status>("idle");
	let failNext = $state(false);

	// Composition recipe from the README, frozen: Button's own spinner covers the
	// entire loading phase (iconStart never mounts until loading is already false),
	// so StatusMorph only ever plays its check-pop / error-shake here, not the full
	// ring→check morph — that's reserved for standalone usage (see the Standalone
	// example).
	async function save() {
		status = "loading";
		await new Promise((resolve) => setTimeout(resolve, 1200));
		status = failNext ? "error" : "success";
	}
</script>

{#snippet icon()}
	<StatusMorph bind:state={status} />
{/snippet}

<div class="flex w-full flex-col items-center gap-6 p-6">
	<Button loading={status === "loading"} iconStart={icon} onclick={save}>Save changes</Button>

	<Switch bind:checked={failNext}>Fail next save</Switch>
</div>

Examples

Basic Usage

Standalone

The full ring → check/cross morph, outside the Button composition.

Semantic Tone

tone="semantic" reads the shared status colors.

Props

PropTypeDefaultDescription
state "idle" | "loading" | "success" | "error""idle"Current state, two-way bound. The component only ever writes it back to "idle" itself, when resetAfter fires — every other write is the caller's, and is always honoured
resetAfter number1800Milliseconds until an automatic reset to "idle" after "success" or "error". 0 disables the timer entirely (manual reset only)
labels { loading?: string; success?: string; error?: string }{ loading: "Loading", success: "Done", error: "Failed" }Live-region text per state; unset keys fall back to the defaults
tone "current" | "semantic""current""current" paints every glyph in currentColor; "semantic" reads the shared --ft-status-running/-done/-error vocabulary instead
haptic booleanfalseBest-effort tactile feedback (the success/error haptic patterns) on entering those states; silently a no-op wherever the Vibration API is unsupported or refused
class string-Additional CSS classes
ref HTMLSpanElement | nullnullBindable reference to the root element

Slots

SlotDescription
idle Custom idle content, rendered in the same calc(1em + 1px) footprint instead of the default transparent scaffold, so swapping to it never shifts layout