Metamorph

Morph Button

A button that shrinks into a ball while it sends and opens again with a check.

Preview

Installation

Terminal
npx @usefluid/cli add morph-button

Usage

MorphButton.tsx
import { MorphButton, type MorphButtonState } from "./MorphButton";

const [state, setState] = useState<MorphButtonState>("idle");

<MorphButton
  state={state}
  labels={{ idle: "Place order", loading: "Sending", done: "Order sent" }}
  onClick={async () => {
    setState("loading");
    await placeOrder();
    setState("done");
  }}
/>

API reference

PropTypeDefaultDescription
state"idle" | "loading" | "done"—Drive it from your real request.
labels{ idle, loading?, done }Send / Sending / SentThe words for each state.
...button props—Anything a <button> takes.

Keyboard and screen readers

aria-busy while loading, and a status region announces Sending and Sent.

Notes

It stays on done until you set state back to idle.

Every colour comes from the --fluid-* variables, and the piece is yours to change.

How it is made

The button's width is a spring that goes from the size of the text down to 48 px and back. Inside, the icon is a contour of the engine: an arrow, shapes while sending, and a check at the end. The state text goes to screen readers.

For your agent

For your agent

Add Morph Button to my project with the Fluid library. Use MorphButton on the form submissions in my project. Read my project first. Use my logo, my colours and my font through the --fluid-* variables. Keep 44px touch targets, visible focus and reduced motion. If I have no SVG logo, ask before making one up.