Skip to content
animorio
IT
All components

The address that becomes a label

Forms, Transitions, SVGGSAP, CSS onlyAdvancedCompressed weight: Demo 12 KBGSAP 28 KBFreeDownload the zip

The delivery address in a shop’s checkout: name, street, town, a postcode checked against the shape of the chosen country, and country. With everything right the card itself becomes the shipping label: the edges of the fields fade, the card closes and takes the black rules of a label, the words fly from the fields to their lines changing size and weight, the route is typed into the black box and a print head crosses the label printing a real Code 128 barcode.

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 address that becomes a label</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/geist@5.3.0/index.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/geist-mono@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- the delivery address of a checkout: with every field right, the card turns into the shipping label, the words
         fly from the fields to their lines and a real Code 128 barcode is printed under them. Sending is pretended:
         see post() in the script -->
    <div class="ship" data-ship data-autoclic>
      <div class="sheet" data-sheet>
        <form class="addr" action="#" novalidate aria-labelledby="ship-title" data-form>
          <h2 class="addr__title" id="ship-title">Delivery address</h2>

          <div class="field" data-field="name">
            <label class="field__label" for="ship-name">Full name</label>
            <div class="field__box" data-box>
              <input class="field__input" id="ship-name" name="name" type="text" autocomplete="shipping name" autocapitalize="words" spellcheck="false" enterkeyhint="next" maxlength="40" aria-describedby="ship-name-err" aria-invalid="false" data-input>
              <span class="field__caret" aria-hidden="true" data-caret></span>
            </div>
            <p class="field__err" id="ship-name-err" data-err></p>
          </div>

          <div class="field" data-field="street">
            <label class="field__label" for="ship-street">Street and number</label>
            <div class="field__box" data-box>
              <input class="field__input" id="ship-street" name="street" type="text" autocomplete="shipping address-line1" spellcheck="false" enterkeyhint="next" maxlength="48" aria-describedby="ship-street-err" aria-invalid="false" data-input>
              <span class="field__caret" aria-hidden="true" data-caret></span>
            </div>
            <p class="field__err" id="ship-street-err" data-err></p>
          </div>

          <div class="addr__row">
            <div class="field" data-field="city">
              <label class="field__label" for="ship-city">Town or city</label>
              <div class="field__box" data-box>
                <input class="field__input" id="ship-city" name="city" type="text" autocomplete="shipping address-level2" spellcheck="false" enterkeyhint="next" maxlength="32" aria-describedby="ship-city-err" aria-invalid="false" data-input>
                <span class="field__caret" aria-hidden="true" data-caret></span>
              </div>
              <p class="field__err" id="ship-city-err" data-err></p>
            </div>
            <div class="field field--post" data-field="post">
              <label class="field__label" for="ship-post">Postcode</label>
              <div class="field__box" data-box>
                <input class="field__input" id="ship-post" name="post" type="text" autocomplete="shipping postal-code" autocapitalize="characters" autocorrect="off" spellcheck="false" enterkeyhint="next" maxlength="10" aria-describedby="ship-post-err" aria-invalid="false" data-input>
                <span class="field__caret" aria-hidden="true" data-caret></span>
              </div>
              <p class="field__err" id="ship-post-err" data-err></p>
            </div>
          </div>

          <div class="field field--country" data-field="country">
            <label class="field__label" for="ship-country">Country</label>
            <div class="field__box" data-box>
              <select class="field__input field__select" id="ship-country" name="country" autocomplete="shipping country" data-country>
                <option value="GB">United Kingdom</option>
                <option value="IE">Ireland</option>
                <option value="IT">Italy</option>
                <option value="DE">Germany</option>
                <option value="FR">France</option>
                <option value="US">United States</option>
              </select>
              <svg class="field__chev" viewBox="0 0 12 12" aria-hidden="true"><path d="M2.5 4.5 6 8l3.5-3.5"/></svg>
            </div>
          </div>

          <div class="addr__foot">
            <button class="send" type="submit" data-send>
              <span class="send__bg" aria-hidden="true"></span>
              <span class="send__label">Ship here</span>
            </button>
          </div>
        </form>

        <!-- the label the card turns into: drawn for the eye, the form above stays the real thing -->
        <div class="label" aria-hidden="true" data-label>
          <div class="label__from">
            <span class="label__cap">From</span>
            <span>Larkspur Goods · Unit 4, Mill Lane · Bristol</span>
          </div>
          <div class="label__main">
            <div class="label__to">
              <span class="label__cap">Ship to</span>
              <span class="label__line label__line--name" data-to="name"></span>
              <span class="label__line" data-to="street"></span>
              <span class="label__line label__line--city"><span data-to="city"></span> <span data-to="post"></span></span>
              <span class="label__line label__line--country" data-to="country"></span>
            </div>
            <div class="label__sort" data-sort>
              <span class="label__cap label__cap--inv">Route</span>
              <span class="label__route" data-route></span>
            </div>
          </div>
          <div class="label__code">
            <svg class="label__bars" data-bars preserveAspectRatio="none"></svg>
            <span class="label__head" data-head></span>
            <span class="label__track" data-track></span>
          </div>
        </div>
      </div>

      <div class="ship__after" data-after>
        <p class="after__line"><span data-after-text>Arrives Thursday, tracked all the way.</span> <button class="after__change" type="button" data-change>Change</button></p>
      </div>
      <p class="sr" role="status" aria-live="polite" data-live></p>
    </div>
  </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.

33 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 address that becomes a label” from Animorio and fit it into my site

What it does

On a white page a white card with round corners, Delivery address, with the fields Full name, Street and number, Town or city and Postcode side by side, and Country with United Kingdom; under it the black Ship here pill. The field in use takes a black edge; in the empty postcode the example of the chosen country shows in grey (SW1A 2HQ, 20121) and what you write turns to capitals, taking the right space when you leave it. A field left with something wrong, or pressing the button, takes an orange edge, gives a small jolt sideways and under it opens the line that says what fits (UK postcodes look like SW1A 2HQ.); once it is right the line closes again. With everything right the button says Printing the label, the names and edges of the fields fade leaving only the written words hanging, the card closes to the height of a label with almost square corners, a black edge and a shadow; the black rules appear, From Larkspur Goods at the top, Ship to and the black Route box. The words fly one after the other from the fields to the lines of the label, the name larger and heavier, town, postcode and country in capitals; SW1A 2HQ is typed into the box; then a thin orange line runs underneath from left to right and the barcode is printed behind it, while the tracking number is typed below, LG 182 626 096 GB. Under the label opens Arrives Thursday, tracked all the way. and Change: the words fly back into their fields and the card opens again.

How it works

Real fields and every country’s postcode

Every field has its label, shipping autocomplete, aria-invalid and aria-describedby to the line under it. The postcode is checked against the shape of the chosen country (United Kingdom, Ireland, Italy, Germany, France, United States), turns to capitals as you type without moving the caret and takes the right space when you leave it. The error line has a height of its own that opens and closes on a tween, so the fields below move down and up softly.

The words in flight (FLIP)

On sending, a copy of each field’s words is laid on it in the same type and place, and the real words turn transparent. The label’s lines, already filled but invisible, give the destination: x, the middle of the line, the size and the weight. Every copy flies with x, y, scale and fontWeight on one timeline, staggered by STAGGER; halfway the lines in capitals take their capitals, and on arrival the copy gives way to the real line.

The card that becomes a label

At the same time the names and edges of the fields fade, the height of the card goes from the form’s to the label’s, the corners from 20 to 6 pixels, the edge from grey to black with a shadow: the card is not replaced, it changes job. Going back plays the flight in reverse, starting from the lowest line, so the words going down never cross the ones waiting above.

A real Code 128

The tracking number comes from the address (an FNV hash of the lines, nine digits, between the sender’s two letters and the country). The barcode is Code 128 set B: the start, every character with its six widths of bar and space, the check character (weighted sum modulo 103) and the stop, with ten quiet modules either side. The bars are SVG rectangles on a viewBox in modules, a whole number of pixels wide (MODULE) and with shape-rendering crispEdges, so they scan.

The print head

Printing is an object going from 0 to 1 on GSAP in PRINT seconds, with no ease: at every step the barcode’s clip-path opens from the left up to that point and the print head’s orange line sits there, while the number is typed character by character at the head’s pace. The start and end of the postcode are typed into the Route box.

Values to change

NameFileDefaultWhat it changes
FLIGHTscript.js0.8The seconds the words take to fly from the fields to the label.
STAGGERscript.js0.07The seconds between one word taking off and the next.
PRINTscript.js0.9The seconds the print head takes to cross the barcode.
MODULEscript.js2The width of the thinnest bar, in pixels (it drops if the label is narrow).
SENDERscript.jsLGThe two letters the tracking number starts with.
WAITscript.js0.5How many seconds the pretended saving of the address takes.

Try them in the Playground

When to use it

In the delivery step of a checkout, in a shop that ships: the label tells the buyer, better than a summary, that the address is right and where the parcel will go. It also suits the address page of an account or a shipping app.

On phones

The card takes the width of the screen (358 pixels on a 390 phone), town and postcode stay side by side, the fields are 46 pixels tall and the type 16, so Safari does not zoom. The postcode opens the keyboard in capitals, Country opens the phone’s picker. On the label the route box narrows and the barcode keeps whole modules, centred.

With reduced motion

No flights, no jolts and no print head: the error lines fade in, and on sending the form fades away and the label appears already printed, with the whole barcode and number. Change does the same in reverse.

Pitfalls

  • In the row of town and postcode the second field took the margin of a field following another field and sat 14 pixels lower than the first: inside the row that margin is now zero.
  • Going back, the words all left together from lines 17 pixels apart and overlapped in flight. Now the lowest line leaves first, with more time between departures: they go down to distant fields without crossing.
  • A barcode stretched to any width has half-pixel bars that do not scan. Now every module is a whole number of pixels, at most MODULE, and the code is centred.

Libraries

GSAP 3.15.0, Geist (font variabile) 5.3.0, Geist Mono (font variabile) 5.3.0.

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

Testing

Tested on 19 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