Skip to content
animorio
IT
All components

The button that asks twice

Buttons, Text, PhysicsGSAP, CSS onlyMediumCompressed weight: Demo 9.0 KBGSAP 28 KBFreeDownload the zip

The Delete project button of an app, in the minimal style, that deletes nothing on the first click: the label becomes Click again to delete and the pill stretches on a spring. The time to answer is the room that is left: over three seconds the pill closes in on the question, the letters condense on the width axis to always fit exactly, and a thin vermilion line under them burns down from both ends. If nobody answers, Delete project comes back; on the second click the pill folds down into its line, which becomes the rule under Project deleted, with Undo beside it.

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="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>The button that asks twice</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/archivo@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the frame keeps the room of the widest state, so the preview never has to move -->
    <div class="frame" data-frame>
      <!-- the pill: drawn by the script on every frame. After the first click it asks again, and the time to answer
           is the room it has: the pill closes in on the question, the letters condense on the width axis to stay
           inside, and a thin line under them burns down to nothing. Confirmed, the pill folds down into that line -->
      <span class="pill" aria-hidden="true" data-pill>
        <span class="label" data-rest>Delete project</span>
        <span class="label" data-ask>Click again to delete</span>
        <span class="fuse" data-fuse></span>
      </span>
      <button class="del" type="button" aria-label="Delete project" data-del data-autoclic></button>

      <!-- once it is done: a quiet note sitting on the line the pill became, and the way back -->
      <div class="done" data-done>
        <span class="done__clip" aria-hidden="true"><span class="done__text" data-done-text>Project deleted</span></span>
        <button class="undo" type="button" aria-label="Undo, restore the project" data-undo>
          <span class="undo__clip" aria-hidden="true">
            <svg class="undo__arrow" viewBox="0 0 16 16" data-arrow><path d="M5.6 3.8 2.6 6.8l3 3M2.6 6.8h7.1a3.4 3.4 0 0 1 0 6.8H7.2" pathLength="1" data-arrow-path/></svg>
            <span class="undo__word" data-undo-word>Undo</span>
          </span>
        </button>
      </div>
    </div>
    <p class="sr" role="status" aria-live="polite" data-said></p>
  </main>

  <script src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
  <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.

27 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 button that asks twice” from Animorio and fit it into my site

What it does

On a white page there is only an ink pill, 60 pixels high, reading Delete project in Archivo, light on dark. When the page opens the pill is born round and stretches on a spring while the letters rise one after the other. Under the pointer the label widens on the wdth axis and the pill swells by a few pixels. On the first click Delete project leaves upwards and from below Click again to delete rises letter by letter, its last word in vermilion; the pill stretches to hold it, goes one pixel past and comes back. Under the question a thin vermilion line is lit from the middle. From then on the time shows in two ways: the line burns down from both ends until it is gone, and the pill slowly closes in on the question, faster towards the end; the letters condense on the width axis (from 100 to 64) and take a little weight, so the sentence always fits the pill exactly, tighter and tenser. If no second click comes in three seconds the question drops out, Delete project falls back in from above and the pill takes a small breath on its spring. If it comes, the question falls into the line, the top edge of the pill comes down to the line and the pill becomes a thin rule; out of the rule rise Project deleted, in grey, and Undo with its arrow drawing itself. Undo does it all backwards: the words sink into the line, the rule opens up into the pill and Delete project is back.

How it works

The labels measured once

Archivo is a variable typeface with a width axis, from 62 to 125. At the start, once the typeface has arrived (document.fonts.load for both weights), the script measures Delete project and Click again to delete on the real element with getBoundingClientRect, at six widths of the axis and two weights. The width at any axis and weight comes from interpolating between the four nearest values: while the pill moves nothing is measured any more, it is only arithmetic.

Time is room

The first click starts a GSAP tween that carries one number, the time, from 0 to 1 over WINDOW seconds, with no curve. The time goes through a curve that starts slow and speeds up, and from it come the axis width of the question (from SQUEEZE[0] to SQUEEZE[1]) and its weight (from WEIGHT[0] to WEIGHT[1]); the pill is as long as the question at that axis plus the margin, so it closes in on the sentence and the sentence condenses to fit. The line is as long as the question at that instant times the time left, centred: it burns down from both ends.

Three springs for the pill

The pill has no fixed shape: it is three numbers, its length, its top edge and its bottom edge, each on a damped spring integrated in half steps, with the time step capped at 1/30 of a second. The length follows SPRING, damped just under 1, and goes a pixel past when it stretches; the edges follow FOLD. When the springs come within three hundredths of a pixel they settle exactly on the target, so the pill at rest is always the same picture and the function on the GSAP ticker removes itself.

Folding into the line

The counting line sits 14 pixels under the centre, and that is where the pill folds: on the second click its edges go to 14 minus and plus 0.75 pixels and its length becomes that of the row Project deleted · Undo, measured with room for the hover. The labels inside the pill always stay at the centre of the frame whatever the top edge does (the script moves their top the other way), so the edge coming down covers them like a blind. Project deleted and Undo sit in two clips that end just above the rule, and rise out of it.

The double click and the keyboard

A second click closer than GUARD seconds to the first is a double click, and confirms nothing: without this check whoever double-clicks out of habit would delete the project. Enter twice does what the mouse does, Escape cancels the question. After the confirmation the focus goes to Undo, and after Undo back to the button. A hidden text with role=status says the question and the time, Deletion cancelled, Project deleted and Project restored.

The preview round

A click made by a script has detail equal to 0, but so do Enter and the space bar: to tell them apart the script notes the key pressed before the click. A script click with no key behind it starts a GSAP timeline with the whole round: the question left to run out, the question confirmed, Project deleted, Undo and Delete project again. Script clicks arriving during the round do nothing; a real click stops it and the button answers from there.

Values to change

NameFileDefaultWhat it changes
LABELscript.js'Delete project'The label at rest.
ASKscript.js'Click again to delete'The question after the first click; its last word takes the accent.
DONEscript.js'Project deleted'The note once it is done, over the rule.
UNDOscript.js'Undo'The word for going back.
WINDOWscript.js3The seconds there are to confirm: the line burns down and the pill closes in over this time.
GUARDscript.js0.35A second click closer than this to the first is a double click and does not confirm.
SQUEEZEscript.js[100, 64]The width axis of the question when it is asked and when time is up (Archivo goes from 62 to 125). The further apart, the more the pill closes in.
WEIGHTscript.js[560, 690]The weight of the labels, and the weight the question reaches as it tightens.
SPRINGscript.js[430, 0.78]The spring of the pill’s length: stiffness and damping (under 1 it goes a pixel past and comes back).
FOLDscript.js[300, 0.8]The spring of the edges, which folds the pill into the line and opens it up again.
STAGGERscript.js0.016The seconds between one letter and the next as a label comes in.
SWELLscript.js6The pixels the pill swells under the pointer.

Try them in the Playground

When to use it

For any action that must not happen by mistake but does not deserve a confirmation dialog: deleting a project, removing a member from a team, emptying the bin, revoking a key. The second click is in the same spot as the first, so confirming costs a single extra gesture; Undo stays for whoever regrets it anyway.

On phones

The pill is the same, 60 high, and the question at full width fits easily in 390 pixels. Without a pointer there is no hover swell: a tap squashes the pill and releases it on the spring; two taps confirm, with the same animations as on a computer, and Undo has a touch area larger than the word.

With reduced motion

No letters rising or falling, no springs and no pill closing in: the labels fade into each other, the pill takes the right size at once and the question stays at full width. The time still shows in the vermilion line burning down, because it is information and not decoration. Once confirmed, the pill is a rule at once and the note fades in.

Pitfalls

  • The preview click and Enter on the keyboard both have detail equal to 0: with detail alone, keyboard users started the demo round instead of confirming. The script notes the key pressed in keydown and uses it up in the click.
  • With the old and new words moving together, for an instant Delete project and Click again sat on top of each other inside the pill. The leaving letters go in two tenths of a second with a tiny stagger, and the new ones start after.
  • On confirming, the fading question still showed while the pill’s edge came down over it, and the rule looked dirty: on confirmation the question leaves in 15 hundredths of a second and the pill folds just after.
  • Undo sat under the rule instead of beside Project deleted: the row’s white-space: pre kept the line break of the HTML inside the button. The pre is only on the words.
  • Undo widening under the pointer went past the end of the rule: the row is measured at rest with eight pixels more and Undo is pinned to its right end (justify-content: space-between), so it widens to the left.
  • Moving the focus to Undo as soon as the button disappeared drew the ring around nothing, before the word arrived: the focus moves to Undo only once the note has risen, and meanwhile the ring of the folding button is transparent.
  • Fontsource’s index.css only loads the weight axis: without wdth.css the question does not condense and the pill, which closes in anyway, would cut it.

Libraries

GSAP 3.15.0, Archivo (font variabile, con gli assi del peso e della larghezza) 5.3.0.

Compressed with gzip, the three demo files weigh 9.0 KB; the libraries: GSAP 28 KB.

Testing

Tested on 16 October 2026

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

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 components