Nº 355 of 520
Photo album with page turns
A black-paged family album with photo corners and white-pen captions. Leaves turn over the spine in 3D on click or the arrow keys.
Preview
Use it
Free to use · credit appreciated1 Once, in your project
npx skills add SusanAcharya/Design-Lounge 2 Then ask
Using the Design Lounge skill, build "Photo album with page turns" (piece gallery-photo-album) in my stack. Match its brief; restyle it onto my product's theme.
No skill? Paste the brief instead. It carries the numbers, tokens and behaviour.
<!-- Design Lounge Nº 355 · "Photo album with page turns" · www.designlounge.live -->
# Photo album with page turns
> **Build brief for a coding agent.** Rebuild this piece in the reader's stack. If they haven't said which stack, ask once, then default to semantic HTML + CSS + a little vanilla JS. Match the numbers below; don't "improve" them.
## What it is
A family photo album from summer 1986, open on a bottle-green felt table. The album has a brown leather cover and black paper pages. Prints have a 7px cream border, sit at small angles, and are held by four cream photo corners. Captions are handwritten in white gel pen (Caveat). The leaves turn over the spine in real 3D: each leaf is a two-faced card that rotates −180° around its left edge. The detail worth copying is the photo corner: one pseudo-element with four diagonal gradients, so any print gets corners without extra markup.
Every photo is an illustrated SVG scene (canoe on a dock, cabin under pines, birthday cake, station wagon, dog, rope swing, campfire, watermelon picnic, fireflies in a jar). No image files.
## Reference behaviour
1. First frame: the album is open at pages 2–3 (the first leaf is already turned). Header counter reads "Pages 2–3 of 8".
2. Pressing → (anywhere), the Next button, or clicking anywhere on the right page turns the top right leaf over to the left in 900ms. The counter updates as the turn starts.
3. Pressing ←, the Previous button, or clicking the left page turns the top left leaf back.
4. A shade gradient sweeps across the turning leaf: opacity 0 → 1 at the midpoint → 0.
5. Turns can be queued quickly; each turning leaf goes on top while it moves (z-index 100+), then settles into the stack.
6. At the start (title page and page 1) Previous is disabled; at the end (page 8 and the end page) Next is disabled. The page clicks follow the same rule.
7. The left inside cover is a title page: "Kettle Pond", a rule, "Cabin 4 · July 19 – August 24, 1986", "The Okafor-Lindqvist family". The right inside back cover reads "Same time next year."
8. Reduced motion: the turn is instant (1ms) and the shade does not animate.
## Structure
```
1280 × 800, column flex
┌───────────────────────────────────────────────────────────────┐
│ ALBUM NO. 3 · SUMMER 1986 │ header max 1040, padding 26 24 0
│ Kettle Pond (Lora italic 30) Pages 2–3 of 8 │
│ ┌ cover, inset −12 −14, leather ──────────────────────────┐ │
│ │┌ left page 480×600 ─────────┐┌ right page 480×600 ──────┐│ │
│ ││ [print −3°] note+arrow ││ [print 2°, 320 wide] ││ │ book 960×600
│ ││ caption ││ caption + small line ││ │ scaled by --s
│ ││ [print 2°] ││ [print −3.5°] ││ │
│ ││ caption 2 ││ caption 3 ││ │
│ │└────────────────────────────┘└──────────────────────────┘│ │
│ └───────────────────────────── ribbon ───────────────────────┘
│ (‹) ← → turn the page (›) │ nav, padding 34 0 20
└───────────────────────────────────────────────────────────────┘
… 179 more lines. Copy or download for the full brief. Loading the source…Brief
219 linesBuild brief for a coding agent. Rebuild this piece in the reader’s stack. If they haven’t said which stack, ask once, then default to semantic HTML + CSS + a little vanilla JS. Match the numbers below; don’t “improve” them.
What it is
A family photo album from summer 1986, open on a bottle-green felt table. The album has a brown leather cover and black paper pages. Prints have a 7px cream border, sit at small angles, and are held by four cream photo corners. Captions are handwritten in white gel pen (Caveat). The leaves turn over the spine in real 3D: each leaf is a two-faced card that rotates −180° around its left edge. The detail worth copying is the photo corner: one pseudo-element with four diagonal gradients, so any print gets corners without extra markup.
Every photo is an illustrated SVG scene (canoe on a dock, cabin under pines, birthday cake, station wagon, dog, rope swing, campfire, watermelon picnic, fireflies in a jar). No image files.
Reference behaviour
- First frame: the album is open at pages 2–3 (the first leaf is already turned). Header counter reads “Pages 2–3 of 8”.
- Pressing → (anywhere), the Next button, or clicking anywhere on the right page turns the top right leaf over to the left in 900ms. The counter updates as the turn starts.
- Pressing ←, the Previous button, or clicking the left page turns the top left leaf back.
- A shade gradient sweeps across the turning leaf: opacity 0 → 1 at the midpoint → 0.
- Turns can be queued quickly; each turning leaf goes on top while it moves (z-index 100+), then settles into the stack.
- At the start (title page and page 1) Previous is disabled; at the end (page 8 and the end page) Next is disabled. The page clicks follow the same rule.
- The left inside cover is a title page: “Kettle Pond”, a rule, “Cabin 4 · July 19 – August 24, 1986”, “The Okafor-Lindqvist family”. The right inside back cover reads “Same time next year.”
- Reduced motion: the turn is instant (1ms) and the shade does not animate.
Structure
1280 × 800, column flex
┌───────────────────────────────────────────────────────────────┐
│ ALBUM NO. 3 · SUMMER 1986 │ header max 1040, padding 26 24 0
│ Kettle Pond (Lora italic 30) Pages 2–3 of 8 │
│ ┌ cover, inset −12 −14, leather ──────────────────────────┐ │
│ │┌ left page 480×600 ─────────┐┌ right page 480×600 ──────┐│ │
│ ││ [print −3°] note+arrow ││ [print 2°, 320 wide] ││ │ book 960×600
│ ││ caption ││ caption + small line ││ │ scaled by --s
│ ││ [print 2°] ││ [print −3.5°] ││ │
│ ││ caption 2 ││ caption 3 ││ │
│ │└────────────────────────────┘└──────────────────────────┘│ │
│ └───────────────────────────── ribbon ───────────────────────┘
│ (‹) ← → turn the page (›) │ nav, padding 34 0 20
└───────────────────────────────────────────────────────────────┘
header: kickerp, the onlyh1, and a counterpwitharia-live="polite"..fitreserves960×600 × --sof layout;.bookinside is 960×600 withtransform: scale(var(--s)), origin top left,perspective: 2400px.- Two static
.pages: left inside cover, right inside back cover. - Four
.leafelements stacked on the right half (left:480px), each withsection.face.front(odd page) andsection.face.back(even page, pre-rotated 180°). - Each photo is a
figure.phwith a.printholding the SVG, and afigcaption. Optional.notehandwritten asides with a drawn arrow. - Two transparent
button.hitareas over the left and right page (tabindex="-1", the visible buttons are the keyboard path). nav.controlslabelled “Album pages” with Previous and Next icon buttons.
Tokens
:root {
--bg: #26332b; /* felt table */
--bg-2: #1e2922; /* table edge */
--cover: #5a2f24; /* leather */
--page: #1c1c1a; /* black album paper */
--ink: #efe8d8; /* white gel pen, primary text */
--ink-2: #b9b19f; /* second caption line, notes */
--ink-3: #8a8578; /* page numbers, hint */
--corner: #d3c8b0; /* photo corners */
--print: #f2ecdf; /* print border */
--ribbon: #c8962f; /* bookmark, focus */
--hand: "Caveat", cursive;
--serif: "Lora", Georgia, serif;
--turn: 900ms;
--sheet: cubic-bezier(.32,.72,0,1);
--ease: cubic-bezier(.2,.7,.2,1);
--page-w: 480px;
--page-h: 600px;
--corner-size: 26px;
}
- Table:
radial-gradient(90% 70% at 50% 45%, #2e3d33, #26332b 55%, #1e2922). - Cover: horizontal gradient
#4c271e → #5a2f24 8% … #3a1d16 50% (spine) … #5a2f24 92% → #4c271e, radius 6px, shadow0 30px 60px -20px #000c, 0 4px 10px #0006. - Paper grain: two dot grids (
#ffffff06at 5px,#00000030at 7px offset 2px 3px). - Gutter: 40px gradient from
#000aat the spine edge of each page. - Print shadow:
0 2px 3px #0009, 0 8px 16px -6px #0008. Photos getsepia(.22) saturate(.86) contrast(.96)to read as 1980s prints.
Typography
| Role | Family | Size / line | Weight | Tracking | Colour |
|---|---|---|---|---|---|
| Kicker | Lora | 12px | 400 | 0.16em, upper | --ink-2 |
| Title h1 | Lora italic | 30px / 1.1 | 500 | 0 | --ink |
| Counter | Lora | 14px | 400 | 0 | --ink-2 |
| Caption | Caveat | 25px / 1.05 | 500 | 0 | --ink |
| Caption second line | Caveat | 19px | 500 | 0 | --ink-2 |
| Note | Caveat | 22px / 1.1 | 500 | 0 | --ink-2 |
| Title page | Caveat | 58px / 1 | 600 | 0 | --ink |
| Page number | Lora | 12px | 400 | 0.1em | --ink-3 |
Captions counter-rotate at −1.4× the print angle, so the handwriting is not parallel to the print.
Motion
| Thing | Trigger | Property | From → to | Duration | Easing | Reduced motion |
|---|---|---|---|---|---|---|
| Leaf turn forward | next | transform | rotateY(0) → rotateY(-180deg) | 900ms | --sheet | 1ms |
| Leaf turn back | prev | transform | rotateY(-180deg) → rotateY(0) | 900ms | --sheet | 1ms |
| Leaf shade | during turn | opacity | 0 → 1 (50%) → 0 | 900ms | --sheet | none |
| Turning z-index | turn start/end | z-index | 100+n while moving, stack value after | instant | — | same |
| Buttons | hover | background, border | #00000026 → #efe8d814 | 160ms | --ease | none |
States
- Hit areas: pointer cursor. Disabled at the ends (
defaultcursor, no click). - Buttons: resting
1px #efe8d833border, 44px circle. Hover lightens. Disabled at 35% opacity. - Focus-visible: 2px
--ribbonoutline, offset 3px. - Faces not in the open spread are
aria-hidden="true". - No loading, empty or error state; the album always has 8 pages.
Accessibility
.bookhasaria-roledescription="photo album"and a label; each face is asectionlabelled “Page N”.- Photos are
figure+figcaption; the caption is the accessible text. SVGs are decorative. - ← and → are global shortcuts; Previous/Next buttons are reachable by Tab, 44px.
- The counter is
aria-live="polite"and announces the new pages at the start of each turn. - Hidden faces are
aria-hiddenso the reader only hears the open spread. - Contrast:
#efe8d8on#1c1c1a≈ 14:1.#b9b19f≈ 8:1.#8a8578page numbers ≈ 4.6:1.
Responsive rules
- ≥1280: scale 1 at 800 high.
- The book scales as a unit:
--s = min(1, (innerWidth − 48) / 988, (innerHeight − 214) / 624), floor 0.3. The.fitbox reserves the scaled size so nothing overlaps. - 1024 and 768: same spread, scaled down.
- <640: the spread still shows at about 0.33. In a product, switch to one page at a time below 640 (each face full width, the turn becomes a horizontal slide) rather than going smaller.
- Never let the book cause a horizontal scrollbar.
Acceptance checklist
Always
- Each leaf is one element with two backface-hidden faces; the back face is pre-rotated 180°.
- The turn rotates around the spine (
transform-origin: 0 50%), with perspective on the parent. - A turning leaf is on top for the whole turn and the stack is correct afterwards.
- ← / → keys, the buttons, and clicking a page all turn pages.
- Previous is disabled on the first spread and Next on the last.
- Every print has four photo corners from a single pseudo-element.
- Captions are handwritten and sit under their print, never over another print or the page number.
- Reduced motion makes the turn instant.
- No image URLs; pictures are inline SVG.
This demo
- Opens on pages 2–3: “Nell turns 7!” and “Watermelon contest” left, “The Wagon. 212,000 miles” and “Biscuit, refusing to swim” right.
- Four leaves, eight pages, plus title page and end page.
- Kicker “Album no. 3 · Summer 1986”, title “Kettle Pond”.
- Turn is 900ms on
cubic-bezier(.32,.72,0,1). - Corners
#d3c8b0, 26px; print border 7px#f2ecdf.
Implementation notes
The leaf. Faces are absolutely stacked; only the leaf rotates.
.book { perspective: 2400px; }
.leaf { position: absolute; left: 480px; width: 480px; height: 600px;
transform-style: preserve-3d; transform-origin: 0 50%;
transition: transform var(--turn) var(--sheet); }
.leaf.turned { transform: rotateY(-180deg); }
.face { position: absolute; inset: 0; backface-visibility: hidden; }
.face.back { transform: rotateY(180deg); }
Z-order. Unturned leaves stack first-on-top; turned leaves stack last-on-top. While turning, lift the leaf above both.
function z() {
leaves.forEach((l, i) => {
if (!l.classList.contains('flipping')) l.style.zIndex = i < spread ? i + 1 : N * 2 - i;
});
}
function go(d) {
const t = spread + d; if (t < 0 || t > N) return;
const leaf = leaves[d > 0 ? spread : spread - 1];
spread = t;
leaf.classList.add('flipping'); leaf.style.zIndex = 100 + (++busy);
leaf.classList.toggle('turned', d > 0);
setTimeout(() => { leaf.classList.remove('flipping'); busy--; z(); }, 920);
}
Photo corners. One pseudo-element, inset −6px, four triangles.
.print::after { content: ""; position: absolute; inset: -6px; --c: var(--corner);
background:
linear-gradient(135deg, var(--c) 0 50%, #0000 50%) top left / 26px 26px no-repeat,
linear-gradient(225deg, var(--c) 0 50%, #0000 50%) top right / 26px 26px no-repeat,
linear-gradient(45deg, var(--c) 0 50%, #0000 50%) bottom left / 26px 26px no-repeat,
linear-gradient(315deg, var(--c) 0 50%, #0000 50%) bottom right / 26px 26px no-repeat;
filter: drop-shadow(0 1px 1px #0007); }
Common mistakes:
- Applying the initial “turned” class from JS after first paint, so the album plays a turn on load. Put it in the markup.
- Swapping z-index at the end of the turn only; the leaf slides under its neighbour halfway through.
overflow:hiddenon the leaf; it flattenspreserve-3d. Put it on the faces.- Rotating the captions with the print. They are written on the page, not on the photo.
- White album pages. This album is black paper; the white pen only works on black.
- Scaling the book with
widthpercentages; scale the whole 960×600 unit so the 3D maths stays in pixels.
Rebuild order:
- Table, header, cover and two static pages.
- One leaf with two faces; get the turn right.
- Prints, corners, captions, notes on all eight faces.
- Stack order, queueing, end-of-album limits.
- Keyboard, counter, aria-hidden faces.
- Scale to fit.
Details
- Palette
- Type
- Caveat · Lora
- Motion
- Rich motion
- Build
- Multi-step
- Tags
Studied
Icon MuseumLet the icons be the only colour. Give each one a shelf, a lift on hover, and a quiet label.
All sourcesOn the shelves