slot-text.js: Text Roll Animation for UI Labels

Category: Animation , Javascript , Recommended | June 14, 2026
AuthorDanilaa1
Last UpdateJune 14, 2026
LicenseMIT
Views0 views
slot-text.js: Text Roll Animation for UI Labels

slot-text is a dependency-free JavaScript utility that creates rolling text animations where each character slides and rotates independently into place.

You can use the library to animate text transitions on copy-to-copied buttons, status messages, numeric counters, or any short label that changes dynamically.

Features:

  • Zero runtime dependencies and a bundle footprint under 2 KB gzipped.
  • Per-character roll animation with independent direction, stagger, and easing.
  • Spam-safe flash mode that auto-reverts text after a configurable delay.
  • Built-in chromatic color helper for rainbow per-character effects.
  • Respects the host element’s font, including proportional and italic typefaces.
  • Skips identical characters to avoid unnecessary motion.
  • Interrupt control lets you either cut off or queue pending animation calls.
  • Framework-specific wrappers for React, Vue, Solid, and Svelte.

How To Use It:

Installation

Install the package from npm.

npm install slot-text

Import the stylesheet once in your application entry file or component tree.

import "slot-text/style.css";

Basic Usage

Create a target element in your HTML.

<button id="publish-label" type="button">Publish</button>

Attach slotText() to the element and change the label when the user performs an action.

import "slot-text/style.css";
import { slotText, chromatic } from "slot-text";
const publishButton = document.querySelector("#publish-label");
// Create the animated label controller.
const publishLabel = slotText(publishButton, "Publish");
// Roll in a temporary success label with a color sweep.
publishButton.addEventListener("click", function () {
  publishLabel.flash("Published", {
    enter: {
      color: chromatic()
    }
  });
});

React Usage

A React button can bind the visible label to component state. Import the CSS once and pass the animated text through the SlotText component.

import "slot-text/style.css";
import { useState } from "react";
import { SlotText } from "slot-text/react";
import { chromatic } from "slot-text";
export function ExportButton() {
  const [exported, setExported] = useState(false);
  return (
    <button
      type="button"
      aria-label={exported ? "Report exported" : "Export report"}
      onClick={() => setExported(true)}
    >
      <SlotText
        text={exported ? "Exported" : "Export"}
        options={{
          direction: exported ? "up" : "down",
          color: exported ? chromatic() : undefined
        }}
      />
    </button>
  );
}

Vue Usage

A Vue component can render animated text from reactive state. Import the component and pass a normal options object.

<script setup lang="ts">
import "slot-text/style.css";
import { ref } from "vue";
import { SlotText } from "slot-text/vue";
const approved = ref(false);
</script>
<template>
  <button
    type="button"
-aria-label="approved ? 'Invoice approved' : 'Approve invoice'"
    @click="approved = true"
  >
    <SlotText
-text="approved ? 'Approved' : 'Approve'"
-options="{ direction: approved ? 'up' : 'down' }"
    />
  </button>
</template>

Solid Usage

A Solid component can attach the action to a span inside a button. Keep the accessible label on the button because the visual text changes during the animation.

import "slot-text/style.css";
import { createSignal } from "solid-js";
import { slotText } from "slot-text/solid";
export function InviteButton() {
  const [label, setLabel] = createSignal("Invite");
  return (
    <button
      type="button"
      aria-label={label()}
      onClick={() => setLabel("Invited")}
    >
      <span
        use:slotText={{
          text: label(),
          options: { direction: "up" }
        }}
      />
    </button>
  );
}

Svelte Usage

A Svelte button can use the action form for local UI state. The animated span receives the label and options object from component variables.

<script lang="ts">
  import "slot-text/style.css";
  import { slotText } from "slot-text/svelte";
  let status = "Follow";
</script>
<button
  type="button"
  aria-label={status}
  on:click={() => status = "Following"}
>
  <span
    use:slotText={{
      text: status,
      options: { direction: "up" }
    }}
  ></span>
</button>

Configuration Options

  • direction ("up" | "down"): Sets the vertical roll direction. The default value is "down".
  • stagger (number): Sets the delay between character animations in milliseconds. The default value is 45.
  • duration (number): Sets the animation time for each character in milliseconds. The default value is 300.
  • exitOffset (number): Sets the delay before the previous character exits in milliseconds. The default value is 50.
  • easing (string): Sets the CSS easing function for the roll transition. The default value is a spring-style cubic-bezier curve.
  • bounce (number): Sets the overshoot amount used by the animation. The default value is 0.6.
  • color (string | function): Sets a temporary text color for entering characters. A function receives the character index and total character count.
  • colorFade (number): Sets the time in milliseconds for the animated color to fade back to the base text color. The default value is 280.
  • skipUnchanged (boolean): Keeps identical characters from rolling again. The default value is true.
  • interrupt (boolean): Controls behavior during an active animation. true starts the latest animation immediately. false waits for the current roll to finish and drops duplicate pending text.

API Methods

import {
  slotText,
  buildSlotText,
  animateSlotText,
  clearSlotText,
  chromatic
} from "slot-text";
const statusElement = document.querySelector("#payment-status");
// Create an animated text controller for one DOM element.
const paymentLabel = slotText(statusElement, "Pending", {
  direction: "down"
});
// Roll to a new label and keep that value.
paymentLabel.set("Paid", {
  direction: "up",
  duration: 240
});
// Roll to a temporary label and return to the previous value.
paymentLabel.flash("Saved", {
  revertAfter: 1400,
  enter: {
    direction: "up",
    color: chromatic()
  },
  exit: {
    direction: "down"
  }
});
// Remove timers and restore the current text value.
paymentLabel.destroy();
// Build the per-character DOM structure directly.
buildSlotText(statusElement, "Ready");
// Animate a DOM element directly without the higher-level controller.
animateSlotText(statusElement, "Processing", {
  direction: "up",
  stagger: 35
});
// Clear the slot-text DOM structure and leave fallback text.
clearSlotText(statusElement, "Idle");
// Create a per-character hue sweep for the color option.
const rainbowText = chromatic({
  from: 20,
  spread: 280,
  saturation: 90,
  lightness: 58
});

Alternatives:

You Might Be Interested In:


Leave a Reply