175

Typographic Index Gallery

£7
or the whole Vault, £99
Open in new tab

Archive index gallery: a numbered serif list of 50 objects scrolls past a reading line while one preview frame wipes to the active entry's image, upwards going down the list and downwards going up.

advanced
6 more details
clip-pathdirectional-transitioninterruptible-transitionsscrollspykeyboard-navigationprogressive-enhancement

Released

About this effect

A gallery that reads like a museum catalogue. Fifty entries sit in a numbered typographic index, each with a serif title, its maker and material, and a year, and the list scrolls inside its own column. Beside it, one fixed frame shows the active entry's image, with the entry number echoed large beneath it as 23 / 50.

Read the full effect overview

Hover a title and it takes the frame. Scroll the list instead, with a wheel, a trackpad, a finger or the keyboard, and whichever row crosses the fixed reading line becomes active, so the gallery works the same on a phone with no hover at all. Each change wipes the frame in the direction of travel with a clip-path reveal, the incoming image settling from a slight scale, while the active row indents and brightens and the rest of the index dims back.

Typographic Index Gallery - GSAP animation effect preview

What's included

10 items
  • Direction-aware clip-path wipe: moving down the list reveals the next image upwards, moving up reveals it downwards, with a slight scale settle on the incoming image
  • Reading-line activation: the row crossing a fixed line in the scrolling list becomes active, so wheel, trackpad, touch and keyboard all drive it without hover
  • Hover and focus take a row directly; a still cursor never fights the reading line while the list scrolls beneath it
  • Fast scrolling never queues wipes: an early wipe retargets its image, later ones stack at most a few layers deep and the frame always lands on the latest row
  • Images decoded on demand from the row's own <img>, the neighbours on each side preloaded, and nothing fetched for rows skimmed past at speed
  • Built for any count: plain list markup, delegated listeners, binary-search row lookup and a counter padded to the list length
  • Active row indents and brightens, the number turns to the accent, and the large counter rolls in the direction of travel
  • Arrow keys, Page Up/Down, Home and End move through the index with a single tab stop; Enter or click fires a tig:select event and an onSelect hook
  • Phones put the frame on top and the index below it, reduced motion swaps with a short fade, and without JavaScript every row shows its own image
  • revert() removes every generated layer, listener and tween and restores the original markup

Perfect for

5 use cases
  • Museum, archive and collection sites presenting dozens of objects as a browsable catalogue
  • Design studios and makers listing a back catalogue of products, editions or commissions
  • Photographers and illustrators indexing a long series where the titles matter as much as the pictures
  • Furniture, lighting and homeware shops browsing a range without opening product pages
  • Auction previews, exhibition checklists and publication contents pages with an image per entry

How it works

2 sections

The index is an ordinary ordered list that scrolls inside its own column, padded so the first and last rows can reach a reading line set at a fraction of its height. As it scrolls, a binary search over the measured row positions finds the row on the line and makes it active; a moving mouse or keyboard focus can claim a row directly.

The preview frame stacks image layers. Each change adds a layer above the last and reveals it with an animated clip-path inset from the edge matching the direction of travel, while the image inside settles from a slight scale. If another change arrives early in a wipe, that wipe simply swaps to the newer image; later changes start a new layer on top, and finished wipes discard everything beneath them. Images are fetched only once the active row holds still for a moment, then decoded before they are shown, with the rows either side preloaded.

Difficulty Advanced
Includes HTML + JS + CSS source, documentation, AI setup prompt, lifetime updates

Lighthouse, as measured

Google Lighthouse on this effect's demo, from the latest scan. A measurement of the demo as shipped, not a promise for your page.

Accessibility
100
Best practices
100

No performance score, on purpose. That figure depends on how you deploy: your server's compression and caching, your CDN, the connection and the device doing the test, none of which the code controls. The same page can score very differently on two consecutive runs, so measure it where it will live.

The Vault opens this source and the whole library.

Each product gives you the HTML, CSS and JavaScript source, the full documentation and an AI setup prompt, licensed for unlimited personal and commercial projects. Every paid product is tested for desktop and mobile layouts, reduced-motion handling, animation cleanup and keyboard controls wherever there is something to operate.

One payment, over 350 products, and future additions to the library.

Documentation

Quick Start

1. Add to your HTML <head>:

In your downloadThe working code for this step, in index.html.

2. Add before the closing </body> tag:

In your downloadThe working code for this step, in index.html.

3. Add the gallery markup. Give .tig a height (the demo fills the viewport); the list scrolls inside it.

In your downloadThe working code for this step, in index.html.

Using It With Your Own Design

What the effect needs from your markup: a [data-tig] root containing a scrolling [data-tig-list] of [data-tig-item] rows, and a [data-tig-frame] for the preview. Each item needs an image source, either a <img data-tig-img> inside it or a data-src (plus optional data-alt) on the item itself. Everything else is optional: [data-tig-row] is the focusable control (use a <button> or a link), [data-tig-title] and every [data-tig-meta] feed the caption, and the [data-tig-count], [data-tig-total], [data-tig-caption-title] and [data-tig-caption-meta] slots are filled if present. [data-tig-plate] is the frame's first image before any interaction and the no-JS fallback. Several galleries on one page work independently.

What is only the demo's styling: the fonts (Instrument Serif and Geist), the dark palette, the coral accent, the four-column row grid, the column labels and cue, the edge fade on the list, the large counter and the phone layout. Restyle or delete any of it; the script finds nothing by class name. The html, body { overflow: hidden } and .demo rules only make the demo fill its frame without page scrolling.

CSS the effect depends on:

  • The list must be the element that scrolls (overflow-y: auto and a constrained height). It is an ordinary nested scroller: it needs no data-lenis-prevent, and Lenis on the host page leaves it alone.
  • .tig__frame needs position: relative and overflow: hidden, and .tig__plate, .tig__layer and .tig__layer img must fill it (position: absolute; inset: 0, object-fit: cover). The script creates the .tig__layer elements.
  • .tig__plate[hidden] { display: none } keeps the hidden first image out of the way once a wipe has replaced it.
  • The .has-js .tig.is-live rules apply the start and end padding (--tig-pad-start, --tig-pad-end) that lets the first and last rows reach the line, plus the proximity snap (--tig-snap). Keep them if you rename things.
  • .has-js .tig__thumb { display: none } hides the per-row images when the script is running, so they never download in the list.

Options

Setting Where Default Description
--tig-line CSS on .tig 0.4 (0.3 on phones) Reading-line position as a fraction of the list's height. Set per breakpoint.
data-reading-line [data-tig] none Overrides --tig-line for one instance, e.g. data-reading-line="0.5".
--tig-indent CSS on .tig 18px (10px on phones) How far the active title shifts in.
data-src / data-alt [data-tig-item] none Image source and alt text for rows without an <img data-tig-img>.
SETTINGS.wipe script.js 0.85 Seconds for an unhurried wipe.
SETTINGS.wipeFast script.js 0.42 Seconds when entries change in quick succession.
SETTINGS.retarget script.js 0.45 A wipe less than this far through swaps to the newer image instead of stacking a new wipe.
SETTINGS.settleScale script.js 1.08 Starting scale of the incoming image.
SETTINGS.loadDelay script.js 90 Milliseconds a row must stay active before an uncached image is fetched.
SETTINGS.preload script.js 2 Neighbours decoded on each side of the active row.
SETTINGS.maxLayers script.js 4 Most wipe layers stacked during a burst.

Events and API

Clicking a row, or pressing Enter or Space on it, shows its image and fires tig:select. Nothing else opens by default, so you decide what selection means: a lightbox, a product page, a filter.

In your downloadThe working code for this step, in script.js.

Event Detail When
tig:change { index, item, source } The active row changes. source is pointer, line, key, api or init.
tig:select { index, item, src, title, meta } A row is clicked or activated with Enter or Space.
Method Description
gallery.goTo(index, { scroll }) Activate a row (zero-based). Scrolls it to the line unless scroll: false.
gallery.next() / gallery.prev() Step one row and scroll it to the line.
gallery.index The active row's index.
window.typographicIndexGallery.revert() Remove every generated layer, listener, tween and inline style, and restore the original markup. Same as window.gsapContext.revert().

Large Collections

  • Images are fetched only when a row stays active for a moment, then decoded before the wipe starts, so a fast scroll past 30 rows loads one image, not 30. The rows either side of the active one are preloaded.
  • Rapid changes never queue: a wipe that has barely started swaps to the newer image, and a later change starts a new layer on top that discards everything beneath it when it lands. At most a few layers exist at once.
  • All listeners are delegated to the list, and the row on the line is found with a binary search over measured positions, so hundreds of rows cost the same per frame as ten.
  • Row positions are re-measured on resize and after web fonts load. If you add or remove rows after load, call revert() and execute the script again.

Accessibility

  • Keyboard: the index is one tab stop. Arrow Up and Down move between rows, Page Up and Page Down jump ten, Home and End go to the ends; each focused row is scrolled to the reading line and becomes active. Enter or Space selects.
  • Touch: no hover needed. Scrolling the list drives the reading line, and a tap activates the row and parks it on the line.
  • Hover: only a pointer that actually moves claims a row, so a still cursor never fights the reading line while the list scrolls under it.
  • Screen readers: rows are native buttons in an ordered list and the active row carries aria-current; the frame's image always has the active entry's alt text.
  • Reduced motion: the frame swaps with a short fade, with no wipe, travel or scale; the title indent and the counter change without animation, and keyboard scrolling jumps instead of easing. Changing the preference while the page is open rebuilds the effect at the current row.
  • Without JavaScript: the list scrolls normally and every row shows its own image beside its text.

Images

The 50 demo images are baked WebP derivatives in assets/img/, cropped to 720×900 (4:5) with a slight saturation trim so the set reads as one collection. They come from Pexels and Unsplash and are used under the Pexels License and the Unsplash License. The titles, makers and dates are fictional. Replace the images with your own; any aspect ratio works because the frame crops with object-fit: cover, and around 900px tall stays sharp on high-density screens.

To regenerate or change them from a GSAP Vault checkout, edit assets/img-manifest.json (each entry names a stock-library source, an output name, a size and a grade) and run bun scripts/build-template-assets.ts typographic-index-gallery.

File Entry Source Original
01.webp Oak Tray with Bud Vase Pexels https://images.pexels.com/photos/7607461/pexels-photo-7607461.jpeg
02.webp Stack of Pinched Bowls Pexels https://images.pexels.com/photos/6611419/pexels-photo-6611419.jpeg
03.webp Cane-Back Dining Chair Pexels https://images.pexels.com/photos/35108893/pexels-photo-35108893.jpeg
04.webp Pleated Pendant, pair Pexels https://images.pexels.com/photos/13613289/pexels-photo-13613289.jpeg
05.webp Ochre and Tenmoku Bowls Pexels https://images.pexels.com/photos/33367176/pexels-photo-33367176.jpeg
06.webp Double Coat Hook Pexels https://images.pexels.com/photos/36650043/pexels-photo-36650043.jpeg
07.webp Low Table, Lounge Chair Pexels https://images.pexels.com/photos/2079294/pexels-photo-2079294.jpeg
08.webp Sapphire Study Pexels https://images.pexels.com/photos/11830966/pexels-photo-11830966.jpeg
09.webp Slatted Globe Lamp Pexels https://images.pexels.com/photos/23020540/pexels-photo-23020540.jpeg
10.webp Lidded Jars, studio shelf Pexels https://images.pexels.com/photos/26733186/pexels-photo-26733186.jpeg
11.webp Low Walnut Sideboard Pexels https://images.pexels.com/photos/12277217/pexels-photo-12277217.jpeg
12.webp Breast Drills, set of six Pexels https://images.pexels.com/photos/15404056/pexels-photo-15404056.jpeg
13.webp Stone Setting, microscope Pexels https://images.pexels.com/photos/6263153/pexels-photo-6263153.jpeg
14.webp Rough Garnet Specimens Pexels https://images.pexels.com/photos/13307186/pexels-photo-13307186.jpeg
15.webp Ladder-Back Side Chair Pexels https://images.pexels.com/photos/12965306/pexels-photo-12965306.jpeg
16.webp Loom Warp, detail Pexels https://images.pexels.com/photos/6634744/pexels-photo-6634744.jpeg
17.webp Radial Ceiling Light Pexels https://images.pexels.com/photos/5032376/pexels-photo-5032376.jpeg
18.webp Dotted Mug, in progress Pexels https://images.pexels.com/photos/18960521/pexels-photo-18960521.jpeg
19.webp Tool Wall, east bench Pexels https://images.pexels.com/photos/7483054/pexels-photo-7483054.jpeg
20.webp Thread Cones, dye-lot archive Pexels https://images.pexels.com/photos/34117399/pexels-photo-34117399.jpeg
21.webp Strap Hinges and Ring Pulls Pexels https://images.pexels.com/photos/13425281/pexels-photo-13425281.jpeg
22.webp Thin Gold Rings, a handful Pexels https://images.pexels.com/photos/15958820/pexels-photo-15958820.jpeg
23.webp Ochre Pleat Pendants Pexels https://images.pexels.com/photos/20152628/pexels-photo-20152628.jpeg
24.webp Glaze Firing, kiln load 14 Pexels https://images.pexels.com/photos/7222326/pexels-photo-7222326.jpeg
25.webp Glaze Test Session Pexels https://images.pexels.com/photos/7674846/pexels-photo-7674846.jpeg
26.webp Bearded Mask, fragment Pexels https://images.pexels.com/photos/31679934/pexels-photo-31679934.jpeg
27.webp Track Bicycle, red rims Pexels https://images.pexels.com/photos/28216172/pexels-photo-28216172.jpeg
28.webp Unfired Bowls on Kiln Shelves Pexels https://images.pexels.com/photos/28867401/pexels-photo-28867401.jpeg
29.webp Diamond and Blue Topaz Pexels https://images.pexels.com/photos/13412779/pexels-photo-13412779.jpeg
30.webp Slatted Globe, hung Pexels https://images.pexels.com/photos/17849255/pexels-photo-17849255.jpeg
31.webp Slatted Bench Pexels https://images.pexels.com/photos/12233290/pexels-photo-12233290.jpeg
32.webp Composing Stick Pexels https://images.pexels.com/photos/1440501/pexels-photo-1440501.jpeg
33.webp Trimming a Foot Ring Pexels https://images.pexels.com/photos/9304551/pexels-photo-9304551.jpeg
34.webp Stacked Bands, wide ring Pexels https://images.pexels.com/photos/18344078/pexels-photo-18344078.jpeg
35.webp Marble Side Table Pexels https://images.pexels.com/photos/7607320/pexels-photo-7607320.jpeg
36.webp Pot Still No. 2 Pexels https://images.pexels.com/photos/10040004/pexels-photo-10040004.jpeg
37.webp Jeweller’s Bench, at rest Pexels https://images.pexels.com/photos/6262830/pexels-photo-6262830.jpeg
38.webp Terracotta Bomber Unsplash https://images.unsplash.com/photo-1591047139829-d91aecb6caea
39.webp Cider Bottles and Crate Pexels https://images.pexels.com/photos/34525378/pexels-photo-34525378.jpeg
40.webp Sanding a Tabletop Pexels https://images.pexels.com/photos/5466146/pexels-photo-5466146.jpeg
41.webp Bisque Load, plates Pexels https://images.pexels.com/photos/9733234/pexels-photo-9733234.jpeg
42.webp Ring Mandrel and Gauges Pexels https://images.pexels.com/photos/4354598/pexels-photo-4354598.jpeg
43.webp At the Bench Pexels https://images.pexels.com/photos/37358114/pexels-photo-37358114.jpeg
44.webp Offset Press, two-unit Unsplash https://images.unsplash.com/photo-1562155695-fb6e1f95fcfd
45.webp Signet and Plain Band Pexels https://images.pexels.com/photos/8433597/pexels-photo-8433597.jpeg
46.webp Cup Set, round of eight Unsplash https://images.unsplash.com/photo-1498804103079-a6351b050096
47.webp Throwing a Cylinder Pexels https://images.pexels.com/photos/10111544/pexels-photo-10111544.jpeg
48.webp Wall Rail with Coats Pexels https://images.pexels.com/photos/18144397/pexels-photo-18144397.jpeg
49.webp City Car, pale blue Unsplash https://images.unsplash.com/photo-1549317661-bd32c8ce0db2
50.webp Stillhouse, pair of stills Pexels https://images.pexels.com/photos/34635802/pexels-photo-34635802.jpeg

Dependencies

Required:

  • GSAP 3.12+ (core only; the demo pins 3.15.0)

No plugins, no Lenis and no build step.

Also in your download: the full source, worked examples, the events and programmatic API, and the class reference.

£7