Skip to content
txbrendan/ui

Morph Label

beta

Words that morph in place when they change: the width eases to the new words while the old ones blur out and the new ones blur in.

Preview

press

refused

status

Not saved yet

Install

  1. 1. Add the registry to components.json, once per app. Without it, the shadcn CLI looks up @txbrendan in a public list that txbrendan/ui does not control.

    npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"
  2. 2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.

    npx shadcn@latest add @txbrendan/theme
  3. 3. Add the component.

    npx shadcn@latest add @txbrendan/morph-label

Use it for

  • Words that change where people are looking, like Follow to Following, Save to Saved, or a short status beside a value.
  • Use MorphLabel explicitly when a word change needs the blur treatment. Button uses MotionSwap for its default crossfade.
  • Whatever holds it grows or shrinks with it, so a button eases to its new width instead of snapping.

Avoid it for

  • Long text or a paragraph. It is for a few words on one line. Words wider than the space end in an ellipsis, so in a flex row give the box that holds it min-w-0.
  • Words that change many times a second, like a timer. Use Rolling Number for numbers that count.
  • Saying something only through the change. Words that matter, like an error, also need a role="alert" or role="status" so they are read out.

Props

  • children

    The words. When they change, the old ones blur out as the new ones blur in.

    string | number

  • className

    Extra classes on the label. Later classes win.

    string

Accessibility

  • Screen readers read only the new words. The old words are hidden from them while they fade.
  • It does not announce the change. Put it in a live region, or add a role="status" message, when the change must be heard.
  • The morph follows the --motion-* tokens. Reduced motion lands the new width at once and keeps only the fade, in place. With motion off, the words swap at once.

Examples

Words that change on a press

const [following, setFollowing] = useState(false);

<Button emphasis={following ? "soft" : "solid"} onClick={() => setFollowing((on) => !on)}>
  {following ? "Following" : "Follow"}
</Button>

A short status that changes in place

<p className="text-sm text-muted-foreground" role="status">
  <MorphLabel>{saving ? "Saving…" : "Saved just now"}</MorphLabel>
</p>