Transitions✨ Premium

Page Turn

A real page turn: page A swings −160° around its left edge, inside a 1,200 px perspective, with its shadow and the book's spine, and uncovers page B lying underneath; the second press brings it back. CSS + 0.9 KB of JavaScript, target page at once when the user reduces motion.

CSS3DPremium

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Transitions has 52 effects, including 9 free. Explore the category →

3 usage examples

Next page of a picture book — a reading app for preschool — Page Turn example 1

① Next page of a picture book — a reading app for preschool

WhenDuring story time, Mrs. Roussel taps "Turn the page" on the interactive whiteboard: page 4, where the wolf knocks at the door, swings on the binding and uncovers page 5.
WhyThe page swinging on its binding keeps the book gesture children know; the next page is already underneath.
SettingsThe container becomes the right-hand page of an open book (184 × 208 px), the left page is a fixed backdrop; cream paper, flat illustrations, text in a rounded handwritten font; large "Turn the page ›" button for the whiteboard.
Leafing through the guest book — a rural vacation rental's website — Page Turn example 2

② Leafing through the guest book — a rural vacation rental's website

WhenBefore booking the cottage for the fall break, a family leafs through the guest book: the August page, with the drawing by Jules, age 7, turns and lets them read September's notes.
WhyThe spine and the shadow give the notebook lying on the cottage table; going back brings the page over again.
SettingsRuled pages (a line every 18 px), green cover under the pages, wooden-slat table around it; messages in handwriting and signatures in brown; child's drawing (sun, house, hen, pool) on August's page.
Leafing through a civil register — a county archives website — Page Turn example 3

③ Leafing through a civil register — a county archives website

WhenA genealogist looking for Pierre Auguste Durand's birth, in March 1874, turns the register's page: folio 23 (record no. 37) swings over and uncovers folio 24, where record no. 38 is waiting.
WhyThe page turn respects the original object and the order of records in the digitized register.
SettingsYellowed paper in a radial gradient, records in chancery script with number and name in the margin, folio number at the bottom; the view counter on the right goes from 23 to 24 with the page; viewer toolbar below the book.

How it works

The .pageturn-container (240 × 200 px) holds the perspective (perspective: 1200px). Both .pageturn-pages are stacked inside with inset: 0, rounded only on the free side (4px 16px 16px 4px), with transform-origin: left center: they turn around their left edge, the binding. Page A is on top (z-index: 2) and casts a shadow to the right; page B waits underneath (z-index: 1). A 4 px .pageturn-spine strip, above both, draws the spine.

When .active toggles, A turns to rotateY(-160deg) in 0.9 s (cubic-bezier(.4, 0, .2, 1)). The perspective makes the free edge grow as it comes toward the eye, then the page goes edge-on, around −84°, and stops being drawn: backface-visibility: hidden hides its reverse, and the .active rule also sets it to visibility: hidden with a 0.32 s delay, the moment it is edge-on, for engines that do not apply the former (the test WebKit is one). B then appears in full. B itself does not move. Measured in Chromium 151: 250 ms after the click, page A only covers 177 px of width on screen (189 px in Firefox 153); at 1.1 s it is at −160° and the state still holds three seconds later. Checked by full-width screen capture in Chromium, Firefox and WebKit 26.5: B alone after the click, nothing left of the binding, then A again after the second click, which brings the page back on top (it becomes visible again at 0.32 s, edge-on).

Past 90°, the page is no longer drawn: the final angle (−160°) therefore does not change the end image, it sets the path, that is how fast the page lifts and then vanishes edge-on. The script adds or removes .active on click, Enter or Space on every .pageturn-container of the page, keeps aria-pressed up to date and sets aria-hidden on the covered page; everything is prefixed pageturn-, nothing is exposed on window. A simpler variant, which swings a colored panel, also exists in the category.

Accessibility

  • Reduced motion: under prefers-reduced-motion: reduce, the rotation is removed and the next page shows at once (measured at 60 ms in Chromium and Firefox: page A already at −160°).
  • Keyboard and screen readers: the container has role="button", tabindex="0" and aria-pressed; Enter and Space turn the page like a click (measured). The covered page gets aria-hidden="true": only the visible page is read. 2 px outline on keyboard focus.
  • Multi-page book: provide explicit "Previous page" and "Next page" buttons and announce the page number (aria-live="polite" region); a turning page does not say where you are.
  • Page text: it swings with the page and becomes unreadable as soon as it lifts; that is intended, but do not put information there that must be read during the gesture.

Browser compatibility

CSS (perspective, rotateY, transform-origin, backface-visibility with the -webkit- prefix, delayed visibility, inset) + 0.9 KB of JavaScript, no dependency. Measured in Chromium 151, Firefox 153 and WebKit 26.5: page turned, page B alone visible, checked by full-width screen capture (no mirrored reverse, WebKit included), state B held, return on the second click; keyboard and reduced motion measured in Chromium and Firefox. Each page turns on its own inside its parent's perspective, with no preserve-3d: the most widely supported form of 3D. inset sets the floor (Chrome 87, Firefox 66, Safari 14.1).

Chrome 87+✓ Full
Firefox 66+✓ Full
Safari 14.1+✓ Full
Edge 87+✓ Full
Mobile iOS 14.5+✓ Full
Android Chrome 87+✓ Full

Without JavaScript, page A stays on screen. In an engine that ignores backface-visibility, the visibility switch still hides the turned page: its mirrored reverse does not show left of the binding.

The code

HTML structure to paste into your page (the CSS + JS come with Premium):

index.html — structure
<div class="pageturn-stage">
  <div class="pageturn-container" role="button" tabindex="0" aria-pressed="false">
    <div class="pageturn-spine"></div>
    <div class="pageturn-page pageturn-page-b" aria-hidden="true">…</div>
    <div class="pageturn-page pageturn-page-a">…</div>
  </div>
  <span class="pageturn-hint">…</span>
</div>
Unlock the full code — $9.90/mo incl. tax

The full HTML + CSS + JS of Page Turn, ready to paste — plus all 840 effects. Or $299 once, lifetime access.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Final angle (CSS — rotateY(-160deg)) −160° The angle only changes the path: past 90°, the page is no longer drawn, so −120°, −160° and −175° give the same end image; the larger the angle, the sooner the page goes edge-on. For a barely lifted page that partly shows B, use −30° to −60° and remove visibility: hidden from the .active rule. For another angle beyond 90°, reset the visibility delay (0.32 s) to the moment the page goes edge-on.
Perspective (CSS — the container's perspective) 1,200 px 800 px makes the free edge more dramatic as it comes toward the eye; 2,000 px gives an almost flat, subtle turn.
Duration and curve (CSS — transition on .pageturn-page and .pageturn-page-a) 0.9 s, cubic-bezier(.4, 0, .2, 1) 0.6 s to leaf through quickly; cubic-bezier(.3, .1, .3, 1) lifts the page slowly then lets it drop. The visibility delay (0.32 s) follows the duration: set it to 0.21 s for 0.6 s.
Page shadow (CSS — box-shadow on .pageturn-page-a) rgba(0, 0, 0, .3) 5px 0 20px It gives the paper's thickness on the page below; raise the blur to 30 px for thick paper, remove it for a flat look.
Spine (CSS — .pageturn-spine) 4 px of 8% white A black-to-transparent gradient over 6 px mimics the hollow of an open book's binding.

FAQ

Stack all pages in the container, the first on top (decreasing z-index). Each click turns the top page still lying flat (an is-turned class applying rotateY(-160deg)); going back removes the class from the last turned page. Raise a page's z-index while it turns so it passes over the others.
Because it is no longer drawn past 90°: backface-visibility: hidden hides its reverse, and visibility: hidden, applied with a 0.32 s delay when the page goes edge-on, takes over in engines that ignore the former (in WebKit 26.5, without this switch, the reverse would stay visible, mirrored, left of the binding). That is what uncovers B with no extra JavaScript. To show a back (the left page of an open book), you need a back face inside the page and transform-style: preserve-3d, like a two-sided card, and remove the visibility switch.
Not with this code, which toggles between two states. For a drag, listen to pointermove, turn the horizontal movement into an angle (0 to −160°) written to style.transform with no transition, then on release add or remove .active depending on whether the page went past −80°.