Skip to content
animorio
IT
All components

The code that wraps

Code, Text, TransitionsGSAP, CSS onlyAdvancedCompressed weight: Demo 12 KBGSAP 28 KBFreeDownload the zip

The code block of the docs of a small SDK for publishing posts, in the minimal style: hairline rules, greys and a single blue for the values. Pressing Wrap lines makes the long lines wrap live: the lines below make room, then the tail of every long line comes out from under the right edge, drops a row and slides into its indent, word after word, each on its own spring, lighter while it runs and fuller as it lands. The line numbers follow, and a hairline draws itself in the margin beside the rows of the same line. Copy puts the code on the clipboard and draws a tick.

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 code that wraps</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/recursive@5.3.0/full.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- a code block from the docs of a small publishing SDK, Slugline. Wrap lines bends the long lines live: the tail
         of each one comes out from under the right edge, drops a row and slides into its hanging indent, word after
         word, while the lines below make room and their numbers follow. Copy puts the code on the clipboard and
         draws a tick -->
    <section class="snippet" aria-label="Publish a post with the Slugline SDK" data-snippet data-autoclic>
      <header class="snippet__bar">
        <span class="file" data-file>publish.ts</span>
        <div class="tools">
          <button class="tool wrap" type="button" role="switch" aria-checked="false" data-wrap>
            <svg class="wrap__icon" viewBox="0 0 20 20" aria-hidden="true">
              <path class="wrap__edge" d="M15.5 3.5V16.5"/>
              <path class="wrap__line" d="M3 7L19 7C19 7 19 7 19 7L19 7" data-wrap-line/>
              <path class="wrap__head" d="M17 5L19 7L17 9" data-wrap-head/>
            </svg>
            <span class="tool__label" data-wrap-label>Wrap lines</span>
          </button>
          <button class="tool copy" type="button" data-copy>
            <svg class="copy__icon" viewBox="0 0 20 20" aria-hidden="true">
              <g data-sheets>
                <rect x="7" y="7" width="9.5" height="9.5" rx="2"/>
                <path d="M13 7V5.5a2 2 0 0 0-2-2H5.5a2 2 0 0 0-2 2V11a2 2 0 0 0 2 2H7"/>
              </g>
              <path class="copy__tick" pathLength="1" d="M4.5 10.4l3.6 3.6 7.6-7.8" data-tick/>
            </svg>
            <span class="copy__label"><span data-copy-label>Copy</span></span>
          </button>
        </div>
      </header>
      <div class="code" role="region" aria-label="publish.ts" tabindex="0" data-code>
        <div class="code__gutter" aria-hidden="true" data-gutter></div>
        <div class="code__view" data-view>
          <div class="code__sheet" aria-hidden="true" data-sheet></div>
        </div>
        <pre class="sr" data-text></pre>
      </div>
      <p class="sr" aria-live="polite" data-live></p>
    </section>
  </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.

28 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 code that wraps” from Animorio and fit it into my site

What it does

On a white page a nearly white code block with a grey hairline round it. At the top the file's name, publish.ts, in grey, and on the right two controls: Wrap lines, with a small icon of a line running past a margin, and Copy, with two sheets. Below, ten lines of code for Slugline (an invented SDK for publishing a blog's posts) in monospaced Recursive: the words of the language black and fuller, the names black, the signs and the comment in grey (the comment in italics), the texts and the values in blue. On the left the line numbers, small and grey. Three lines are too long and end under a fade on the right edge; the code scrolls sideways. Pressing Wrap lines the icon bends (the line turns back before the margin) and goes blue, the label takes weight, and the lines wrap: first the lines below go down to make room, one after the other; at the same time the tail of every long line drops a row, then slides left to its indent (the line's own plus two spaces), the first word in front and the others a few thousandths of a second behind each other, like a little train that stretches and closes up. While they run the words get lighter on the weight axis; as they land they go a pixel too far, come back and take weight for a moment. The numbers stay on the first row of each line, the block grows, and in the margin a grey hairline draws itself down beside the rows that wrapped. Pressing it again the tails go back: first they slide right, the head in front, then they rise; then the lines below come back up and the block gets shorter. Copy puts the code on the clipboard: the sheets shrink and vanish, a blue tick draws itself and the label rolls to Copied; after a while Copy comes back.

How it works

The code on a grid

Each line of the file is cut into words and spaces by a regex (comments, texts in quotes or backticks, figures, names, signs); a name is a word of the language if it is in a list, a function if a bracket follows, a property name if a dot comes before. Spaces do not become elements: they only count as columns. Recursive with its MONO axis at 1 is monospaced and keeps its width across weights and slants, so a single measure (60 zeros, getBoundingClientRect) gives the width of a character, and every word knows where it stands: column times width, row times height. The words are absolutely positioned spans, moved with a transform.

Wrapping like an editor

With Wrap lines on, a line longer than the columns of the box breaks at the latest possible place: after a space in the second half of the row, else after a comma or an opening bracket, else before a dot; spaces at the start of the new row are skipped. The rows after the first start at the line's indent plus HANG spaces (never past half the row). Off, each line stays on one row, the sheet is as wide as the longest line and the box scrolls sideways, with a fade on the edge (a mask-image with two animated @property values) saying there is more code.

FLIP on springs

On a change every word keeps where it is and is given its new place, plus two moments: when it starts going down and when it starts sliding. A damped spring for each axis (SPRING: stiffness and damping, 0.86 means a pixel too far and back) takes it there, in steps of at most 1/120 of a second and never more than 1/30 a frame. Pressing again halfway, the springs change their goal from where they are, with no jump; a box that changes width gives new goals too, and the words follow.

The choreography

Wrapping, every line starts ROW_STAGGER after the one above, so the ones below make room first; the tail of a long line goes down with its line and a little later, LEAD seconds on, starts sliding left: the path turns the corner. In the tail the head word starts first and the others follow WORD_STAGGER apart. Going back the order is reversed: the tails slide right (the rightmost word in front), then rise, and only then the lines below come back up and the block gets shorter. The line numbers have their own spring and follow the first row of each line.

The weight that runs and lands

Every frame a word's speed takes up to THIN off its weight (Recursive's wght axis); when a travelling word gets within a pixel of its goal, for 0.42 seconds it takes up to LAND extra weight, on a sine arc. The font stays as wide, so nothing moves; font-variation-settings is written only when it changes. When nothing moves the loop stops.

Copy, the keys and the preview

Copy writes to the clipboard with navigator.clipboard, or else with a hidden field and execCommand, or else selects the text; the confirmation goes through aria-live, and the real code sits in a pre that screen readers read. Wrap lines is a button with role=switch and aria-checked, and also answers Alt+Z as editors do. A click from a script on the block (the library's preview) plays the whole round in a timeline: it wraps, copies and goes back as it was.

Values to change

NameFileDefaultWhat it changes
CODEscript.js["// Publish a draft, then refresh every page that lists it", "import { createClient } from '@slugline/sdk';", …]The code, one line of the file for each entry. Tabs become two spaces.
START_WRAPPEDscript.jsfalseWhether the long lines start wrapped.
HANGscript.js2How many spaces further in the rows of a wrapped line start.
SPRINGscript.js[150, 0.86]The spring every word moves on: how stiff and how damped (1 = no overshoot at all).
ROW_STAGGERscript.js0.014The seconds between a line and the one below it.
WORD_STAGGERscript.js0.01The seconds between one word and the next in the same tail.
LEADscript.js0.2How much earlier the drop starts than the slide: the bigger, the sharper the corner of the path.
THINscript.js150How much weight a word loses while it runs, on the wght axis.
LANDscript.js170How much weight a word takes for a moment as it lands.
COPIEDscript.js1.6The seconds Copied stays before going back to Copy.

Try them in the Playground

When to use it

For the documentation of an SDK, an API or a command-line tool, a product's install guide, the technical blog of a studio or a developer: wherever there are code samples with long lines to be read on a phone.

On phones

Under 480 pixels the code goes down to 12 pixels and the margin of the numbers narrows, so about 40 characters fit on a row: off, the code scrolls sideways under the finger; on, the lines wrap even two or three times, with the same springs and the same choreography. Under 360 pixels the file's name hides to leave room for the two controls. Taps do not wait for a double tap (touch-action manipulation), and nothing spills sideways.

With reduced motion

The words go straight to their new place and the code comes back with a fade; the hairline in the margin appears without drawing itself, the Wrap lines icon changes without bending, Copy's tick and label change without moving, and the entrance is a single fade.

Pitfalls

  • Tails that dropped and slid at once cut diagonally through the line below, which had not made room yet: now the tail goes down with its line (the lines below start a moment earlier) and slides LEAD seconds later, once the row is free.
  • With the drop after the line, the tail stayed still up there while its line went down and looked torn off: the tail's drop starts with its line's, and it moves as far as the line below, so they never touch.
  • The ResizeObserver on the box fired every frame while the block grew taller and put the final height back at once, which jumped: it only counts when the width changes.
  • Scrolled sideways halfway, the words jumped left when wrapping: the scroll goes back to zero and the same distance is taken off every word's position and goal, so it starts from where it was seen.
  • Before the font came the words all sat in the top corner: they stay hidden until document.fonts.load has brought Recursive and the grid is measured.
  • While the block wraps the sheet narrows, but the words still on the right would widen the scroll area and show the scroll: on, the box does not scroll (overflow hidden), off it does.

Libraries

GSAP 3.15.0, Recursive (font variabile) 5.3.0.

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