Skip to content
animorio
IT
Back to the library

The reluctant button

Text, HoverCSS onlyEasyCompressed weight: Demo 5.2 KBno librariesFreeDownload the zip

A button that says no twice before copying the address, and every time the sentence decodes letter by letter out of block glyphs.

The code

A folder that runs on its own: index.html, style.css, script.js and the libraries from a CDN. The React and Vue tabs hold the same demo as a ready component, which switches its animations off when it leaves the page.

<!doctype html>
<html lang="it">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Il bottone che non vuole</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/archivo@5.3.0/standard.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="pagina">
    <header class="testa">
      <p class="marchio">Scarti</p>
      <p class="testa__riga">Mostra di foto venute male</p>
    </header>

    <section class="invito" aria-labelledby="titolo">
      <h1 class="invito__titolo" id="titolo">Hai una foto venuta&nbsp;male?</h1>
      <p class="invito__testo">Mandacela: la appendiamo in sala, accanto alle altre. Se è venuta proprio male, anche meglio.</p>
      <!-- il bottone dice di no due volte; al terzo clic cede e copia l'indirizzo -->
      <button class="bottone" type="button" data-bottone data-autoclic><span class="bottone__testo" data-testo>Non scriverci</span></button>
      <p class="esito" data-esito aria-live="polite"></p>
    </section>

    <footer class="piede">
      <a href="#" data-mescola>Orari e indirizzo</a>
      <a href="#" data-mescola>Instagram</a>
      <a href="#" data-mescola>Per la stampa</a>
    </footer>
  </main>

  <script src="script.js"></script>
</body>
</html>

Playground

Change values, colours and text with the controls and watch the animation respond live. Or write in the code yourself. Then download the folder with your changes.

25 values to change

Or ask Claude Code for it

With the Animorio connector, Claude Code takes it from here, guide included, and fits it into your project. Paste this request:

Take “The reluctant button” from Animorio and fit it into my site

What it does

An exhibition’s contact page, all blue: “Hai una foto venuta male?” (got a badly taken photo?), and a paper-coloured button saying “Non scriverci” (don’t write to us). At the first click it answers “Sei sicuro?” (are you sure?) and shrinks back, at the second “Dico sul serio” (I mean it) and shakes sideways, at the third it gives in: “Hai vinto. Copiato.” (you win. copied.), and only now does the address go to the clipboard. The text never changes at once: every letter spins through block glyphs and lands on the new one, left to right. After two and a half seconds the button forgets everything and starts over. When the pointer passes, the sentence reshuffles, and so do the links at the bottom.

How it works

Three sentences, one copy

The button counts clicks: each one moves to the next sentence, and only the third calls navigator.clipboard.writeText, falling back to an invisible textarea and execCommand('copy'). Clicks that do not come from a person, with e.isTrusted false, never touch the clipboard. The last sentence stays 2.4 seconds; halfway through, the button forgets after 4.

The decode

Every character gets a random start up to 0.2 seconds and an end 0.1 to 0.34 seconds later, plus 12 ms for each position: the ends drift to the right and the sentence reads from the left. Before its start a character shows the old letter, between start and end a block glyph, after that the new letter. The glyph changes only with a 0.3 probability on each frame.

Still spaces and a locked width

Spaces are never scrambled: the sentence keeps its shape even while everything inside it moves. The button measures itself once with all four sentences and takes the width of the longest, so the sentences change and it does not. The block glyphs come from another typeface and are wider than the letters: they are measured once, and each sits in a span squeezed with a scaleX to the average width of a letter, with a negative margin giving back the space removed.

The body

Every refusal has its own reaction, with the Web Animations API: at the first the button shrinks to 0.93 and back, at the second it shakes sideways by −9, 8, −6 and 3 px, at the third it swells to 1.07. When the pointer passes, the sentence decodes again into itself, and the footer links do the same.

Values to change

NameFileDefaultWhat it changes
FRASIscript.js4The button’s four sentences, from the first no to giving in.
INDIRIZZOscript.js'scarti@example.com'The address that reaches the clipboard at the third click: a placeholder.
RESTA, PAZIENZAscript.js2.4, 4After how many seconds the button forgets, at the end and halfway.
GLIFIscript.js'▚▞▖▗▘▝░▒▓█'The block glyphs that spin during the decode.
ATTESA, GIROscript.js0.2, [0.1, 0.34]The longest delay before a character spins, and how long it spins.
CAMBIAscript.js0.3The probability that a glyph changes on each frame.

Try them in the Playground

When to use it

For a contact, a sign-up or any button that can afford a bit of irony. The decode on its own suits the links and labels of a site with a technical character.

On phones

The same: tap three times. There is no reshuffle on hover, because on a phone there is no pointer passing by.

With reduced motion

No glyphs and no reactions: the sentences simply change, and the address is still copied at the third click.

Pitfalls

  • If an old click handler copies at the first click and the new one at the third, both fire: the address reaches the clipboard before the button has finished playing hard to get. One handler.
  • A new glyph on every frame, at 120 Hz, is a grey blur: change it with a probability, so every block stays readable for a few frames.
  • If spaces are scrambled too, the sentence turns into a bar of noise and you can no longer see it resolve.
  • The block glyphs are not in the page’s typeface and come from the system one, wider: without squeezing them the decoding sentence spills out of the button.
  • Fake clicks from a script must not write to anyone’s clipboard: check e.isTrusted.

Libraries

Archivo (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 5.2 KB, and no libraries are needed.

Testing

Tested on 28 September 2026

  • Desktop: Done
  • Phone: Done
  • Reduced motion: Done
  • Safari on iPhone: Done

License

Use it in your own projects and in your clients’ projects. Do not resell it or redistribute it as is. GSAP stays under its own Standard license. Full license.

All animations