Letter Swap <mv-letter-swap>

Rolagem letra a letra para links de navegação e botões: ao passar o mouse ou receber o foco do teclado, cada letra rola para longe e revela sua gêmea, em cascata, ou gira como um pequeno cubo 3D. As letras reais continuam no DOM e em suas próprias caixas fixas, então o rótulo nunca se reorganiza, quebra entre palavras, continua selecionável e pode mudar em tempo de execução.

CategoriaTexto animado
TipoWeb Component (<mv-letter-swap>)
Statusestável
Keywordsletter, swap, roll, flip, 3d, hover, nav, button, stagger, accessible

When to use

  • Nav links or button labels should roll their letters on hover and keyboard focus
  • A 3D cube flip of each letter suits a playful or technical brand
  • A short label should roll once when it enters the viewport, on page load or on demand
  • The revealed letters should take an accent color while a control is hovered

Avoid when

  • A different label must roll in on hover, or a highlighter should glide between phrases → use Rolling Text instead
  • Long sentences or paragraphs; the per-letter roll is meant for short labels
  • The label is underlined; per-letter boxes do not carry the parent text decoration

Instalação

node scripts/add.mjs letter-swap --out ./src/marvelous

Agente de IA com o servidor MCP do Marvelous UI: install_components({ slugs: ["letter-swap"], 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/letter-swap/letter-swap.js, components/letter-swap/letter-swap.css.

Uso

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:2.25rem;justify-items:center;text-align:center">
  <nav aria-label="Demo" style="display:flex;gap:.25rem;padding:.35rem;border:1px solid var(--mv-border);border-radius:999px;background:var(--mv-surface);box-shadow:var(--mv-shadow-sm)">
    <a href="#" style="padding:.45rem .9rem;border-radius:999px;color:var(--mv-fg);text-decoration:none;font:500 .9rem var(--mv-font-sans)"><mv-letter-swap>Work</mv-letter-swap></a>
    <a href="#" style="padding:.45rem .9rem;border-radius:999px;color:var(--mv-fg);text-decoration:none;font:500 .9rem var(--mv-font-sans)"><mv-letter-swap stagger-from="center">Studio</mv-letter-swap></a>
    <a href="#" style="padding:.45rem .9rem;border-radius:999px;color:var(--mv-fg);text-decoration:none;font:500 .9rem var(--mv-font-sans)"><mv-letter-swap stagger-from="random">Journal</mv-letter-swap></a>
    <a href="#" style="padding:.45rem .9rem;border-radius:999px;background:var(--mv-fg);color:var(--mv-bg);text-decoration:none;font:500 .9rem var(--mv-font-sans)"><mv-letter-swap direction="down">Contact</mv-letter-swap></a>
  </nav>

  <p style="margin:0;font:800 clamp(2.2rem,6vw,3.8rem)/1 var(--mv-font-sans);letter-spacing:-.045em;text-transform:uppercase">
    <mv-letter-swap variant="flip" trigger="view" loop stagger="45" duration="700">Creative</mv-letter-swap>
    <span style="color:var(--mv-fg-muted)">by nature</span>
  </p>

  <div style="display:flex;gap:.75rem;flex-wrap:wrap;justify-content:center">
    <button class="mv-button" data-size="lg"><mv-letter-swap variant="flip" stagger="30">Start for free</mv-letter-swap></button>
    <button class="mv-button" data-size="lg" data-variant="outline"><mv-letter-swap stagger="15" duration="420" easing="out" style="--mv-letter-swap-twin:var(--mv-accent)">Watch the demo</mv-letter-swap></button>
  </div>
</div>

API

Attributes

NameTipoDefaultDescription
textstringLabel to roll. Defaults to the element text; changing it rebuilds the letters.
variantslide | flipslideslide: the letter slides; flip: a rotating 3D cube.
triggerhover | view | load | manualhoverhover follows the parent link or button (mouse or pen hover, keyboard focus); view and load roll once; manual waits for replay().
directionup | downupRoll direction: up (the twin rises from below) or down.
durationnumber500Duration per letter (ms).
staggernumber22Delay between letters (ms).
stagger-fromstart | center | end | randomstartWhere the cascade starts.
delaynumber0Initial delay (ms).
easingCSS easingvar(--mv-ease-in-out)Easing curve: any CSS easing, or a token name (out, in, in-out, emphasized, spring, bounce, snappy).
loopbooleanWith view, load or manual: keeps rolling while visible, pausing 1.6 s between rolls.

Properties

NameTipoDescription
labelstringRead only. The label as announced to screen readers.

Methods

NameDescription
replay()Rolls every letter once and settles back invisibly. Returns a Promise resolved when done (at once under reduced motion).

CSS variables

NameDefaultDescription
--mv-letter-swap-leading1.2Unitless line height of each letter box (the clipping window). The extra leading is cancelled, so the line keeps its height.
--mv-letter-swap-easingvar(--mv-ease-in-out)Default easing curve.
--mv-letter-swap-twincurrentColorColor of the revealed twin while hovered or focused (a replay always lands on identical letters).
--mv-letter-swap-perspective20emPerspective depth (flip variant).

Accessibility

Screen readers get the label once, as plain text; the rolling letters are aria-hidden, so there is no per-letter reading. The real letters stay in the DOM (selectable, copied once) and the twin is drawn by CSS. Hover follows the parent control and its keyboard :focus-visible; touch does not trigger it. Each letter keeps a fixed box, so nothing reflows. Reduced motion, print and forced colors show static text.

Esta página foi traduzida com IA. Informar um problema de tradução