Back to CSS Page Transitions Book Page Turn CSS + JS
Share
HTML
<div class="scene">
  <div class="book" id="book">

    <!-- Spread 0 -->
    <div class="spread active" id="s0">
      <div class="leaf leaf-left s0-left">
        <div style="position:relative;z-index:1;text-align:center;padding:48px 32px;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;">
          <div class="cover-ornament">✦ ✦ ✦</div>
          <div class="cover-title">The Invisible <em>Library</em></div>
          <div class="cover-subtitle">A Novel in Four Books</div>
          <div class="cover-rule"></div>
          <div class="cover-author">Marguerite Vane</div>
          <div class="cover-year">MDCCCXCII</div>
        </div>
      </div>
      <div class="leaf leaf-right">
        <div class="s0-right-inner">
          <div class="title-page-label">First Edition · London</div>
          <div class="title-page-title">The Invisible<br><em>Library</em></div>
          <div class="title-page-sub">Being a Complete & Faithful Account of the Travels of Miss Helena Voss Through the Rooms of Memory</div>
          <div class="title-page-meta">
            Printed by Alderman & Sons · Paternoster Row<br>
            Set in 12pt Garamond · Hand-bound in vellum<br>
            Edition of 250 copies
          </div>
          <div class="page-num">ii</div>
        </div>
      </div>
    </div>

    <!-- Spread 1 -->
    <div class="spread" id="s1">
      <div class="leaf leaf-left">
        <div class="page-inner">
          <div class="running-head">The Invisible Library</div>
          <div class="rule"></div>
          <div class="chapter-heading">
            <span class="chapter-num">Chapter the First</span>
            <div class="chapter-title">In Which a Letter<br>Arrives at Dusk</div>
          </div>
          <div class="rule"></div>
          <div class="body-text">
            <span class="dropcap">T</span>he letter came on a Thursday, which Helena had always considered an unreliable day — too far from the comfort of Wednesday, not close enough to the relief of Friday. It arrived not by post but by the hand of a boy no older than ten, who pressed it into her palm without a word and turned and ran before she could ask him anything sensible.
            <br><br>
            It was addressed in a hand she did not recognise, though the ink had an odd quality: too dark for iron gall, too warm for carbon, as if whoever had written it had dipped their pen in something between midnight and rust.
          </div>
          <div class="page-num">3</div>
        </div>
      </div>
      <div class="leaf leaf-right">
        <div class="page-inner">
          <div class="running-head">Chapter the First</div>
          <div class="rule"></div>
          <div class="body-text">
            She broke the seal — a figure-eight pressed in wax the colour of dried blood — and read the four lines inside twice, then a third time, in the failing light of her sitting room window. They said only this:
            <br><br>
            <em>The library is open. You were expected. Come before the candles go out, which they will at midnight. Bring nothing that matters to you, for it will not return as it was.</em>
            <br><br>
            Helena set the letter on her writing desk and looked at it for a long time. Then she put on her coat, took her lamp, and went out into the street, which had begun, very quietly, to rain.
            <br><br>
            She did not bring anything that mattered to her. She was not certain she had anything left that did.
          </div>
          <div class="marginal-note">cf. Borges, Labyrinths, pp.51–58</div>
          <div class="page-num">4</div>
        </div>
      </div>
    </div>

    <!-- Spread 2 -->
    <div class="spread" id="s2">
      <div class="leaf leaf-left">
        <div class="page-inner">
          <div class="running-head">The Invisible Library</div>
          <div class="rule"></div>
          <div class="poem-title">An Interlude: The Catalogue</div>
          <div class="poem-body">
            <p>Here are the books that were never written,<br>shelved between the ones that were.<br>Here are the thoughts that stopped mid-sentence<br>and dried before they could occur.</p>
            <p>The index to a lost cartography.<br>A grammar of a wordless tongue.<br>The history of a city founded<br>in a country that has come undone.</p>
            <p>All present. All accounted for.<br>All alphabetised by what they lack.<br>The library collects the absent<br>as faithfully as what came back.</p>
          </div>
          <div class="rule" style="margin-top:auto"></div>
          <div class="page-num">47</div>
        </div>
      </div>
      <div class="leaf leaf-right">
        <div class="page-inner">
          <div class="running-head">An Interlude</div>
          <div class="rule"></div>
          <div style="height:60px"></div>
          <div class="poem-body">
            <p>She read the catalogue by lamplight,<br>running her finger down the spines<br>of volumes that existed only<br>as an entry between the lines.</p>
            <p><em>Dreams of the Paralysed Mathematician.</em><br><em>A Psalter Written Left to Right.</em><br><em>Everything Your Mother Almost Said</em><br><em>on the particular Tuesday night.</em></p>
            <p>She closed it carefully. She set it back.<br>She understood, now, what this place required:<br>not the books she'd brought, nor those she'd read,<br>but the ones she'd always half-desired.</p>
          </div>
          <div class="page-num">48</div>
        </div>
      </div>
    </div>

    <!-- Spread 3 -->
    <div class="spread" id="s3">
      <div class="leaf leaf-left">
        <div class="page-inner">
          <div class="running-head">The Invisible Library</div>
          <div class="rule"></div>
          <div class="body-text">
            <span class="dropcap">S</span>he emerged at dawn into a street she did not recognise, though she had lived in the city all her life. The library was gone — or rather, it had returned to being the unremarkable stone building on the corner of Cloth Fair that had stood there, according to the Parish records, since 1742.
            <br><br>
            Her notebook, which she had not brought, was in her hand. It was full.
            <br><br>
            She walked home, reading as she went, which is the most dangerous form of walking, and by the time she reached her door she had understood that the library had not given her the books inside it. It had given her back the ones she'd lost — the ones she had been, without knowing it, writing all along.
          </div>
          <div class="rule" style="margin-top:auto"></div>
          <div style="text-align:center;font-style:italic;font-size:12px;color:rgba(28,16,8,0.4);margin-top:12px">Finis</div>
          <div class="page-num">214</div>
        </div>
      </div>
      <div class="leaf leaf-right">
        <div class="page-inner">
          <div class="colophon">
            <div class="colophon-mark">…</div>
            <div class="colophon-text">
              This book was composed and set by hand at the Alderman Press, London, in the autumn of 1892. The typeface is Garamond, cut in the manner of Granjon. The paper is Arches mould-made.
            </div>
            <div class="colophon-pub">Alderman &amp; Sons · Paternoster Row · London</div>
          </div>
          <div class="page-num">215</div>
        </div>
      </div>
    </div>

    <!-- Turning page element -->
    <div class="page-turn-container" id="turner">
      <div class="turn-front" id="turnFront">
        <div class="fold-shadow"></div>
      </div>
      <div class="turn-back" id="turnBack"></div>
    </div>

  </div>
</div>

<!-- Nav -->
<div class="book-nav">
  <button class="bnav-btn" id="prevBtn">‹</button>
  <div class="page-indicator" id="pageInd">pp. i–ii</div>
  <button class="bnav-btn" id="nextBtn">›</button>
</div>
CSS
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{
  --cream:#f4edd8;
  --parchment:#e8dbb8;
  --burg:#4a1020;
  --burg-light:#7a2838;
  --ink:#1c1008;
  --gold:#b8922a;
}
html,body{height:100%;overflow:hidden;background:#2a1a10;font-family:'Crimson Pro',serif;}

/* Scene */
.scene{
  position:fixed;inset:0;
  perspective:2000px;
  perspective-origin:50% 40%;
  display:flex;align-items:center;justify-content:center;
}

.book{
  width:min(900px,95vw);
  height:min(600px,90vh);
  position:relative;
  transform-style:preserve-3d;
  transform:rotateX(6deg) rotateY(-2deg);
  box-shadow:
    0 60px 120px rgba(0,0,0,0.6),
    0 20px 40px rgba(0,0,0,0.4);
}

/* Book pages sit side by side */
.book-spread{
  position:absolute;inset:0;
  display:grid;
  grid-template-columns:1fr 1fr;
}

/* Spine shadow */
.book-spread::before{
  content:'';
  position:absolute;
  top:0;bottom:0;
  left:50%;width:4px;
  transform:translateX(-50%);
  background:linear-gradient(90deg,rgba(0,0,0,0.3),rgba(0,0,0,0.1),rgba(255,255,255,0.08),rgba(0,0,0,0.1),rgba(0,0,0,0.3));
  z-index:100;
}

/* Page spread */
.spread{
  position:absolute;inset:0;
  display:grid;grid-template-columns:1fr 1fr;
  transform-style:preserve-3d;
  visibility:hidden;
}
.spread.active{visibility:visible;}

/* Each leaf is half the spread */
.leaf{
  position:relative;
  overflow:hidden;
}

.leaf-left{background:var(--cream);}
.leaf-right{background:var(--parchment);}

/* Thin paper texture via gradient */
.leaf::before{
  content:'';position:absolute;inset:0;
  background:
    repeating-linear-gradient(0deg,transparent,transparent 28px,rgba(0,0,0,0.018) 28px,rgba(0,0,0,0.018) 29px),
    linear-gradient(180deg,rgba(255,255,255,0.4) 0%,transparent 20%,transparent 80%,rgba(0,0,0,0.04) 100%);
  pointer-events:none;z-index:10;
}

/* Turning leaf */
.page-turn-container{
  position:absolute;
  top:0;bottom:0;
  right:0;left:50%;
  transform-style:preserve-3d;
  transform-origin:left center;
  transform:rotateY(0deg);
  z-index:500;
  pointer-events:none;
}

.turn-front,.turn-back{
  position:absolute;inset:0;
  backface-visibility:hidden;
  overflow:hidden;
}

.turn-back{
  transform:rotateY(180deg);
  background:var(--parchment);
}

.turn-back::before{
  content:'';position:absolute;inset:0;
  background:linear-gradient(90deg,rgba(0,0,0,0.12),transparent 30%);
  pointer-events:none;z-index:10;
}

/* Shadow that rides the fold */
.fold-shadow{
  position:absolute;
  top:0;bottom:0;left:0;width:40px;
  background:linear-gradient(90deg,rgba(0,0,0,0.25),transparent);
  pointer-events:none;z-index:20;
}

/* SPREAD CONTENT STYLES */
.page-inner{
  padding:48px 44px;
  height:100%;
  display:flex;flex-direction:column;
  position:relative;
  overflow:hidden;
}

/* Page number */
.page-num{
  position:absolute;bottom:20px;
  font-size:9px;letter-spacing:0.15em;
  color:rgba(28,16,8,0.3);
  font-family:'Crimson Pro',serif;
  font-style:italic;
}
.leaf-left .page-num{left:44px;}
.leaf-right .page-num{right:44px;}

/* Decorative rule */
.rule{
  width:100%;height:1px;
  background:linear-gradient(90deg,transparent,var(--gold),transparent);
  margin:12px 0;
}

/* Drop cap */
.dropcap{
  float:left;
  font-family:'IM Fell English',serif;
  font-size:72px;line-height:0.85;
  margin-right:6px;margin-top:8px;
  color:var(--burg);
}

.running-head{
  font-size:8px;letter-spacing:0.3em;text-transform:uppercase;
  color:rgba(28,16,8,0.4);
  text-align:center;margin-bottom:8px;
  font-family:'Crimson Pro',serif;
}

/* Spread 0 — cover / title page */
.s0-left{
  align-items:center;justify-content:center;
  text-align:center;
  background:var(--burg);
  color:var(--cream);
}
.s0-left::before{display:none;}
.s0-left::after{
  content:'';position:absolute;inset:16px;
  border:1px solid rgba(184,146,42,0.3);
  pointer-events:none;
}

.cover-ornament{
  font-size:28px;color:rgba(184,146,42,0.5);
  letter-spacing:0.2em;margin-bottom:20px;
}
.cover-title{
  font-family:'IM Fell English',serif;
  font-size:36px;font-weight:400;
  line-height:1.2;color:var(--parchment);
  margin-bottom:8px;
}
.cover-title em{font-style:italic;color:var(--gold);}
.cover-subtitle{
  font-size:11px;letter-spacing:0.2em;text-transform:uppercase;
  color:rgba(232,219,184,0.45);margin-bottom:32px;
}
.cover-rule{
  width:60px;height:1px;background:var(--gold);
  margin:0 auto 28px;opacity:0.5;
}
.cover-author{
  font-size:14px;letter-spacing:0.12em;
  color:rgba(232,219,184,0.6);
  font-style:italic;
}
.cover-year{
  font-size:9px;letter-spacing:0.2em;
  color:rgba(232,219,184,0.3);
  margin-top:16px;
}

.s0-right{
  background:var(--cream);
}
.s0-right-inner{
  padding:48px 44px;height:100%;
  display:flex;flex-direction:column;
  justify-content:center;
  position:relative;
}

.title-page-label{
  font-size:8px;letter-spacing:0.3em;text-transform:uppercase;
  color:rgba(28,16,8,0.3);margin-bottom:28px;
}
.title-page-title{
  font-family:'IM Fell English',serif;
  font-size:44px;line-height:1.1;
  color:var(--ink);margin-bottom:12px;
}
.title-page-title em{font-style:italic;color:var(--burg);}
.title-page-sub{
  font-size:14px;font-style:italic;
  color:rgba(28,16,8,0.5);margin-bottom:32px;
  line-height:1.6;
}
.title-page-meta{
  font-size:10px;letter-spacing:0.08em;
  color:rgba(28,16,8,0.3);line-height:2;
  border-top:1px solid rgba(28,16,8,0.1);
  padding-top:20px;margin-top:auto;
}

/* Spread 1 — chapter */
.chapter-heading{
  text-align:center;margin-bottom:24px;
}
.chapter-num{
  font-size:9px;letter-spacing:0.35em;text-transform:uppercase;
  color:var(--burg);margin-bottom:12px;display:block;
}
.chapter-title{
  font-family:'IM Fell English',serif;
  font-size:30px;color:var(--ink);line-height:1.2;
}

.body-text{
  font-family:'EB Garamond',serif;
  font-size:13.5px;line-height:1.85;
  color:rgba(28,16,8,0.8);
  text-align:justify;
  hyphens:auto;
}

/* Spread 2 — poetry */
.poem-title{
  font-family:'IM Fell English',serif;
  font-size:22px;font-style:italic;
  color:var(--burg);margin-bottom:20px;
  padding-bottom:16px;
  border-bottom:1px solid rgba(74,16,32,0.15);
}
.poem-body{
  font-family:'EB Garamond',serif;
  font-size:14px;line-height:2.1;
  color:rgba(28,16,8,0.75);
  font-style:italic;
  padding-left:20px;
}
.poem-body p{margin-bottom:16px;}

.marginal-note{
  position:absolute;right:12px;top:30%;
  width:44px;
  font-size:7px;line-height:1.6;
  color:rgba(28,16,8,0.35);
  text-align:center;
  transform:rotate(90deg);
  transform-origin:center;
  letter-spacing:0.1em;
  font-style:italic;
}

/* Spread 3 — colophon */
.colophon{
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  text-align:center;height:100%;
}
.colophon-mark{
  font-size:48px;color:var(--burg);opacity:0.3;
  margin-bottom:24px;
}
.colophon-text{
  font-size:11px;line-height:1.9;
  color:rgba(28,16,8,0.4);
  font-style:italic;max-width:220px;
  border-top:1px solid rgba(28,16,8,0.1);
  border-bottom:1px solid rgba(28,16,8,0.1);
  padding:20px 0;margin-bottom:20px;
}
.colophon-pub{
  font-size:9px;letter-spacing:0.2em;
  color:rgba(28,16,8,0.25);text-transform:uppercase;
}

/* Nav arrows */
.book-nav{
  position:fixed;bottom:40px;left:50%;transform:translateX(-50%);
  z-index:1000;display:flex;align-items:center;gap:16px;
}
.bnav-btn{
  width:44px;height:44px;
  border:1px solid rgba(232,219,184,0.25);
  background:rgba(28,16,8,0.7);
  color:rgba(232,219,184,0.6);
  font-size:20px;display:flex;align-items:center;justify-content:center;
  cursor:pointer;backdrop-filter:blur(12px);
  transition:all .2s;font-family:serif;
}
.bnav-btn:hover{border-color:var(--gold);color:var(--gold);}

.page-indicator{
  font-family:'Crimson Pro',serif;font-style:italic;
  font-size:12px;color:rgba(232,219,184,0.4);
  letter-spacing:0.08em;
  min-width:80px;text-align:center;
}

/* Ambient bg */
body::before{
  content:'';position:fixed;inset:0;
  background:
    radial-gradient(ellipse 80% 60% at 50% 50%,#3a2015,#1a0e08);
  z-index:-1;
}
JS
const spreads=document.querySelectorAll('.spread');
const turner=document.getElementById('turner');
const turnFront=document.getElementById('turnFront');
const turnBack=document.getElementById('turnBack');
const pageInd=document.getElementById('pageInd');
const labels=['pp. i–ii','pp. 3–4','pp. 47–48','pp. 214–215'];
let cur=0,total=spreads.length,busy=false;

function cloneSpreadContent(spreadEl,side){
  const leaves=spreadEl.querySelectorAll('.leaf');
  const idx=side==='left'?0:1;
  if(!leaves[idx])return;
  const clone=leaves[idx].cloneNode(true);
  clone.style.cssText='position:absolute;inset:0;margin:0;';
  return clone;
}

function turnPage(direction){
  if(busy)return;
  const next=direction>0?cur+1:cur-1;
  if(next<0||next>=total)return;
  busy=true;

  const fromSpread=spreads[cur];
  const toSpread=spreads[next];

  turnFront.innerHTML='';
  const frontClone=cloneSpreadContent(fromSpread,'right');
  if(frontClone)turnFront.appendChild(frontClone);
  turnFront.appendChild(Object.assign(document.createElement('div'),{className:'fold-shadow'}));

  turnBack.innerHTML='';
  const backClone=cloneSpreadContent(toSpread,'right');
  if(backClone){
    backClone.style.transform='scaleX(-1)';
    turnBack.appendChild(backClone);
  }

  turner.style.transition='none';
  turner.style.transform=direction>0?'rotateY(0deg)':'rotateY(-180deg)';
  turner.style.display='block';

  fromSpread.classList.add('active');
  toSpread.classList.remove('active');

  requestAnimationFrame(()=>{
    requestAnimationFrame(()=>{
      turner.style.transition='transform 900ms cubic-bezier(0.6,0,0.4,1)';
      turner.style.transform=direction>0?'rotateY(-180deg)':'rotateY(0deg)';
    });
  });

  setTimeout(()=>{
    fromSpread.classList.remove('active');
    toSpread.classList.add('active');
  },450);

  setTimeout(()=>{
    turner.style.display='none';
    cur=next;
    pageInd.textContent=labels[cur];
    busy=false;
  },920);
}

document.getElementById('nextBtn').onclick=()=>turnPage(1);
document.getElementById('prevBtn').onclick=()=>turnPage(-1);

document.addEventListener('keydown',e=>{
  if(e.key==='ArrowRight')turnPage(1);
  if(e.key==='ArrowLeft')turnPage(-1);
});

let wt=0;
window.addEventListener('wheel',e=>{
  const now=Date.now();if(now-wt<1100)return;wt=now;
  turnPage(e.deltaY>0?1:-1);
},{passive:true});