Skip to content
animorio
IT
Back to the library

Letters within reach

Text, Hover, ScrollGSAPMediumCompressed weight: Demo 6.1 KBGSAP + ScrollTrigger + CustomEase 49 KBFreeDownload the zip

Scroll inside the preview

The last section of the page slides away and uncovers the footer, still underneath as if it had always been there, while the letters of the motto rise one after the other out of their line. Once it is open, the letter under the pointer reaches for it, up or down, its two neighbours follow half as far, and all three take a colour and lean towards the hand.

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>Letters within reach</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/familjen-grotesk@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <!-- the last section of the page: it slides away and uncovers the footer -->
  <main class="closing">
    <div class="closing__inner">
      <p class="closing__brand">Common Room</p>
      <h1 class="closing__title">Long tables, loud dinners, and chairs that can take it.</h1>
      <p class="closing__line">Oak tables for ten, benches for twelve, chairs that stack when the guests go home.</p>
      <a class="closing__button" href="#">Find your table</a>
    </div>
    <p class="closing__hint" data-hint>Scroll to the end</p>
  </main>

  <!-- the footer waits under the page: the mask makes room for it, the footer itself stays put while the page moves -->
  <div class="mask" data-mask>
    <footer class="foot" data-foot>
      <div class="foot__top">
        <p class="foot__lead">Dinner is better with more people at the table.</p>
        <nav class="foot__cols" aria-label="Footer">
          <div>
            <p class="foot__head">Shop</p>
            <a href="#">Tables</a>
            <a href="#">Benches</a>
            <a href="#">Chairs</a>
          </div>
          <div>
            <p class="foot__head">Visit</p>
            <a href="#">The workshop</a>
            <a href="#">Opening hours</a>
          </div>
          <div>
            <p class="foot__head">Say hello</p>
            <a href="#">Write to us</a>
            <a href="#">Instagram</a>
          </div>
        </nav>
      </div>

      <!-- the motto: each letter is its own span, so the pointer can reach it -->
      <p class="slogan" data-slogan aria-label="good company">good company</p>

      <div class="foot__bottom">
        <p>Common Room, tables built in small batches</p>
        <button class="foot__top-link" type="button" data-top>Back to top</button>
      </div>
    </footer>
  </div>

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

36 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 “Letters within reach” from Animorio and fit it into my site

What it does

The end of the page of an invented table and chair brand, Common Room. The closing section is dark green, with rounded bottom corners: scrolling, it slides up and the sand-coloured footer appears underneath, not moving. At the top a sentence and three columns of links, at the bottom the motto “good company” as wide as the screen. As the footer opens, the letters of the motto rise out of their line one after the other, each a little after the one before, and finish together with the footer. Once it is open, the letter under the pointer goes after it: if the pointer is above the line it rises a quarter of the distance, if it is below it sinks; its two neighbours move half as much. The three letters take a colour each (terracotta, olive, plum, saffron, teal, in turn) and lean towards the pointer. Moving on, the colour passes to the new letters and the ones left behind go back to black and into line in under two tenths of a second. If nobody moves the pointer for two and a half seconds, an invisible hand runs back and forth along the motto, rising and dipping. The “Back to top” button goes back up.

How it works

The footer that does not move

The footer sits in a mask as tall as itself, under the closing section, which has the higher z-index. A single ScrollTrigger tween moves it from yPercent −100 to 0 while the mask travels from the bottom of the screen to its own lower edge, scrubbed and with no curve: the footer comes down exactly as far as the page goes up, so on screen it stays still and looks uncovered rather than arriving.

The letters rising

The motto is split into letters, each made of two spans. The outer one waits RISE heights further down, hidden by the line’s overflow: hidden, and rises with a paused timeline that follows the footer’s progress: STAGGER seconds between one letter and the next, on a CustomEase curve that lands softly. At 95% of the opening the line opens up (overflow: visible) and the letters may leave it; it closes again only under 88%, so it does not flicker at the edge.

Which letter

The letter under the pointer is the one it is over, measured with offsetLeft and offsetWidth, which ignore transforms; between two letters it is the nearer one. The space does not count: the neighbours of “d” are “o” and “c”. Before anything else the pointer must be inside the visible part of the footer, or the letters go back into place.

How far it reaches

The vertical distance between the pointer and the line, times REACH (a quarter for the letter under it, an eighth for its neighbours), says how far the inner span moves. The lean comes from the horizontal distance to the letter’s centre, up to LEAN degrees, in the direction that turns it towards the hand. The colour is fixed per letter, from the COLORS list. Every change is a PAINT-second tween with overwrite auto, and only when the value really changes.

The invisible hand

After GHOST seconds with no pointer on the footer, a GSAP ticker moves a fake pointer: back and forth across the motto in 5.2 seconds, up and down on a faster sine, always inside the footer. One real move is enough to switch it off.

Values to change

NameFileDefaultWhat it changes
REACHscript.js0.25, 0.125How far the letter under the pointer follows it, and how far its two neighbours, as a part of the distance from the line.
LEANscript.js6How far the reached letters lean towards the pointer, in degrees.
PAINTscript.js0.18The seconds a letter takes to change colour and height.
SETTLEscript.js0.28The seconds they take to settle back into line when the footer closes.
RISEscript.js1.3How far below the line the letters wait before the footer opens, in letter heights.
STAGGERscript.js0.04The delay between one letter and the next as they rise, in seconds of a 0.6-second rise.
GHOSTscript.js2.5The seconds without a pointer before the invisible hand plays with the letters. At 0 it never comes.
COLORSscript.js#d9643a, #7d8b4a, #9b6aa8, #e8a427, #3e8e8cThe letters’ colours, one per letter, in turn.
--sandstyle.css#e8dcc6The footer’s colour.
--nightstyle.css#1f2a22The colour of the section that slides away.

Try them in the Playground

When to use it

For the footer of any site with a name or a short motto to show big: two words, a dozen letters at most. With a long phrase the letters become small and the game cannot be read. It wants a solid typeface with separate letters; a joined script would break at its connections.

On phones

The footer is uncovered the same way, scrolling with a finger. Touching the motto and dragging, the letters follow the finger; over the motto the page does not scroll, above it it does. When the finger lifts the letters go back into line and, after two and a half seconds, the invisible hand comes.

With reduced motion

The footer simply sits under the page and the letters are already in line. Under the pointer they take their colour but neither move nor lean, and there is no invisible hand.

Pitfalls

  • The CSS translate and rotate properties, driven by variables, looked like the way to keep the rise apart from the pointer game. But GSAP, as soon as it takes an element, reads those properties, folds them into its transform and sets them to none: the variables changed and the letters stayed put. It takes two elements, one for GSAP and one for the other movement.
  • The tails of g, p and y go below the line: with overflow: hidden and tight line spacing they were cut off, and with the footer open they landed on the credits line. The line has 0.22 em of room underneath.
  • With a single threshold, someone scrolling slowly around 95% would see the line open and close on every frame: that is why it opens at 95% and closes again at 88%.
  • On a phone Safari’s bars change the height while scrolling and send resize: rebuilding everything on every resize would make the footer jump. It is rebuilt only when the width changes.

Libraries

GSAP 3.15.0, ScrollTrigger (GSAP) 3.15.0, CustomEase (GSAP) 3.15.0, Familjen Grotesk (font variabile) 5.3.0.

Compressed with gzip, the three demo files weigh 6.1 KB; the libraries: GSAP + ScrollTrigger + CustomEase 49 KB.

Testing

Tested on 1 October 2026

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

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