Skip to content
animorio
IT
All components

The dialog drawn on a thread

Dialogs, Transitions, SVGGSAPAdvancedCompressed weight: Demo 10 KBGSAP 28 KBFreeDownload the zip

A confirmation dialog in the minimal style, drawn by a single thread: the outline of the Archive project button is the end of a thin line that, when pressed, leaves the button with a vermilion tip, climbs and runs all the way round the window, while the button unwinds to feed it; when the loop closes the window settles by a pixel and the words light up one after another. Cancel winds the thread back into the button, Archive closes the window into a line that tightens back into the button, now Archived.

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 dialog drawn on a thread</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/mona-sans@5.3.0/wdth.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage" data-stage>
    <!-- the dialog drawn on a thread: the button's outline is the end of one thin thread. Pressed, the thread runs out
         of the button and draws the window round; Cancel winds it back into the button, Archive closes the window into
         a single line that tightens into the button, now done -->
    <span class="slot" data-slot data-autoclic>
      <button class="opener" type="button" aria-haspopup="dialog" aria-controls="archive-dialog" data-open>
        <span class="opener__fill" aria-hidden="true"></span>
        <span class="opener__face">
          <span class="opener__label opener__label--size" aria-hidden="true" data-size></span>
          <span class="opener__label" data-rest>Archive project</span>
          <span class="opener__label opener__label--done" aria-hidden="true" data-done>
            <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.6l4.3 4.3L19 7.4" data-tick/></svg>
            <span>Archived</span>
          </span>
        </span>
      </button>
      <span class="slot__under">
        <button class="undo" type="button" hidden data-undo>Undo</button>
      </span>
    </span>
  </main>

  <div class="veil" data-veil></div>

  <div class="layer">
    <div class="box" id="archive-dialog" role="dialog" aria-modal="true" aria-labelledby="archive-title" aria-describedby="archive-text" data-box>
      <div class="box__paper" data-paper></div>
      <div class="box__body">
        <h2 class="box__title" id="archive-title" data-title>Archive this project?</h2>
        <p class="box__text" id="archive-text" data-part><strong>Brightwell rebrand</strong> leaves your workspace with its files and comments. You can restore it from Archive at any time.</p>
        <div class="box__rule" aria-hidden="true" data-rule></div>
        <div class="box__actions">
          <button class="act act--ghost" type="button" data-part data-cancel>Cancel</button>
          <button class="act act--ink" type="button" data-part data-confirm>Archive</button>
        </div>
      </div>
    </div>
  </div>

  <svg class="thread" aria-hidden="true" data-thread>
    <path class="thread__ink" d="" data-ink/>
    <path class="thread__tip" d="" data-tip/>
  </svg>

  <p class="sr" aria-live="polite" data-live></p>

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

23 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 dialog drawn on a thread” from Animorio and fit it into my site

What it does

On a white background there is only a pill button, Archive project, outlined by a thin black thread. As the page comes in, the thread draws the button by running round it, led by a vermilion tip. With the mouse over it the fill swells, the words widen and the end of the thread sticks out a few pixels from the top of the button, in vermilion. Pressed, that end runs up and draws the whole outline of the window, while the button’s outline unwinds from the top and its words narrow and fade: the two ends of the thread meet at the same instant, the window breathes by a pixel and settles, the white paper comes in with its shadow and inside, one after another, light up the letters of Archive this project? (narrow and light, then wide and full), the sentence about Brightwell rebrand, the thin rule drawn from the left and the Cancel and Archive buttons. Cancel, Esc or a click outside wind the thread back: the button’s outline comes together again while the window’s goes back into the button. Archive closes the window into a single line through the middle of the button, the line tightens to its width and swells to its height, and the button says Archived with a vermilion tick that draws itself; under it appears Undo, which puts it back as it was.

How it works

One thread, three pieces

The thread is a single SVG path in a layer as big as the screen, rebuilt at every frame in pixels: the button’s outline (a pill run clockwise from the top), the stretch that climbs from the top of the button to the top of the window (straight, or an S with upright ends if they are not in line) and the window’s outline (a rounded rectangle that starts and ends at the same point). The lengths are worked out exactly: sides plus quarter circles, 2πr for the corners.

Two ends that run

The part that shows is a single stroke-dasharray dash, moved with stroke-dashoffset: the front end goes from the end of the button to the end of the window (u), the back end from the start of the button to the start of the window (v), leaving a moment later. Both runs are fractions worked out again on that frame’s lengths, so a resize half way through the drawing moves nothing. The vermilion tip is the same path with a TIP pixel dash in front of the end.

The seam

Where the loop closes, two butt-ended strokes leave a hair of gap: the first loop starts a pixel and a half before its point, the last one ends a pixel and a half past it, and the two overlap. As soon as it closes, the outline grows by 1.4 pixels and comes back with elastic.out: the settling.

The words

The title is split into letters (the words never break): each comes from wdth 72 and wght 300 to wdth 100 and wght 640, with back.out for a pixel of overshoot, one every LETTERS seconds. Then the sentence, the thin rule (scaleX from the left) and the two buttons, rising by 8 pixels with back.out. The button’s words use a --w variable inside font-variation-settings, and an unseen copy of the words, as wide as under the mouse, keeps the button always the same size: the outline never moves.

Closing into a line

With Archive the window’s outline changes shape instead of unwinding: its top and bottom sides meet in the line through the middle of the button (power3.in), the line holds for an instant, tightens to the button’s width and opens to its height with back.out(1.1), a pixel past it. When it matches the button, the same drawing passes to the thread’s first piece without the change showing.

A real dialog

The window has role dialog and aria-modal, the button aria-haspopup and aria-expanded. Opening, the page behind becomes inert and the focus goes to Cancel; Tab goes round Cancel and Archive, Esc and a click outside cancel. As soon as it closes the window stops answering and the focus goes back to the button (or to Undo), even while the thread is still moving. An aria-live region says archived and back. A click from a script (the library’s preview) makes a whole round in a GSAP timeline: open, cancel, open again, archive, Undo.

Values to change

NameFileDefaultWhat it changes
DRAWscript.js1.05The seconds the thread takes to run out of the button and draw the window.
REWINDscript.js0.7The seconds the thread takes to wind back into the button on Cancel.
FOLDscript.js0.8The seconds the window takes to close into a line and become the done button.
TIPscript.js30The length of the coloured tip that leads the thread, in pixels.
PEEKscript.js8How far the end of the thread sticks out of the button on hover, in pixels.
DIMscript.js0.05How much the page darkens behind the window, from 0 to 1.
LETTERSscript.js0.018The seconds between one letter of the title and the next.
RADIUSscript.js20The corner radius of the window, in pixels.

Try them in the Playground

When to use it

For confirming an action that can still be undone: archiving a project, moving an order, leaving a workspace, cancelling a booking. It suits apps and dashboards made of thin lines, and the button that draws itself works as an invitation too.

On phones

The window takes the whole width less 16 pixels a side, and the thread takes the same path. Under the finger the button’s fill swells and the end of the thread sticks out, as with the mouse; a tap outside cancels.

With reduced motion

The thread does not run: the button’s outline and words fade, then the window appears in their place already drawn, with the words still; closing does the opposite, and Archived with its tick fades in. Nothing moves and the two never show together.

Pitfalls

  • Two butt-ended strokes touching at the same point leave a white hair in the closed outline: the first loop starts a pixel and a half early and the last one ends a pixel and a half late, so they overlap.
  • The button’s words narrow with the wdth axis as the thread leaves, and a button as wide as its words got narrower: the thread’s outline moved and Archived came out narrower. An unseen copy of the words, as wide as under the mouse, keeps the button always the same size.
  • Closing with Esc, the window stayed inert and the focus stayed on Archive for the whole rewinding: an Enter straight after archived. Now the window lets go as soon as it closes and the focus goes straight back to the button.
  • With reduced motion the window appeared while the button’s words faded and the two showed on top of each other: first the button goes, then the window comes.

Libraries

GSAP 3.15.0, Mona Sans (font variabile, assi wght e wdth) 5.3.0.

Compressed with gzip, the three demo files weigh 10 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