Skip to content
animorio
IT
All components

The alphabet index

Navigation, SVG, PhysicsCSS only, GSAPAdvancedCompressed weight: Demo 10 KBGSAP 28 KBFreeDownload the zip

A phone’s contacts with the A to Z index on the edge: dragging a finger along the index the nearby letters swell out from the edge along an arc, the letter under the finger becomes a big bubble beside it, and the list jumps to the people with that initial with a little kick for every letter passed. Letting go, the arc sinks back on a spring.

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 alphabet index</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fontsource-variable/figtree@5.3.0/index.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <main class="stage">
    <!-- a contacts list with the A to Z index on its edge. Drag along the index: the letters near the finger swell
         out from the edge along an arc, the letter under it grows into a big bubble beside it, and the list jumps to
         that letter with a small kick for every letter passed. Letting go, the arc sinks back on a spring -->
    <section class="book" aria-labelledby="book-title" data-book>
      <header class="book__head">
        <h1 class="book__title" id="book-title">Contacts</h1>
        <p class="book__sub">Everyone you know</p>
      </header>
      <div class="book__body">
        <div class="book__list" tabindex="0" aria-label="Contacts" data-list>
          <div class="book__shake" data-shake></div>
        </div>
        <!-- the index: the swollen track, the letters, and the bubble with the letter under the finger -->
        <div class="index" data-index>
          <svg class="index__track" aria-hidden="true" data-track><path data-track-path/></svg>
          <div class="index__letters" role="listbox" tabindex="0" aria-label="Jump to letter" aria-orientation="vertical" data-letters></div>
          <div class="bubble" aria-hidden="true" data-bubble>
            <svg class="bubble__shape" viewBox="-60 -60 120 120"><path class="bubble__shadow" data-bubble-path/><path class="bubble__shadow" data-bubble-path/><path data-bubble-path/></svg>
            <span class="bubble__letter" data-bubble-letter>A</span>
          </div>
        </div>
      </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.

19 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 alphabet index” from Animorio and fit it into my site

What it does

On a white background, a tall frame with very rounded corners, with no phone around it: Contacts at the top, below it the list of people grouped by the initial of their surname, with each letter’s header staying at the top as you scroll. Every person has their initials in a rounded square tinted with a colour taken from the name, the name with the surname in bold and a note below (mobile, Dentist, Climbing partner). On the right edge the index of the 26 letters, in blue; those with nobody (I, Q, U, X) are pale grey, and the letter at the top of the list is white on a small blue square. Pressing and dragging on the index, a pale band comes away from the edge and swells under the finger; the nearby letters move out from the edge along a bell-shaped arc and grow, the biggest under the finger, and the others shift up and down to make room. Beside it a blue bubble appears with a point towards the letter, the letter big in white. Passing over a letter, the list jumps to its people on a stiff spring, with a little kick in the finger’s direction for every letter passed; the bubble gives a jolt, the letter in it rolls and the header it lands on lights up for an instant. Empty letters are skipped: it goes to the nearest one with people. Letting go, the arc sinks back with a small bounce and the bubble withdraws. A click on a letter, the arrow keys or a letter typed on the keyboard go there with the same arc.

How it works

The finger in letters

The finger’s position on the index becomes a continuous number, in letters: 0 at the middle of A, 25 at the middle of Z. From it the chosen letter: the number rounded, but only once the finger has passed the border between two letters by HYSTERESIS (so it does not flicker on the border); if the letter is empty, the nearest one with people is taken, looking first in the finger’s direction.

The bell-shaped arc

Every letter has a distance d from the finger, in letters, and from it a bell g = exp(-d²/2σ²) with σ = SPREAD, multiplied by how swollen the arc is. The letter moves out from the edge by BULGE × g pixels and grows by 1 + (GROW − 1) × g; to make room, all of them shift by a hyperbolic tangent of the distance, up above the finger and down below. The pale band is an SVG path taken from the same bell.

Springs in and out

How swollen the arc is is a number on a lightly damped spring (SWELL): pressing it goes to 1, letting go it returns to 0 with a small bounce. The arc’s centre follows the finger on a stiff spring too, and the bubble follows the chosen letter. The springs are stiff, so every frame is split into steps of at most 1/120 of a second, with the frame capped at 1/30.

The list that clicks

At every new letter the list’s scrollTop runs to the letter’s header on a stiff, nearly critical spring. A second spring, lively and lightly damped, gets a push of speed in the finger’s direction, stronger if more than one letter was passed, and moves the list a few pixels with translateY: that is the kick. Where the phone allows it, navigator.vibrate gives a tap as well.

Keyboard and screen readers

The index is a listbox with aria-activedescendant, the letters are options and the empty ones have aria-disabled. The arrows go to the next or previous letter with people, Home and End to the ends, a typed letter goes there; the arc swells and sinks back a moment after the last key. A click on a letter (from the keyboard too) slides the finger there by itself, with GSAP.

Values to change

NameFileDefaultWhat it changes
BULGEscript.js44How far the letter under the finger moves out from the edge, in pixels.
SPREADscript.js2.3How many letters on each side the arc reaches: the width of the bell.
GROWscript.js1.9How much the letter under the finger grows.
SWELLscript.js[260, 0.5]The arc swelling and sinking back: the strength of the spring and its damping (1 = no bounce).
HYSTERESISscript.js0.18How far past the border between two letters the finger goes before the letter changes, in letters.
KICKscript.js5The list’s kick at every letter, in pixels.
HAPTICSscript.jstrueA tiny vibration at every letter, on phones that allow it.

Try them in the Playground

When to use it

For any long list in alphabetical order: contacts, customers, the artists of a music library, glossaries, lists of cities or countries, ingredients. With fewer than about forty entries the list scrolls easily on its own and the index is not needed.

On phones

It is made for the finger: the index has touch-action: none, so dragging along it does not scroll the page, and pointer capture keeps hold even if the finger slips off the edge. The frame is as wide as the screen minus 32 pixels and as tall as the screen allows; the list scrolls normally with the finger. Nothing spills over sideways.

With reduced motion

The letters neither move nor grow, the band does not swell and the list does not kick: the chosen letter lights up, the bubble fades in beside it and stays a moment, and the list goes straight to the letter.

Pitfalls

  • With a stiff spring on scrollTop and a slow frame (1/30 of a second) the step exploded: the list ended up at the bottom instead of the letter. Every frame is split into steps of 1/120 of a second and the springs stay stable even at a few frames a second.
  • Growing the letters under the finger made the neighbours overlap: each one shifts by a hyperbolic tangent of its distance from the finger, which opens the room near the finger and leaves the far ones still.
  • Without hysteresis, a finger resting on the border between two letters swapped them at every pixel and the list jumped back and forth: the letter only changes once the finger has passed the border by HYSTERESIS.
  • The spring’s bounce below zero pushed the letters out of the frame, to the right: the offset only uses the positive part, the bounce shows in the size.
  • A drop-shadow filter on the bubble and the band, which change every frame, made the frame rate collapse: the bubble’s shadow is two copies of the shape, lighter and offset.

Libraries

GSAP 3.15.0, Figtree (font variabile) 5.3.0.

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

Testing

Tested on 5 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 components