Letter Swap <mv-letter-swap>

Roulement lettre par lettre pour les liens de navigation et les boutons : au survol ou au focus clavier, chaque lettre roule pour révéler son double, en cascade, ou tourne comme un petit cube 3D. Les vraies lettres restent dans le DOM et dans leurs propres boîtes fixes, si bien que le libellé ne se recompose jamais, passe à la ligne entre les mots, reste sélectionnable et peut changer à l’exécution.

CatégorieTexte animé
TypeWeb Component (<mv-letter-swap>)
Statutstable
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

Installation

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

Agent IA avec le serveur MCP de Marvelous UI : install_components({ slugs: ["letter-swap"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).

Fichiers copiés (dépendances comprises) : 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.

Utilisation

Balisage de référence : partez de celui-ci et personnalisez-le avec les attributs, data-* et les variables 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

NameTypeDefaultDescription
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

NameTypeDescription
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.

Cette page a été traduite par IA. Signaler un problème de traduction