Spinning Text
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
<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
<template>
<div class="p-4 flex justify-center">
<MSpinningText children="SPINNING-TEXT-IS-AWESOME" />
</div>
</template>
Tight ring, reversed
<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
`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
Customize
Props
| Prop | Type | Default | Description |
|---|---|---|---|
childrenrequired | string | - | Text to spin. |
style | object | undefined | Custom styles to apply to the container. |
duration | number | 10 | Animation duration in seconds. |
className | string | undefined | CSS classes to apply to the container. |
class | string | undefined | Classes for the container element, merged with className after the theme's
root slot. |
reverse | boolean | false | Reverse the spinning direction. |
fontSize | number | 1 | Font size multiplier. |
radius | number | 5 | Radius of the spinning circle, in ch. |
transition | object | {} | Animation transition options. Merged over the base
{ repeat: Infinity, ease: 'linear' }; a duration here wins over the
duration prop. |
variants | object | {} | Animation variants for container and items. variants.container is spread over
the default visible variant, so a custom visible replaces the 360° target. |
ui | object | undefined | Per-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 |
|---|---|---|---|
ui | object | - | 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
rotatethrough motion-v's variants. Each letter's position is a statictransform(translate+rotate+translateYby index) written once instyle— one running animation regardless of how many letters are on the ring. - Node count equals character count: one
Motionspan per letter plus onesr-onlycopy of the string. Every letter is absolutely positioned inside arelativeroot with no other content, so the root itself takes no layout space and the ring overflows a zero-size box. radiusandfontSizeonly change the values written into each letter'sstyle— there is no measuring, no observer and no scroll or pointer listener anywhere in the component.- The
containerVariantsandfinalTransitioncomputeds rebuild on every render but read only props, so they are cheap; the letter styles are plain function calls during render.