Skip to content
animorio
IT
Back to the library

The letter curtain transition

Transitions, TextGSAP, CSS onlyMediumCompressed weight: Demo 6.8 KBGSAP 28 KBFreeDownload the zip

A minimal click page change for a theatre: the new page's title falls over the page as a curtain of dark slats, one per letter, each with its letter standing tall and narrow on it. The slats hit the floor and bounce, then the letters let go, shrink and widen until they are the title.

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 letter curtain transition</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/roboto-flex@5.3.0/full.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage" data-stage>
    <!-- the first page, light -->
    <section class="page page--a is-on" data-page="a" aria-label="Tonight">
      <header class="bar">
        <span class="brand">Velde Theatre</span>
        <nav class="nav" aria-label="Pages">
          <a href="#tonight" aria-current="page">Tonight</a>
          <a href="#season" data-go="b">Season</a>
        </nav>
      </header>
      <div class="hero hero--a" data-hero>
        <p class="kicker">Tonight at eight</p>
        <h1 class="big">The Glass Hour</h1>
        <a class="go go--next" href="#season" data-go="b" data-autoclic>
          <span>See the season</span>
          <svg class="go__arrow" viewBox="0 0 22 12" aria-hidden="true"><path d="M1 6h19M15 1.5 20 6l-5 4.5"/></svg>
        </a>
      </div>
      <footer class="foot">
        <span>Doors open at seven</span>
        <span>Box office</span>
      </footer>
    </section>

    <!-- the second page, dark: its title is the curtain -->
    <section class="page page--b" data-page="b" aria-label="Season" aria-hidden="true">
      <header class="bar">
        <span class="brand">Velde Theatre</span>
        <nav class="nav" aria-label="Pages">
          <a href="#tonight" data-go="a">Tonight</a>
          <a href="#season" aria-current="page">Season</a>
        </nav>
      </header>
      <div class="hero hero--b" data-hero>
        <h2 class="title" data-title>SEASON</h2>
        <ul class="shows">
          <li><span class="shows__name">The Glass Hour</span><span class="shows__when">November</span></li>
          <li><span class="shows__name">Salt and Iron</span><span class="shows__when">January</span></li>
          <li><span class="shows__name">A Quiet Year</span><span class="shows__when">March</span></li>
        </ul>
        <a class="go go--back" href="#tonight" data-go="a" data-autoclic>
          <svg class="go__arrow" viewBox="0 0 22 12" aria-hidden="true"><path d="M21 6H2M7 1.5 2 6l5 4.5"/></svg>
          <span>Back to tonight</span>
        </a>
      </div>
      <footer class="foot">
        <span>Doors open at seven</span>
        <span>Box office</span>
      </footer>
    </section>

    <!-- the curtain, built by the script: one slat for each letter of the title, and the letters -->
    <div class="curtain" data-curtain aria-hidden="true"></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.

34 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 letter curtain transition” from Animorio and fit it into my site

What it does

The site of a made-up theatre, Velde Theatre, in two pages: Tonight, light, with the evening's show in a very narrow face, and Season, black, with the title SEASON, three shows on thin rules with the months in orange and the link back. A click on See the season drops six dark slats as tall as the screen from above, one for each letter of SEASON, starting from the column that was clicked and then out to the sides; on each stands its light letter, very narrow, heavy and stretched upwards. Each slat falls faster and faster, hits the floor, bounces about ten pixels (a sliver of the light page shows underneath) and settles, and the letter squashes a hair. When the last one is down the whole screen is black like the new page: the slats go without it showing and the letters let go, flying to their places in the title as they shrink, widen and lose weight, each at its own pace; meanwhile the header, the shows and the link rise. A click on Back to tonight sends the rest of the page down, the title's letters grow tall and narrow again in their slats and the curtain goes up, one slat after another from the left, over the light page.

How it works

A slat and a letter for each letter

The title is split into letters and for each the script makes a slat (a div a sixth of the screen wide, black like the new page) and a separate letter in a layer above. Slat and letter fall with the same y tween, so they look like one thing; but the letter is not inside the slat, so it can fly off when the slats go.

The same letter at two settings

Each letter is one element for GSAP (where its centre is and how far it is scaled) with a span inside centred by the CSS with translate(−50%, −50%), whose font-stretch and font-weight come from the --w and --g variables. On the slat it is Roboto Flex at width 25 and weight 760, stretched upwards with scaleY; in the title width 100 and weight 560. The title's centres are measured on the boxes of the real letters (line-height 1, no letter spacing), so at the end the swap with the real title does not show.

Scaling from the right point

The letter's wrapper has its own width, and GSAP scales from its centre by default: the letter, drawn round the top left corner, slid away as it scaled. With transformOrigin at 0 0 it scales right from the letter's centre. And since the box of a line of type is not centred on the capitals, the position on the slat shifts the letter just enough for the capital's middle to be halfway down the screen even when stretched.

Fall, bounce and flight

Each slat falls in DROP seconds with power3.in, delayed STAGGER for every column away from the click; once down it comes back up BOUNCE pixels and falls again, and the letter squashes a little. Then the pages swap under the curtain, the slats are hidden and the letters fly with expo.inOut for the position, power3.inOut for the scale and power2.inOut for width and weight, each a little after the other.

Click, phone and reduced motion

Each change is one timeline with timeScale for SPEED, and a click that comes halfway waits in a queue. The measures are taken again on every resize: slats, letter size, stretch (at most STRETCH) and the title's positions. With reduced motion the pages swap with fades, one after the other.

Values to change

NameFileDefaultWhat it changes
DROPscript.js0.55The seconds a slat takes to fall.
STAGGERscript.js0.06The seconds between one slat and the next.
BOUNCEscript.js10How many pixels a slat bounces when it hits the floor: 0 = it just lands.
STRETCHscript.js2.2How far a curtain letter may be stretched upwards, at most (1 = not at all).
FLYscript.js0.95The seconds the letters take to fly into the title.
SPEEDscript.js1The speed of everything: 2 is twice as fast, 0.5 half as fast.

Try them in the Playground

When to use it

To enter a page with a short, strong title: the programme of a theatre, a cinema, a festival or a concert, the tickets page, a portfolio section, a venue's menu. It works best with titles of four to eight letters and a dark new page.

On phones

The same transition with the sizes adapted: the six slats are narrow, the letters smaller and stretched more, the title shrinks to the width of the screen and the shows go underneath.

With reduced motion

Nothing falls and nothing flies: the old page fades out and then the new one fades in.

Pitfalls

  • The letters scaled from the centre of their wrapper and not from the letter's: stretched on the slats they ended up at the top of the screen, cut off. The wrapper scales from 0 0, where the letter's centre is.
  • The box of a line of type has room for descenders underneath: centred halfway down the screen, a capital stretched twice stood too high. The letter is shifted by how far the capital's middle (measured with an empty element on the baseline) sits above the box's middle, times the stretch.
  • The little squash on landing was on the inner span: GSAP's inline transform wiped out the CSS translate(−50%, −50%) and the letter jumped. The squash goes on the wrapper, which already belongs to GSAP.
  • The rule above the list of shows appeared before the rows, a thin line between the flying letters: it now rises with the whole list.

Libraries

GSAP 3.15.0, Roboto Flex (font variabile) 5.3.0.

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