178

Print Pile Gallery

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

A photo gallery as a pile of prints on a table: pick any print up, drag it, throw it and it slides to rest, turn it over to read the handwritten note on the back, and tidy the whole pile into a grid in one move.

advanced
12 more details
dragmomentumthrowphysicscard-stack3d-transformsstaggertouch-gestureskeyboard-navigationfocus-managementreduced-motionprogressive-enhancement

Released

About this effect

Your photographs arrive as a loose pile of prints on a dark table, white-bordered and tipped at odd angles, overlapping the way a summer's worth of snapshots does when you tip them out of the envelope. Any print can be picked up: it lifts off the table with a deeper shadow, rises to the top of the pile and follows your hand, swinging a little around the point you are holding it by. Let go mid-swing and it is thrown, sliding across the table, glancing off the edges and slowing to rest.

Read the full effect overview

Click or tap a print, or press Enter, and it is brought to the front, straightened and turned over to show the back: a date and a note in handwriting. Turn it again and it goes back where it was. Press Tidy and the pile is dealt into a neat, evenly spaced grid with every photograph visible, the prints near the middle landing first and each one overshooting a touch and settling square. Scatter throws them back into a fresh pile, different every time.

Print Pile Gallery - GSAP animation effect preview

What's included

14 items
  • A loose, overlapping pile of prints with white borders and paper shadows, laid out the same way on every visit
  • Pick up any print: it lifts, rises to the top and swings slightly around the point you hold
  • Throw a print and it slides across the table with friction, bouncing softly off the edges and never leaving the table
  • Click, tap or Enter turns a print over to a handwritten date and note on the back; again turns it back to its place
  • Double-click a print and it lifts off the table into a full-size close-up that sharpens as the large photograph arrives; click to zoom into any point, move the pointer to look around, or step through the pile with the arrow keys
  • Tidy deals the whole pile into an even grid, middle first, with an overshoot and a small settle as each print lands
  • Scatter throws the prints back into a new pile, so it is never the same twice
  • Grab a print in mid-flight and it is yours; press Tidy or Scatter as often as you like
  • Sized for large sets: prints and grid columns follow the number of photographs and the shape of the table, up to 50 prints and beyond
  • On phones the tidy grid scrolls inside the table while the page stays still
  • Keyboard: Tab between prints, arrow keys nudge the focused print, Enter turns it over, T tidies, S scatters, Escape turns everything back
  • Live announcements for each turned print and each change between pile and grid
  • Reduced motion: no throws or flights, prints are placed and turned with a short fade; no JavaScript: a wrapped grid of prints with their captions
  • Small API (tidy, scatter, flip) and mode and flip events

Perfect for

4 use cases
  • Holiday, family and event galleries that should feel like a box of prints rather than a slideshow
  • Photographer and illustrator portfolios that invite visitors to play with the work
  • Wedding and party pages where guests leave a note on the back of each photo
  • Brand storytelling and 'about us' pages built from archive snapshots with captions

How it works

2 sections

The gallery is an ordinary list of figures: each one holds the photograph and a caption, and that caption becomes the handwritten back of the print. Without JavaScript the list is a tidy grid of prints with the captions underneath, so nothing is lost.

With JavaScript, GSAP takes over every print's position, angle and size and lays the list out as a pile from a fixed seed, so the pile looks the same each visit until it is scattered. Dragging, swinging, throwing and sliding run from GSAP's own clock only while something is moving, and stop the moment everything is at rest. Turning a print over is a 3D flip of the card, and the close-up flies the print from wherever it lies to the middle of the stage, loading the large photograph only when it is asked for. Tidy and Scatter are staggered GSAP timelines that you can interrupt at any point. The prints are never re-ordered in the page, so screen readers and the Tab key keep the order of your list, and everything the script adds is removed again on revert().

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-print-pile], sizes the prints to the table and the number of photographs, and lays them out as a pile. No GSAP plugins are needed.

Using It With Your Own Design

What the effect needs from your markup:

  • A root with data-print-pile, containing .print-pile__table (the surface the prints live on) and inside it .print-pile__list, a list of .print-pile__item elements.
  • Each item holds one figure.print with a .print__front wrapping an <img>, and a figcaption.print__back. Give the image its real width and height: the script reads them to shape a portrait or landscape print. The image alt, followed by the caption, is what screen readers hear for the print; the caption is also announced when it is turned over.
  • For the close-up, give each <img> a data-full attribute naming a larger copy of the photograph (the demo uses 1400px files). It is only fetched when that print is opened; the small file shows straight away and the large one fades in over it. Without data-full the close-up uses the image you already have.
  • .print__date and .print__note inside the caption are optional. Anything you put in the figcaption appears on the back.
  • Any button inside the root with data-pile-action="tidy" or data-pile-action="scatter" becomes a control, and its aria-pressed follows the current arrangement. The control row is optional; T and S work without it.

What is only the demo's styling:

  • The colours, the vignette on the table, the Archivo control row and the hint line. Change or delete them freely.
  • Caveat on the backs, the biro blue, the paper tones and the printed Nº 07 lab number (a CSS counter in .print__back::before).
  • The photographs in assets/img/ are demo images baked for this product. Replace them with your own.

CSS the effect depends on (all in the "EFFECT" blocks of style.css):

  • .print-pile__table needs a real height. In the demo it fills the space under the control row (flex: 1; min-height: 0); in your page give it a height such as 70vh, or the pile has nowhere to lie.
  • .print-pile.is-live rules switch the list to absolute positioning, put each item at the table's top-left corner, set touch-action: none on prints (and pan-y when the tidy grid scrolls) and build the two faces with backface-visibility: hidden. Keep them, or the prints will stack, scroll the page under a finger or show both faces.
  • .has-js .print-pile:not(.is-live) .print-pile__list { visibility: hidden; } hides the list for the moment between first paint and the script placing the prints. The script removes .has-js if GSAP fails to load, which brings the plain grid back.
  • The script owns transform, z-index, width, height and font-size on every .print-pile__item. Style the print through .print__front and .print__back, not by transforming the item. The writing on the back is sized in em, so it scales with the print.
  • .print-pile__lift is a span the script adds to each item for the lifted shadow. Restyle its box-shadow to taste.
  • The close-up is built by the script inside the root (.print-pile__viewer), so the root needs position: relative and the viewer covers the whole gallery. Its "EFFECT: the close-up viewer" block sets the shade, the paper border (--view-border, set by the script) and the clipping lens the zoom happens in; restyle the caption and the four buttons freely. The rule marked "DEMO ONLY" just keeps the buttons clear of the demo page's corner badge: delete it.

Options

Attribute Values Default Description
data-print-pile marker required Marks one gallery; several can share a page
data-mode pile, grid pile How the prints are arranged on load
data-seed integer 7 Which pile you get on load. Each seed always gives the same pile
data-spread 0.2 to 1 0.84 How far the pile spreads across the table
data-tilt 0 to 45 16 Largest tilt of a print in the pile, in degrees
data-friction 0.5 to 12 3.2 How quickly a thrown print slows down; higher stops sooner
data-swing 0 to 3 1 How much a held print swings around the point you hold. 0 keeps its angle
data-max-size px 210 Longest edge of a print. Prints get smaller as the set grows
data-intro true, false true Whether the pile drops onto the table on load

Accessibility

  • Keyboard: every print is a focusable button. Tab moves through the prints in list order, arrow keys nudge the focused print (Shift for bigger steps), Enter or Space turns it over, Z lifts it up for a close look, Escape turns every print back. In the close-up, Left and Right move to the previous and next print, Z or + zooms, minus fits, Tab cycles its four buttons and Escape puts the print back, returning focus to it. T tidies and S scatters while focus is in the gallery, and the Tidy and Scatter buttons do the same. A focused print rises to the top of the pile so its outline is never buried.
  • Close-up: a modal dialog with a caption giving the print's number, description and the writing on its back. Focus moves to its Close button and stays inside it until it closes.
  • Screen readers: each print is named by its image alt followed by the writing on its back, and reports its turned-over state. A polite live region reads the back of each print as it turns over and announces Tidy and Scatter. The list order in the page never changes, however the pile is shuffled.
  • Touch: one finger drags a print, a tap turns it over and a double tap opens the close-up. In the close-up a tap zooms, a drag looks around the zoomed print and a sideways swipe moves to the next one. Several prints can be dragged with several fingers. When the tidy grid is taller than the table, vertical swipes scroll it and sideways drags pick a print up. The page itself never scrolls under a print.
  • Reduced motion: no throws, swings or flights. Prints still follow a dragged finger or pointer and stop where they are let go; Tidy, Scatter, turning a print over and the close-up happen at once behind a short fade. Changing the preference while the page is open rebuilds the gallery in the new mode.
  • No JavaScript: the list is a wrapped grid of prints with their captions written underneath, scrolling inside the table.

Image Credits

The 44 photographs are art-directed derivatives (cropped, resized and lightly desaturated) of photos published under the Pexels License, baked into assets/img/ at 520px on the long edge. Ten are a second, upright crop of the same photo. assets/img-manifest.json records each source; edit it and run bun scripts/build-template-assets.ts print-pile-gallery to re-bake, or simply replace the files with your own and update the <img> dimensions.

Files Source URL
print-01.webp Pexels https://images.pexels.com/photos/19847806/pexels-photo-19847806.jpeg
print-02.webp Pexels https://images.pexels.com/photos/17929440/pexels-photo-17929440.jpeg
print-03.webp, print-38.webp Pexels https://images.pexels.com/photos/5069216/pexels-photo-5069216.jpeg
print-04.webp, print-36.webp Pexels https://images.pexels.com/photos/5851473/pexels-photo-5851473.jpeg
print-05.webp Pexels https://images.pexels.com/photos/5952863/pexels-photo-5952863.jpeg
print-06.webp Pexels https://images.pexels.com/photos/32063429/pexels-photo-32063429.jpeg
print-07.webp Pexels https://images.pexels.com/photos/18720264/pexels-photo-18720264.jpeg
print-08.webp, print-40.webp Pexels by Operaciones Lima https://www.pexels.com/photo/group-of-runners-exercising-outdoors-in-lima-37858833/
print-09.webp Pexels https://images.pexels.com/photos/2965607/pexels-photo-2965607.jpeg
print-10.webp, print-41.webp Pexels https://images.pexels.com/photos/18729882/pexels-photo-18729882.jpeg
print-11.webp Pexels https://images.pexels.com/photos/5069218/pexels-photo-5069218.jpeg
print-12.webp Pexels https://images.pexels.com/photos/30942322/pexels-photo-30942322.jpeg
print-13.webp, print-42.webp Pexels https://images.pexels.com/photos/5851475/pexels-photo-5851475.jpeg
print-14.webp, print-43.webp Pexels https://images.pexels.com/photos/4853112/pexels-photo-4853112.jpeg
print-15.webp Pexels https://images.pexels.com/photos/15222306/pexels-photo-15222306.jpeg
print-16.webp, print-35.webp Pexels https://images.pexels.com/photos/6775404/pexels-photo-6775404.jpeg
print-17.webp Pexels https://images.pexels.com/photos/31628832/pexels-photo-31628832.jpeg
print-18.webp, print-37.webp Pexels https://images.pexels.com/photos/32837501/pexels-photo-32837501.jpeg
print-19.webp, print-39.webp Pexels https://images.pexels.com/photos/5069201/pexels-photo-5069201.jpeg
print-20.webp Pexels https://images.pexels.com/photos/7519087/pexels-photo-7519087.jpeg
print-21.webp Pexels by RDNE Stock project https://www.pexels.com/photo/healthy-people-woman-women-6539881/
print-22.webp, print-44.webp Pexels https://images.pexels.com/photos/28387811/pexels-photo-28387811.jpeg
print-23.webp Pexels https://images.pexels.com/photos/7112541/pexels-photo-7112541.jpeg
print-24.webp Pexels https://images.pexels.com/photos/3775158/pexels-photo-3775158.jpeg
print-25.webp Pexels https://images.pexels.com/photos/5851478/pexels-photo-5851478.jpeg
print-26.webp Pexels by AirFit https://www.pexels.com/photo/people-jogging-in-place-6150627/
print-27.webp Pexels https://images.pexels.com/photos/36344918/pexels-photo-36344918.jpeg
print-28.webp Pexels https://images.pexels.com/photos/5069230/pexels-photo-5069230.jpeg
print-29.webp Pexels https://images.pexels.com/photos/26953466/pexels-photo-26953466.jpeg
print-30.webp Pexels https://images.pexels.com/photos/5851474/pexels-photo-5851474.jpeg
print-31.webp Pexels by Mikhail Nilov https://www.pexels.com/photo/man-and-woman-doing-high-five-6740312/
print-32.webp Pexels https://images.pexels.com/photos/10762369/pexels-photo-10762369.jpeg
print-33.webp Pexels https://images.pexels.com/photos/6012208/pexels-photo-6012208.jpeg
print-34.webp Pexels https://images.pexels.com/photos/5851472/pexels-photo-5851472.jpeg

Dependencies

Required:

  • GSAP 3.12+ (the demo is pinned to 3.15.0)

Optional:

  • None. No GSAP plugins and no smooth-scroll library; the gallery is pointer, touch and keyboard driven and does not use Lenis.

Fonts: Archivo (controls) and Caveat (the handwriting) from Google Fonts. Both are demo choices; the effect works with any face.

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

£7