176

Spine Accordion Gallery

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

A large photo gallery shown as 50 thin vertical spines across the full width; the pointer opens a fisheye panel that glides between photos, with click-to-hold, keyboard and touch-drag browsing.

advanced
8 more details
fisheyeaccordionticker-looppointer-trackingvelocity-reactivetouch-dragkeyboard-navigationprogressive-enhancement

Released

About this effect

Fifty photographs sit side by side as thin vertical spines, each one a real strip from the centre of its photo. Move the pointer across the row and the spine beneath it opens into a wide panel while its neighbours open a little less the further away they sit, so the whole set stays visible while one image is shown properly.

Read the full effect overview

Slow movement glides from photo to photo; a fast sweep sends a ripple through the row that settles back into one open panel. Click to hold a photo open, or browse with the arrow keys or a horizontal drag on a phone.

Spine Accordion Gallery - GSAP animation effect preview

What's included

11 items
  • Holds 10 to 60 images in one row, with 50 shown in the demo
  • Fisheye widths: one panel opens to about a third of the stage, neighbours open less and less with distance
  • Spines show a centred strip of the actual photo, never a stretched one
  • One GSAP ticker eases every width; it sleeps once the row has settled
  • Fast sweeps open a wider spread for a moment and leave a short wake before settling
  • Click or tap holds a panel open; click again or Escape releases it
  • Arrow keys, Home, End and Page Up/Down move the open panel, with screen reader announcements
  • Horizontal touch drag scrubs the row without blocking vertical page scrolling
  • Narrow screens pan a 6px-spine strip so the open panel takes most of the width
  • Caption number and title reveal with a short stagger; tiny vertical numbers label each spine
  • Plain HTML list of lazy WebP images that falls back to a scrollable strip without JavaScript

Perfect for

5 use cases
  • Wedding and event photographers showing a whole day in one view
  • Portfolio archives where the size of the set is part of the story
  • Product or collection lookbooks with dozens of shots
  • Travel and editorial photo essays that should read as a sequence
  • Agency case-study pages that summarise a large shoot at a glance

How it works

2 sections

The photos are an ordinary ordered list, so they stay readable without JavaScript and to screen readers. Each spine shows the middle strip of its photo, and opening it reveals more of the same picture rather than zooming in.

GSAP drives the motion from one animation loop: the open point follows the pointer with a little weight, and every spine eases to its new width, so slow movement glides and a fast sweep ripples before settling. The row always fills the stage exactly, and the loop sleeps once everything is still. On narrow screens the spines keep a minimum width and the row pans to follow the open photo. Reduced motion switches panels instantly, and revert() removes everything the script added.

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 the gallery to your <body>:

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

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

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

The script finds every [data-spine-gallery], measures its viewport and starts one GSAP ticker per gallery. It numbers the photos itself: you write only the image and its title.

Using It With Your Own Design

What the effect needs from your markup: a [data-spine-gallery] root holding a [data-spine-viewport] (the focusable, clipped stage), a [data-spine-track] inside it and one [data-spine-item] per photo. Inside each item the script reads an optional figcaption; it prepends a .spine-gallery__num to it and appends a .spine-gallery__spine number label to the item. An optional [data-spine-counter] anywhere in the root receives "12 / 50". Every hook is a data- attribute, so you can rename the classes.

What is only the demo's styling: the Inter Tight face, the off-white palette, the header bar, the caption type, the hairline between spines, the held-open dot and the "held" suffix on the counter. The .album rules in style.css belong to the demo page and can go entirely.

CSS the effect depends on:

  • .has-js .spine-gallery__viewport must clip (overflow: hidden) and keep touch-action: pan-y, which lets vertical swipes scroll the page while horizontal drags scrub the row.
  • .has-js .spine-gallery__item starts as flex: 1 1 0; min-width: 0 so the first paint is an even row of spines; .is-live switches items to flex: 0 0 auto once the script writes widths.
  • The image is position: absolute; left: 50%; width: var(--spine-panel); transform: translateX(-50%). The script sets --spine-panel to the open panel width. This is what makes a spine show the middle of its photo and makes opening read as an unmask, not a zoom. If you size the image to the item instead, spines become tiny squashed thumbnails.
  • Captions are pre-hidden under .has-js and revealed by the script; .is-instant (reduced motion) shows the active caption with CSS alone. Give the caption a fixed width (the demo uses the panel width) so it never rewraps while a panel opens.
  • The viewport needs a height. The demo fills the window; any fixed or svh height works.

Options

Set these on the [data-spine-gallery] element.

Attribute Values Default Description
data-start Index from 0 0 Photo open on load
data-open 0.1 to 0.9 0.35 Open panel width as a fraction of the stage
data-open-narrow 0.1 to 0.95 0.62 Open panel width on narrow stages
data-narrow-below Pixels 640 Stage width below which the narrow values apply
data-spread 0.3 to 6 1.05 Falloff width in items: larger opens more neighbours
data-spread-narrow 0.3 to 6 0.55 Falloff width on narrow stages
data-spine Pixels 6 Minimum spine width. When the row cannot fit at this width, it pans instead
data-ease 1 to 40 7 Follow rate per second; higher is snappier
data-ripple 0 to 3 1 How much a fast sweep widens the falloff; 0 turns the ripple off

Interaction

  • Pointer: the open panel follows the pointer across the stage. Click holds the photo open, so the pointer can leave; click again (or press Escape) to release it.
  • Touch: drag horizontally to scrub through the row; on release the nearest photo stays open. Tap a spine to open and hold it, tap the open panel to release. Vertical swipes scroll the page as normal.
  • Keyboard: the viewport is focusable. Left and Right arrows move one photo, Page Up and Page Down move five, Home and End jump to the ends, Enter or Space holds or releases, Escape releases.

Accessibility

  • The viewport is a labelled group with aria-roledescription="gallery". A polite live region announces "Photo 12 of 50, The kiss" after keyboard, click and tap changes, but not during pointer sweeps.
  • The open item gets aria-current="true". Every image keeps its own alt text.
  • Reduced motion: no easing or ripple; panels switch instantly and captions appear without a stagger. Every control still works. Changing the preference while the page is open rebuilds the gallery in the right mode.
  • No JavaScript or no GSAP: the list is a horizontally scrollable strip of full images with captions underneath.

Performance

Each frame writes one width per item and a transform on the track, and reads nothing from the layout, so 50 images cost a single layout pass per frame. The ticker sleeps once the row has settled and wakes on input. Images are plain lazy WebP files; the demo's 50 photos are about 1.9MB in total.

Images

The demo photographs are art-directed derivatives (cropped to 640×900 and gently desaturated) of photos from Pexels, used under the Pexels licence. Sources are listed in assets/img-manifest.json, which names entries from the GSAP Vault stock library. Replace the files in assets/img/ with your own, or point each <img> at your images: any aspect ratio works, since the frame crops with object-fit: cover. Around 900px tall is enough for a sharp panel on high-density screens.

Dependencies

Required:

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

Not used: Lenis or any GSAP plugin. The effect never scrolls the page.

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

£7