Text & Numbers

Spinning Text

Text that spins with 3D effects.

The SpinningText component creates text that spins with 3D effects. It's perfect for creating eye-catching headings and animated text elements.

Usage

Welcome to NUXT-MOTION-BLOCK

Create stunning interfaces with our collection of animated components that ENHANCE-USER-ENGAGEMENT and drive results.

Real-time Preview

See changes instantly as you type with our live preview feature

99.9-PERCENT-UPTIME
<template>
  <div class="p-8">
    <div class="max-w-3xl mx-auto text-center">
      <h1 class="text-4xl md:text-5xl font-bold mb-6">
        Welcome to
        <MSpinningText
          children="NUXT-MOTION-BLOCK"
          class="text-primary"
        />
      </h1>

      <p class="text-xl text-muted mb-8">
        Create stunning interfaces with our collection of animated components that
        <MSpinningText
          children="ENHANCE-USER-ENGAGEMENT"
          class="text-purple-600 dark:text-purple-400 font-semibold"
        />
        and drive results.
      </p>

      <div class="bg-default rounded-xl shadow-lg p-6 max-w-2xl mx-auto mb-8">
        <div class="flex flex-col md:flex-row items-center justify-between gap-6">
          <div class="flex-1 text-center md:text-left">
            <h3 class="text-lg font-semibold mb-2">
              Real-time Preview
            </h3>
            <p class="text-muted text-sm">
              See changes instantly as you type with our live preview feature
            </p>
          </div>
          <MSpinningText
            children="99.9-PERCENT-UPTIME"
            class="text-2xl font-bold text-green-600 dark:text-green-400"
          />
        </div>
      </div>

      <div class="flex flex-wrap justify-center gap-4">
        <UButton
          size="lg"
          color="primary"
        >
          Get Started
        </UButton>
        <UButton
          size="lg"
          variant="outline"
        >
          View Documentation
        </UButton>
      </div>
    </div>
  </div>
</template>

Examples

Minimal ring

SPINNING-TEXT-IS-AWESOME
<template>
  <div class="p-4 flex justify-center">
    <MSpinningText children="SPINNING-TEXT-IS-AWESOME" />
  </div>
</template>

Tight ring, reversed

TIGHT-AND-FASTREVERSED-SLOW
<template>
  <div class="flex flex-col items-center gap-10 p-8">
    <MSpinningText
      children="TIGHT-AND-FAST"
      class="text-sm font-bold tracking-widest text-primary"
      :radius="2.5"
      :font-size="0.8"
      :duration="4"
    />
    <MSpinningText
      children="REVERSED-SLOW"
      class-name="text-xs uppercase text-muted"
      reverse
      :radius="4"
      :font-size="0.6"
      :duration="16"
    />
  </div>
</template>

radius is measured in ch — the letter's own width — so a small ring with a small fontSize reads as a tight badge, while reverse spins the second ring the other way. className lands on the container alongside class, which is how the second ring is greyed without touching its letters.

Custom motion

DOUBLE-TURN

`variants.container` replaces the 360° target with 720°, and `transition` switches the easing to ease-in-out for a softer start and stop at each cycle seam.

<template>
  <div class="flex flex-col items-center gap-4 p-8">
    <MSpinningText
      children="DOUBLE-TURN"
      class="text-lg font-semibold text-secondary"
      :duration="6"
      :variants="variants"
      :transition="transition"
      :style="{ filter: 'drop-shadow(0 0 12px currentColor)' }"
    />
    <p class="max-w-xs text-center text-sm text-muted">
      `variants.container` replaces the 360° target with 720°, and
      `transition` switches the easing to ease-in-out for a softer start and stop at each cycle seam.
    </p>
  </div>
</template>

<script setup>
const variants = {
  container: {
    visible: { rotate: 720 },
  },
}
const transition = {
  ease: 'easeInOut',
}
</script>

variants.container is spread over the default visible variant, so a custom visible replaces the 360° target outright — here 720°, two full turns per cycle. transition merges over the base { repeat: Infinity, ease: 'linear' }, and its duration wins over the duration prop when both are set.

Customize

NUXT-MOTION-BLOCK

Customize

TextNUXT-MOTION-BLOCK
Duration (s)10
Reverse
Font size1
Radius5

Props

Prop Type Default Description
childrenrequiredstring-Text to spin.
styleobjectundefinedCustom styles to apply to the container.
durationnumber10Animation duration in seconds.
classNamestringundefinedCSS classes to apply to the container.
classstringundefinedClasses for the container element, merged with className after the theme's root slot.
reversebooleanfalseReverse the spinning direction.
fontSizenumber1Font size multiplier.
radiusnumber5Radius of the spinning circle, in ch.
transitionobject{}Animation transition options. Merged over the base { repeat: Infinity, ease: 'linear' }; a duration here wins over the duration prop.
variantsobject{}Animation variants for container and items. variants.container is spread over the default visible variant, so a custom visible replaces the 360° target.
uiobjectundefinedPer-slot classes merged over the theme defaults. See UI.

UI

This component's theme exposes the slots below. Override any of them per instance with the ui prop, or app-wide under ui.spinningText in app.config.ts.

Prop Type Default Description
uiobject-Per-slot classes merged over the theme defaults.
Slot Props Description
root-relative
letter-absolute left-1/2 top-1/2 inline-block
srLabel-sr-only

Accessibility

Every letter is aria-hidden="true" and the full children string is mirrored into the srLabel slot as an sr-only span, so assistive technology reads the text once, in order, rather than as a ring of disconnected characters. The root is a plain span with no role.

There is no reduced-motion handling: the container runs a repeat: Infinity rotation regardless of prefers-reduced-motion. For a decorative badge that is usually fine — but do not put meaning that has to sit still inside one without pausing it yourself.

Performance

  • The container runs a single infinite rotate through motion-v's variants. Each letter's position is a static transform (translate + rotate + translateY by index) written once in style — one running animation regardless of how many letters are on the ring.
  • Node count equals character count: one Motion span per letter plus one sr-only copy of the string. Every letter is absolutely positioned inside a relative root with no other content, so the root itself takes no layout space and the ring overflows a zero-size box.
  • radius and fontSize only change the values written into each letter's style — there is no measuring, no observer and no scroll or pointer listener anywhere in the component.
  • The containerVariants and finalTransition computeds rebuild on every render but read only props, so they are cheap; the letter styles are plain function calls during render.
Copyright © 2026