The letters of the label fold inward and become three hopping dots while the action runs. The dots then pinch together and the done label rises in letter by letter behind a drawn check.
The quiet power of default settings
npx shadcn@latest add https://hyperblur.dev/r/button-dots-morph.jsonWorks in any React project set up with the shadcn CLI. The file lands in components/hyperblur/button-dots-morph.tsx.
import { ButtonDotsMorph } from "@/components/hyperblur/button-dots-morph"
<ButtonDotsMorph doneLabel="Published" onClick={() => publish()}>Publish story</ButtonDotsMorph>| Prop | Type | Default | Description |
|---|---|---|---|
| children | string | "Publish story" | Idle label. Split into letters, so it must be a string. |
| loadingLabel | string | "Publishing" | Accessible label and announcement while loading. |
| doneLabel | string | "Published" | Label shown after loading. |
| status | "idle" | "loading" | "done" | undefined | Controlled state. Leave unset to let the button manage itself. |
| onClick | (event) => unknown | undefined | Called on click. A returned promise keeps the dots running until it settles; a rejection returns to idle. |
| duration | number | 1700 | Milliseconds of loading when onClick does not return a promise. |
| resetAfter | number | 2000 | Milliseconds before the done state returns to idle. |
| variant | "light" | "accent" | "dark" | "light" | White pill, accent pill or graphite pill. |
| accent | string | "#2f7bff" | Accent pill color and focus ring. |
| disabled | boolean | false | Disables interaction and dims the control. |