Word Rotate <mv-word-rotate>
Palavra rotativa no meio da frase para frases de destaque (slide, flip 3D, desfoque ou fade, letra por letra) cuja largura se ajusta suavemente para que a frase nunca pule. Acessível por padrão: os leitores de tela ouvem uma única frase estável que lista todas as opções, ela para ao passar o mouse e no foco de um link que a envolve, pausa fora da tela e em abas ocultas, e fica em uma só palavra com movimento reduzido.
| Categoria | Texto animado |
|---|---|
| Tipo | Web Component (<mv-word-rotate>) |
| Status | estável |
| Keywords | rotating-text, flip-words, text-loop, hero, headline, inline, layout-animation, accessible |
When to use
- A hero headline should cycle one inline word, like Build for designers, developers or teams
- The rotating word should sit in a tinted pill whose width animates between words
- A sold or public page needs a rotating word that screen readers hear once, not on every change
- The word loop must be driven from script with next, previous or an index
Avoid when
- A standalone word should melt into the next with a gooey blur → use Morphing Text instead
- Whole phrases should be typed and deleted letter by letter → use Typewriter instead
Instalação
node scripts/add.mjs word-rotate --out ./src/marvelousAgente de IA com o servidor MCP do Marvelous UI: install_components({ slugs: ["word-rotate"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).
Arquivos copiados (dependências incluídas): tokens/tokens.css, core/base.css, core/dom.js, core/element.js, core/motion.js, core/observe.js, components/word-rotate/word-rotate.js, components/word-rotate/word-rotate.css.
Uso
Início rápido, a menor marcação que funciona:
<p>Build for <mv-word-rotate words="designers, developers, teams"></mv-word-rotate></p>Marcação de referência, para usar como ponto de partida e personalizar com atributos, data-* e variáveis CSS:
<div style="display:grid;gap:2rem;width:min(100%,46rem);margin-inline:auto;text-align:center">
<h2 style="margin:0;font:700 clamp(1.9rem,4.6vw,3.2rem)/1.15 var(--mv-font-display);letter-spacing:-.035em">
Build for
<mv-word-rotate data-variant="pill" words="designers, developers, teams" style="--mv-word-rotate-color:var(--mv-accent-fg)"></mv-word-rotate>
<br>without the all-nighters.
</h2>
<div style="display:grid;gap:.9rem;font-size:clamp(1.1rem,2.4vw,1.45rem);font-weight:600;letter-spacing:-.02em">
<p style="margin:0">Interfaces that feel <mv-word-rotate effect="flip" interval="2200" words="fast, reliable, elegant, accessible" colors="var(--mv-accent), var(--mv-info), var(--mv-success), var(--mv-warning)"></mv-word-rotate></p>
<p style="margin:0">From prototype to <mv-word-rotate effect="blur" interval="2400" conjunction="and" words="production, launch, growth" style="--mv-word-rotate-color:var(--mv-accent)"></mv-word-rotate></p>
<p style="margin:0;color:var(--mv-fg-muted)">Ships as <mv-word-rotate effect="slide" direction="down" interval="1800" words="HTML, React, Vue, Svelte, Astro" style="--mv-word-rotate-color:var(--mv-fg);font-family:var(--mv-font-mono)"></mv-word-rotate>, zero friction.</p>
<p style="margin:0;color:var(--mv-fg-muted)">Looks <mv-word-rotate effect="fade" interval="2000" words="crisp, smooth, clean" style="--mv-word-rotate-color:var(--mv-fg)"></mv-word-rotate> by default.</p>
</div>
<p style="margin:0;font-size:.85rem;color:var(--mv-fg-muted)">Hover a word to hold it. Screen readers hear each sentence once, with every option.</p>
</div>API
Attributes
| Name | Tipo | Default | Description |
|---|---|---|---|
words | string | Words separated by commas (or |). Otherwise: one child per word. | |
effect | slide | flip | blur | fade | slide | Transition between two words; letters enter one after another. |
interval | number (ms) | 2600 | How long each word is shown. |
duration | number (ms) | 560 (680 for flip) | Duration of the transition and of the width glide. |
stagger | number (ms) | 18-30 (0 for fade) | Delay between letters. |
easing | CSS easing | var(--mv-ease-out) | Letter easing curve (tokens accepted). |
direction | up | down | up | Slide / flip direction. |
colors | list of CSS colors | One color per word, cycled. | |
conjunction | or | and | none | or | How the screen reader sentence joins the words: “designers, developers, or teams”. Uses the page language (lang). |
label | string | Replaces the screen reader sentence entirely. | |
paused | boolean | Pauses the rotation. It also holds by itself on hover, on focus of a wrapping link or button, off-screen and in hidden tabs. | |
data-variant | pill | Tinted pill whose width animates. | |
data-held | boolean | Set by the component while hover or focus holds the word (for styling). |
Properties
| Name | Tipo | Description |
|---|---|---|
index | number | Current word (settable, wraps around; assignable before the element is defined). |
Methods
| Name | Description |
|---|---|
next() | Next word. |
previous() | Previous word. |
Events
| Name | Description |
|---|---|
mv-change | New word shown (detail.index, detail.word). |
CSS variables
| Name | Default | Description |
|---|---|---|
--mv-word-rotate-color | inherit | Word color. |
--mv-word-rotate-duration | Default duration. | |
--mv-word-rotate-stagger | Default stagger. | |
--mv-word-rotate-easing | Letter easing curve. | |
--mv-word-rotate-width-easing | var(--mv-ease-emphasized) | Width glide easing curve. |
--mv-word-rotate-radius | 0.3em | Pill corner radius. |
Accessibility
The animated word is aria-hidden; a visually hidden sentence lists every option once (“designers, developers, or teams”, built with Intl.ListFormat in the page language), so screen readers never hear a change. There is no live region. The rotation holds while the pointer is on the word or while focus is inside a wrapping link or button, pauses off-screen and in hidden tabs, and under reduced motion shows one static word with no width animation. The pill keeps a visible outline in forced colors.