9 CSS 3D Designs04 / 09
Page-Turn Flipbook
A physical book with a dark leather cover, cream parchment spreads, and pages that arc through a real 0° → -180° rotateY transform with the hinge anchored at transform-origin: left center. Each spread carries genuine editorial typography — pull quotes, column rules, drop caps, colored section pages — and the gutter shadow on each face simulates the dip near the spine. Click the right half of the book to advance, the left half to go back, or use the keyboard arrows / nav buttons. Playfair Display + EB Garamond. Best for digital magazines, restaurant menus, lookbooks, interactive storytelling, brand annual reports.
Published
The code
<section class="cd-flb" aria-label="Page-turn flipbook demo">
<div class="card">
<div class="book-scene">
<div class="book" data-cd-flb-book>
<div class="book-shadow" aria-hidden="true"></div>
<div class="left-cover" data-cd-flb-cover>
<div class="cover-art">
<div class="cover-eyebrow">FORMA STUDIO</div>
<div class="cover-rule"></div>
<div class="cover-title">FORMA<span class="cover-vol">Vol. VI</span></div>
<div class="cover-rule"></div>
<div class="cover-sub">Design · Architecture · Thought</div>
</div>
</div>
<div class="page" data-page="0">
<div class="page-front paper-warm">
<div class="pg pg-warm">
<div class="spread-label">Chapter One</div>
<div class="spread-title sm">The Weight<br />of Silence</div>
<div class="spread-rule"></div>
<p class="spread-body">There is a moment before speech when the world holds its breath. Architecture inhabits this pause — not the buildings themselves, but the spaces they carve from air.</p>
<p class="spread-body mt">The corridor narrows, draws the eye forward. Light slants from an unseen source. To walk through is to be composed by the building.</p>
<span class="pg-num right">1</span>
<span class="spread-big-num">I</span>
</div>
</div>
<div class="page-back paper-cool">
<div class="pg pg-cool">
<div class="spread-label">Essay II</div>
<div class="spread-title xs">Form<br />Without<br />Apology</div>
<div class="spread-rule alt"></div>
<p class="spread-body alt">To design is to make a claim about the world as it should be — not as it is. The blueprint precedes the building, and in that gap lives everything called imagination.</p>
<span class="pg-num left">2</span>
</div>
</div>
</div>
<div class="page" data-page="1">
<div class="page-front paper-sage">
<div class="pg pg-sage">
<div class="spread-label">Visual Field</div>
<div class="spread-img sage-img">
<div class="circle-outer">
<div class="circle-inner"></div>
</div>
</div>
<div class="spread-pullquote sage">"Growth is not linear. It is fractal — repeating at every scale."</div>
<p class="spread-body sage-body">The forest does not plan its canopy. Each tree reaches for light by the same simple rule: grow toward the sun. The emergent whole is unplanned and irreducible.</p>
<span class="pg-num right sage">3</span>
</div>
</div>
<div class="page-back paper-rose">
<div class="pg pg-rose">
<div class="spread-label">Interlude</div>
<div class="spread-title sm rose">Red<br />Notation</div>
<div class="spread-rule rose"></div>
<p class="spread-body rose-body">Color arrives before language. Red is felt in the chest before named by the tongue. The ancient painters knew this — their ochre hands pressed flat on cave walls were not decoration. They were declaration.</p>
<span class="pg-num left rose">4</span>
</div>
</div>
</div>
<div class="page" data-page="2">
<div class="page-front paper-warm">
<div class="pg pg-warm">
<div class="spread-label">Final Study</div>
<div class="spread-title sm">Endings<br />as Form</div>
<div class="spread-rule"></div>
<p class="spread-body">Every designed thing must eventually end — the page turn, the last note, the exit from the building. The best designs make their endings feel inevitable, not arbitrary.</p>
<div class="spread-pullquote warm">"The final line is the first thing written."</div>
<span class="pg-num right">5</span>
</div>
</div>
<div class="page-back paper-ink">
<div class="pg pg-ink">
<div class="colophon-label">COLOPHON</div>
<div class="colophon-title">FORMA</div>
<div class="colophon-sub">A Journal of Design Thought</div>
<p class="colophon-body">Issue VI · Printed on FSC-certified stock · Typography set in Playfair Display and EB Garamond · All rights reserved ©</p>
<span class="pg-num left ink">6</span>
</div>
</div>
</div>
</div>
<div class="nav">
<button class="nav-btn" type="button" data-cd-flb-prev disabled>← Prev</button>
<span class="page-counter" data-cd-flb-counter>Spread 1 of 4</span>
<button class="nav-btn" type="button" data-cd-flb-next>Next →</button>
</div>
</div>
</div>
</section><section class="cd-flb" aria-label="Page-turn flipbook demo">
<div class="card">
<div class="book-scene">
<div class="book" data-cd-flb-book>
<div class="book-shadow" aria-hidden="true"></div>
<div class="left-cover" data-cd-flb-cover>
<div class="cover-art">
<div class="cover-eyebrow">FORMA STUDIO</div>
<div class="cover-rule"></div>
<div class="cover-title">FORMA<span class="cover-vol">Vol. VI</span></div>
<div class="cover-rule"></div>
<div class="cover-sub">Design · Architecture · Thought</div>
</div>
</div>
<div class="page" data-page="0">
<div class="page-front paper-warm">
<div class="pg pg-warm">
<div class="spread-label">Chapter One</div>
<div class="spread-title sm">The Weight<br />of Silence</div>
<div class="spread-rule"></div>
<p class="spread-body">There is a moment before speech when the world holds its breath. Architecture inhabits this pause — not the buildings themselves, but the spaces they carve from air.</p>
<p class="spread-body mt">The corridor narrows, draws the eye forward. Light slants from an unseen source. To walk through is to be composed by the building.</p>
<span class="pg-num right">1</span>
<span class="spread-big-num">I</span>
</div>
</div>
<div class="page-back paper-cool">
<div class="pg pg-cool">
<div class="spread-label">Essay II</div>
<div class="spread-title xs">Form<br />Without<br />Apology</div>
<div class="spread-rule alt"></div>
<p class="spread-body alt">To design is to make a claim about the world as it should be — not as it is. The blueprint precedes the building, and in that gap lives everything called imagination.</p>
<span class="pg-num left">2</span>
</div>
</div>
</div>
<div class="page" data-page="1">
<div class="page-front paper-sage">
<div class="pg pg-sage">
<div class="spread-label">Visual Field</div>
<div class="spread-img sage-img">
<div class="circle-outer">
<div class="circle-inner"></div>
</div>
</div>
<div class="spread-pullquote sage">"Growth is not linear. It is fractal — repeating at every scale."</div>
<p class="spread-body sage-body">The forest does not plan its canopy. Each tree reaches for light by the same simple rule: grow toward the sun. The emergent whole is unplanned and irreducible.</p>
<span class="pg-num right sage">3</span>
</div>
</div>
<div class="page-back paper-rose">
<div class="pg pg-rose">
<div class="spread-label">Interlude</div>
<div class="spread-title sm rose">Red<br />Notation</div>
<div class="spread-rule rose"></div>
<p class="spread-body rose-body">Color arrives before language. Red is felt in the chest before named by the tongue. The ancient painters knew this — their ochre hands pressed flat on cave walls were not decoration. They were declaration.</p>
<span class="pg-num left rose">4</span>
</div>
</div>
</div>
<div class="page" data-page="2">
<div class="page-front paper-warm">
<div class="pg pg-warm">
<div class="spread-label">Final Study</div>
<div class="spread-title sm">Endings<br />as Form</div>
<div class="spread-rule"></div>
<p class="spread-body">Every designed thing must eventually end — the page turn, the last note, the exit from the building. The best designs make their endings feel inevitable, not arbitrary.</p>
<div class="spread-pullquote warm">"The final line is the first thing written."</div>
<span class="pg-num right">5</span>
</div>
</div>
<div class="page-back paper-ink">
<div class="pg pg-ink">
<div class="colophon-label">COLOPHON</div>
<div class="colophon-title">FORMA</div>
<div class="colophon-sub">A Journal of Design Thought</div>
<p class="colophon-body">Issue VI · Printed on FSC-certified stock · Typography set in Playfair Display and EB Garamond · All rights reserved ©</p>
<span class="pg-num left ink">6</span>
</div>
</div>
</div>
</div>
<div class="nav">
<button class="nav-btn" type="button" data-cd-flb-prev disabled>← Prev</button>
<span class="page-counter" data-cd-flb-counter>Spread 1 of 4</span>
<button class="nav-btn" type="button" data-cd-flb-next>Next →</button>
</div>
</div>
</div>
</section>/* ─── 04 Page-Turn Flipbook — editorial book ───────────────── */
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;0,900;1,400;1,700&family=EB+Garamond:ital,wght@0,400;0,500;1,400;1,500&display=swap');
.cd-flb {
--cd-flb-bg: #1c1410;
position: relative;
width: 100%;
min-height: 600px;
background: var(--cd-flb-bg);
font-family: 'EB Garamond', Georgia, serif;
overflow: hidden;
perspective: 2400px;
box-sizing: border-box;
}
.cd-flb *,
.cd-flb *::before,
.cd-flb *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cd-flb .card {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 28px 16px;
}
.cd-flb .card::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(ellipse 60% 50% at 50% 40%, rgba(180,120,40,0.06) 0%, transparent 70%);
pointer-events: none;
}
.cd-flb .book-scene {
transform-style: preserve-3d;
transform: rotateX(5deg) rotateY(-4deg);
transition: transform 0.6s ease;
position: relative;
z-index: 1;
}
.cd-flb .book-scene:hover {
transform: rotateX(4deg) rotateY(-2deg);
}
.cd-flb .book {
width: 560px;
height: 360px;
position: relative;
transform-style: preserve-3d;
}
.cd-flb .book::before {
content: '';
position: absolute;
left: 50%;
top: 0;
bottom: 0;
width: 12px;
transform: translateX(-50%) translateZ(1px);
background: linear-gradient(to right, rgba(0,0,0,0.4) 0%, rgba(0,0,0,0.1) 30%, rgba(0,0,0,0.1) 70%, rgba(0,0,0,0.4) 100%);
z-index: 20;
pointer-events: none;
}
.cd-flb .page {
position: absolute;
width: 280px;
height: 360px;
top: 0;
left: 280px;
transform-origin: left center;
transform-style: preserve-3d;
transition: transform 1.1s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.cd-flb .page.flipped {
transform: rotateY(-180deg);
}
.cd-flb .page-front,
.cd-flb .page-back {
position: absolute;
inset: 0;
backface-visibility: hidden;
overflow: hidden;
}
.cd-flb .page-back {
transform: rotateY(180deg);
}
.cd-flb .left-cover {
position: absolute;
left: 0;
top: 0;
width: 280px;
height: 360px;
z-index: 0;
border-radius: 4px 0 0 4px;
overflow: hidden;
}
.cd-flb .pg {
width: 100%;
height: 100%;
padding: 30px 26px;
display: flex;
flex-direction: column;
position: relative;
}
.cd-flb .pg-warm {
color: #2a1f14;
}
.cd-flb .pg-cool {
color: #2a2432;
}
.cd-flb .pg-rose {
color: #2a1418;
}
.cd-flb .pg-sage {
color: #1a2a1e;
}
.cd-flb .pg-ink {
color: #e8d8c0;
}
.cd-flb .pg-num {
font-family: 'EB Garamond', serif;
font-size: 10px;
letter-spacing: 2px;
position: absolute;
bottom: 16px;
}
.cd-flb .pg-num.right {
right: 22px;
color: #8a6a4a;
}
.cd-flb .pg-num.left {
left: 22px;
color: #7a6898;
}
.cd-flb .pg-num.rose {
color: #8a4050;
}
.cd-flb .pg-num.sage {
color: #4a7a50;
}
.cd-flb .pg-num.ink {
color: rgba(200,180,140,0.4);
}
.cd-flb .page-front::after,
.cd-flb .page-back::after {
content: '';
position: absolute;
top: 0;
bottom: 0;
width: 40px;
pointer-events: none;
}
.cd-flb .page-front::after {
right: 0;
background: linear-gradient(to right, transparent, rgba(0,0,0,0.14));
}
.cd-flb .page-back::after {
left: 0;
background: linear-gradient(to left, transparent, rgba(0,0,0,0.14));
}
.cd-flb .left-cover::after {
content: '';
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 30px;
background: linear-gradient(to right, transparent, rgba(0,0,0,0.2));
}
.cd-flb .paper-warm {
background: #f5ece0;
}
.cd-flb .paper-cool {
background: #ede8f0;
}
.cd-flb .paper-rose {
background: #f5e8e8;
}
.cd-flb .paper-sage {
background: #e8f0e8;
}
.cd-flb .paper-ink {
background: #1a1510;
}
.cd-flb .spread-label {
font-family: 'EB Garamond', serif;
font-size: 9px;
letter-spacing: 4px;
text-transform: uppercase;
opacity: 0.4;
margin-bottom: 16px;
}
.cd-flb .spread-title {
font-family: 'Playfair Display', serif;
font-weight: 900;
line-height: 1.1;
margin-bottom: 14px;
}
.cd-flb .spread-title.sm {
font-size: 26px;
}
.cd-flb .spread-title.xs {
font-size: 22px;
}
.cd-flb .spread-title.rose {
color: #5a1828;
}
.cd-flb .spread-body {
font-family: 'EB Garamond', serif;
font-size: 13px;
line-height: 1.78;
opacity: 0.75;
}
.cd-flb .spread-body.mt {
margin-top: 10px;
}
.cd-flb .spread-body.alt {
color: #3a2a50;
}
.cd-flb .spread-body.rose-body {
color: #3a1820;
}
.cd-flb .spread-body.sage-body {
color: #2a3a26;
}
.cd-flb .spread-pullquote {
font-family: 'Playfair Display', serif;
font-style: italic;
font-size: 16px;
line-height: 1.5;
border-left: 2px solid currentColor;
padding-left: 14px;
margin: 16px 0;
opacity: 0.8;
}
.cd-flb .spread-pullquote.warm {
border-color: #8a6a4a;
color: #4a3424;
}
.cd-flb .spread-pullquote.sage {
border-color: #3a6a40;
color: #2a4a2e;
}
.cd-flb .spread-rule {
width: 40px;
height: 1px;
background: currentColor;
opacity: 0.3;
margin: 14px 0;
}
.cd-flb .spread-rule.alt {
background: #4a3868;
}
.cd-flb .spread-rule.rose {
background: #8a3040;
}
.cd-flb .spread-img {
width: 100%;
height: 100px;
border-radius: 4px;
margin: 14px 0;
overflow: hidden;
flex-shrink: 0;
}
.cd-flb .sage-img {
background: linear-gradient(135deg, #2a4a2e, #1a3020);
display: flex;
align-items: center;
justify-content: center;
}
.cd-flb .circle-outer {
width: 64px;
height: 64px;
border: 2px solid rgba(120,200,120,0.4);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.cd-flb .circle-inner {
width: 32px;
height: 32px;
background: rgba(120,200,120,0.2);
border-radius: 50%;
}
.cd-flb .spread-big-num {
font-family: 'Playfair Display', serif;
font-size: 80px;
font-weight: 900;
line-height: 0.9;
opacity: 0.07;
position: absolute;
right: 16px;
bottom: 30px;
}
/* Cover art */
.cd-flb .cover-art {
width: 100%;
height: 100%;
background: linear-gradient(148deg, #1a0f08, #0e0a06);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 26px;
border-radius: 4px 0 0 4px;
position: relative;
overflow: hidden;
}
.cd-flb .cover-art::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(ellipse 70% 60% at 50% 40%, rgba(180,120,40,0.12) 0%, transparent 70%);
}
.cd-flb .cover-eyebrow {
font-family: 'EB Garamond', serif;
font-size: 9px;
letter-spacing: 5px;
color: rgba(200,160,80,0.55);
margin-bottom: 26px;
z-index: 1;
}
.cd-flb .cover-rule {
width: 56px;
height: 1px;
background: rgba(200,160,80,0.35);
margin: 0 0 26px;
z-index: 1;
}
.cd-flb .cover-title {
font-family: 'Playfair Display', serif;
font-size: 34px;
font-weight: 900;
color: #f0e4cc;
text-align: center;
line-height: 1.1;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
}
.cd-flb .cover-vol {
font-style: italic;
font-weight: 400;
font-size: 20px;
opacity: 0.7;
margin-top: 6px;
}
.cd-flb .cover-rule:nth-of-type(2),
.cd-flb .cover-title + .cover-rule {
margin: 26px 0;
}
.cd-flb .cover-sub {
font-family: 'EB Garamond', serif;
font-style: italic;
font-size: 11px;
color: rgba(200,160,80,0.55);
letter-spacing: 1px;
z-index: 1;
}
.cd-flb .colophon-label {
font-family: 'EB Garamond', serif;
font-size: 9px;
letter-spacing: 4px;
opacity: 0.35;
margin-bottom: 18px;
}
.cd-flb .colophon-title {
font-family: 'Playfair Display', serif;
font-size: 18px;
font-weight: 700;
opacity: 0.9;
margin-bottom: 8px;
}
.cd-flb .colophon-sub {
font-family: 'Playfair Display', serif;
font-style: italic;
font-size: 12px;
opacity: 0.5;
margin-bottom: 22px;
}
.cd-flb .colophon-body {
font-family: 'EB Garamond', serif;
font-size: 11px;
line-height: 1.8;
opacity: 0.45;
}
.cd-flb .nav {
display: flex;
align-items: center;
gap: 18px;
margin-top: 26px;
position: relative;
z-index: 100;
}
.cd-flb .nav-btn {
display: flex;
align-items: center;
gap: 8px;
font-family: 'EB Garamond', serif;
font-size: 12px;
letter-spacing: 2px;
color: rgba(200,170,120,0.7);
background: none;
border: none;
cursor: pointer;
padding: 9px 18px;
border-radius: 4px;
transition: color 0.2s, background 0.2s;
}
.cd-flb .nav-btn:hover {
color: rgba(220,190,140,1);
background: rgba(200,170,120,0.08);
}
.cd-flb .nav-btn:disabled {
opacity: 0.3;
cursor: default;
}
.cd-flb .page-counter {
font-family: 'EB Garamond', serif;
font-size: 12px;
color: rgba(200,170,120,0.45);
letter-spacing: 2px;
min-width: 80px;
text-align: center;
}
.cd-flb .book-shadow {
position: absolute;
bottom: -18px;
left: 10%;
right: 10%;
height: 28px;
background: radial-gradient(ellipse at center, rgba(0,0,0,0.5) 0%, transparent 70%);
filter: blur(12px);
z-index: -1;
}
@media (max-width: 720px) {
.cd-flb .book {
width: 360px;
height: 280px;
}
.cd-flb .page,
.cd-flb .left-cover {
width: 180px;
height: 280px;
}
.cd-flb .page {
left: 180px;
}
.cd-flb .pg {
padding: 22px 18px;
}
.cd-flb .spread-title.sm {
font-size: 20px;
}
.cd-flb .spread-title.xs {
font-size: 18px;
}
.cd-flb .cover-title {
font-size: 26px;
}
}
@media (prefers-reduced-motion: reduce) {
.cd-flb .book-scene,
.cd-flb .page {
transition: none !important;
}
}/* ─── 04 Page-Turn Flipbook — editorial book ───────────────── */
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;0,900;1,400;1,700&family=EB+Garamond:ital,wght@0,400;0,500;1,400;1,500&display=swap');
.cd-flb {
--cd-flb-bg: #1c1410;
position: relative;
width: 100%;
min-height: 600px;
background: var(--cd-flb-bg);
font-family: 'EB Garamond', Georgia, serif;
overflow: hidden;
perspective: 2400px;
box-sizing: border-box;
}
.cd-flb *,
.cd-flb *::before,
.cd-flb *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cd-flb .card {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 28px 16px;
}
.cd-flb .card::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(ellipse 60% 50% at 50% 40%, rgba(180,120,40,0.06) 0%, transparent 70%);
pointer-events: none;
}
.cd-flb .book-scene {
transform-style: preserve-3d;
transform: rotateX(5deg) rotateY(-4deg);
transition: transform 0.6s ease;
position: relative;
z-index: 1;
}
.cd-flb .book-scene:hover {
transform: rotateX(4deg) rotateY(-2deg);
}
.cd-flb .book {
width: 560px;
height: 360px;
position: relative;
transform-style: preserve-3d;
}
.cd-flb .book::before {
content: '';
position: absolute;
left: 50%;
top: 0;
bottom: 0;
width: 12px;
transform: translateX(-50%) translateZ(1px);
background: linear-gradient(to right, rgba(0,0,0,0.4) 0%, rgba(0,0,0,0.1) 30%, rgba(0,0,0,0.1) 70%, rgba(0,0,0,0.4) 100%);
z-index: 20;
pointer-events: none;
}
.cd-flb .page {
position: absolute;
width: 280px;
height: 360px;
top: 0;
left: 280px;
transform-origin: left center;
transform-style: preserve-3d;
transition: transform 1.1s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.cd-flb .page.flipped {
transform: rotateY(-180deg);
}
.cd-flb .page-front,
.cd-flb .page-back {
position: absolute;
inset: 0;
backface-visibility: hidden;
overflow: hidden;
}
.cd-flb .page-back {
transform: rotateY(180deg);
}
.cd-flb .left-cover {
position: absolute;
left: 0;
top: 0;
width: 280px;
height: 360px;
z-index: 0;
border-radius: 4px 0 0 4px;
overflow: hidden;
}
.cd-flb .pg {
width: 100%;
height: 100%;
padding: 30px 26px;
display: flex;
flex-direction: column;
position: relative;
}
.cd-flb .pg-warm {
color: #2a1f14;
}
.cd-flb .pg-cool {
color: #2a2432;
}
.cd-flb .pg-rose {
color: #2a1418;
}
.cd-flb .pg-sage {
color: #1a2a1e;
}
.cd-flb .pg-ink {
color: #e8d8c0;
}
.cd-flb .pg-num {
font-family: 'EB Garamond', serif;
font-size: 10px;
letter-spacing: 2px;
position: absolute;
bottom: 16px;
}
.cd-flb .pg-num.right {
right: 22px;
color: #8a6a4a;
}
.cd-flb .pg-num.left {
left: 22px;
color: #7a6898;
}
.cd-flb .pg-num.rose {
color: #8a4050;
}
.cd-flb .pg-num.sage {
color: #4a7a50;
}
.cd-flb .pg-num.ink {
color: rgba(200,180,140,0.4);
}
.cd-flb .page-front::after,
.cd-flb .page-back::after {
content: '';
position: absolute;
top: 0;
bottom: 0;
width: 40px;
pointer-events: none;
}
.cd-flb .page-front::after {
right: 0;
background: linear-gradient(to right, transparent, rgba(0,0,0,0.14));
}
.cd-flb .page-back::after {
left: 0;
background: linear-gradient(to left, transparent, rgba(0,0,0,0.14));
}
.cd-flb .left-cover::after {
content: '';
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 30px;
background: linear-gradient(to right, transparent, rgba(0,0,0,0.2));
}
.cd-flb .paper-warm {
background: #f5ece0;
}
.cd-flb .paper-cool {
background: #ede8f0;
}
.cd-flb .paper-rose {
background: #f5e8e8;
}
.cd-flb .paper-sage {
background: #e8f0e8;
}
.cd-flb .paper-ink {
background: #1a1510;
}
.cd-flb .spread-label {
font-family: 'EB Garamond', serif;
font-size: 9px;
letter-spacing: 4px;
text-transform: uppercase;
opacity: 0.4;
margin-bottom: 16px;
}
.cd-flb .spread-title {
font-family: 'Playfair Display', serif;
font-weight: 900;
line-height: 1.1;
margin-bottom: 14px;
}
.cd-flb .spread-title.sm {
font-size: 26px;
}
.cd-flb .spread-title.xs {
font-size: 22px;
}
.cd-flb .spread-title.rose {
color: #5a1828;
}
.cd-flb .spread-body {
font-family: 'EB Garamond', serif;
font-size: 13px;
line-height: 1.78;
opacity: 0.75;
}
.cd-flb .spread-body.mt {
margin-top: 10px;
}
.cd-flb .spread-body.alt {
color: #3a2a50;
}
.cd-flb .spread-body.rose-body {
color: #3a1820;
}
.cd-flb .spread-body.sage-body {
color: #2a3a26;
}
.cd-flb .spread-pullquote {
font-family: 'Playfair Display', serif;
font-style: italic;
font-size: 16px;
line-height: 1.5;
border-left: 2px solid currentColor;
padding-left: 14px;
margin: 16px 0;
opacity: 0.8;
}
.cd-flb .spread-pullquote.warm {
border-color: #8a6a4a;
color: #4a3424;
}
.cd-flb .spread-pullquote.sage {
border-color: #3a6a40;
color: #2a4a2e;
}
.cd-flb .spread-rule {
width: 40px;
height: 1px;
background: currentColor;
opacity: 0.3;
margin: 14px 0;
}
.cd-flb .spread-rule.alt {
background: #4a3868;
}
.cd-flb .spread-rule.rose {
background: #8a3040;
}
.cd-flb .spread-img {
width: 100%;
height: 100px;
border-radius: 4px;
margin: 14px 0;
overflow: hidden;
flex-shrink: 0;
}
.cd-flb .sage-img {
background: linear-gradient(135deg, #2a4a2e, #1a3020);
display: flex;
align-items: center;
justify-content: center;
}
.cd-flb .circle-outer {
width: 64px;
height: 64px;
border: 2px solid rgba(120,200,120,0.4);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.cd-flb .circle-inner {
width: 32px;
height: 32px;
background: rgba(120,200,120,0.2);
border-radius: 50%;
}
.cd-flb .spread-big-num {
font-family: 'Playfair Display', serif;
font-size: 80px;
font-weight: 900;
line-height: 0.9;
opacity: 0.07;
position: absolute;
right: 16px;
bottom: 30px;
}
/* Cover art */
.cd-flb .cover-art {
width: 100%;
height: 100%;
background: linear-gradient(148deg, #1a0f08, #0e0a06);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 26px;
border-radius: 4px 0 0 4px;
position: relative;
overflow: hidden;
}
.cd-flb .cover-art::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(ellipse 70% 60% at 50% 40%, rgba(180,120,40,0.12) 0%, transparent 70%);
}
.cd-flb .cover-eyebrow {
font-family: 'EB Garamond', serif;
font-size: 9px;
letter-spacing: 5px;
color: rgba(200,160,80,0.55);
margin-bottom: 26px;
z-index: 1;
}
.cd-flb .cover-rule {
width: 56px;
height: 1px;
background: rgba(200,160,80,0.35);
margin: 0 0 26px;
z-index: 1;
}
.cd-flb .cover-title {
font-family: 'Playfair Display', serif;
font-size: 34px;
font-weight: 900;
color: #f0e4cc;
text-align: center;
line-height: 1.1;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
}
.cd-flb .cover-vol {
font-style: italic;
font-weight: 400;
font-size: 20px;
opacity: 0.7;
margin-top: 6px;
}
.cd-flb .cover-rule:nth-of-type(2),
.cd-flb .cover-title + .cover-rule {
margin: 26px 0;
}
.cd-flb .cover-sub {
font-family: 'EB Garamond', serif;
font-style: italic;
font-size: 11px;
color: rgba(200,160,80,0.55);
letter-spacing: 1px;
z-index: 1;
}
.cd-flb .colophon-label {
font-family: 'EB Garamond', serif;
font-size: 9px;
letter-spacing: 4px;
opacity: 0.35;
margin-bottom: 18px;
}
.cd-flb .colophon-title {
font-family: 'Playfair Display', serif;
font-size: 18px;
font-weight: 700;
opacity: 0.9;
margin-bottom: 8px;
}
.cd-flb .colophon-sub {
font-family: 'Playfair Display', serif;
font-style: italic;
font-size: 12px;
opacity: 0.5;
margin-bottom: 22px;
}
.cd-flb .colophon-body {
font-family: 'EB Garamond', serif;
font-size: 11px;
line-height: 1.8;
opacity: 0.45;
}
.cd-flb .nav {
display: flex;
align-items: center;
gap: 18px;
margin-top: 26px;
position: relative;
z-index: 100;
}
.cd-flb .nav-btn {
display: flex;
align-items: center;
gap: 8px;
font-family: 'EB Garamond', serif;
font-size: 12px;
letter-spacing: 2px;
color: rgba(200,170,120,0.7);
background: none;
border: none;
cursor: pointer;
padding: 9px 18px;
border-radius: 4px;
transition: color 0.2s, background 0.2s;
}
.cd-flb .nav-btn:hover {
color: rgba(220,190,140,1);
background: rgba(200,170,120,0.08);
}
.cd-flb .nav-btn:disabled {
opacity: 0.3;
cursor: default;
}
.cd-flb .page-counter {
font-family: 'EB Garamond', serif;
font-size: 12px;
color: rgba(200,170,120,0.45);
letter-spacing: 2px;
min-width: 80px;
text-align: center;
}
.cd-flb .book-shadow {
position: absolute;
bottom: -18px;
left: 10%;
right: 10%;
height: 28px;
background: radial-gradient(ellipse at center, rgba(0,0,0,0.5) 0%, transparent 70%);
filter: blur(12px);
z-index: -1;
}
@media (max-width: 720px) {
.cd-flb .book {
width: 360px;
height: 280px;
}
.cd-flb .page,
.cd-flb .left-cover {
width: 180px;
height: 280px;
}
.cd-flb .page {
left: 180px;
}
.cd-flb .pg {
padding: 22px 18px;
}
.cd-flb .spread-title.sm {
font-size: 20px;
}
.cd-flb .spread-title.xs {
font-size: 18px;
}
.cd-flb .cover-title {
font-size: 26px;
}
}
@media (prefers-reduced-motion: reduce) {
.cd-flb .book-scene,
.cd-flb .page {
transition: none !important;
}
}(() => {
const root = document.querySelector('.cd-flb');
if (!root) return;
const book = root.querySelector('[data-cd-flb-book]');
const prevBtn = root.querySelector('[data-cd-flb-prev]');
const nextBtn = root.querySelector('[data-cd-flb-next]');
const counter = root.querySelector('[data-cd-flb-counter]');
if (!book || !prevBtn || !nextBtn || !counter) return;
const pages = Array.from(root.querySelectorAll('.page[data-page]'));
const TOTAL_SPREADS = pages.length + 1;
let currentSpread = 0;
function update() {
pages.forEach((page, i) => {
page.classList.toggle('flipped', i < currentSpread);
page.style.zIndex = i < currentSpread ? i + 1 : pages.length - i + 4;
});
counter.textContent = `Spread ${currentSpread + 1} of ${TOTAL_SPREADS}`;
prevBtn.disabled = currentSpread === 0;
nextBtn.disabled = currentSpread === TOTAL_SPREADS - 1;
}
prevBtn.addEventListener('click', () => { if (currentSpread > 0) { currentSpread--; update(); } });
nextBtn.addEventListener('click', () => { if (currentSpread < TOTAL_SPREADS - 1) { currentSpread++; update(); } });
// Click halves of the book to advance — scoped to the book itself
book.addEventListener('click', e => {
if (e.target.closest('.nav-btn')) return;
const rect = book.getBoundingClientRect();
if (e.clientX > rect.left + rect.width / 2) {
if (currentSpread < TOTAL_SPREADS - 1) { currentSpread++; update(); }
} else {
if (currentSpread > 0) { currentSpread--; update(); }
}
});
// Keyboard — only when pointer is inside the wrapper
let pointerInside = false;
root.addEventListener('mouseenter', () => { pointerInside = true; });
root.addEventListener('mouseleave', () => { pointerInside = false; });
document.addEventListener('keydown', e => {
if (!pointerInside) return;
if (e.key === 'ArrowRight') { nextBtn.click(); e.preventDefault(); }
if (e.key === 'ArrowLeft') { prevBtn.click(); e.preventDefault(); }
});
update();
})();(() => {
const root = document.querySelector('.cd-flb');
if (!root) return;
const book = root.querySelector('[data-cd-flb-book]');
const prevBtn = root.querySelector('[data-cd-flb-prev]');
const nextBtn = root.querySelector('[data-cd-flb-next]');
const counter = root.querySelector('[data-cd-flb-counter]');
if (!book || !prevBtn || !nextBtn || !counter) return;
const pages = Array.from(root.querySelectorAll('.page[data-page]'));
const TOTAL_SPREADS = pages.length + 1;
let currentSpread = 0;
function update() {
pages.forEach((page, i) => {
page.classList.toggle('flipped', i < currentSpread);
page.style.zIndex = i < currentSpread ? i + 1 : pages.length - i + 4;
});
counter.textContent = `Spread ${currentSpread + 1} of ${TOTAL_SPREADS}`;
prevBtn.disabled = currentSpread === 0;
nextBtn.disabled = currentSpread === TOTAL_SPREADS - 1;
}
prevBtn.addEventListener('click', () => { if (currentSpread > 0) { currentSpread--; update(); } });
nextBtn.addEventListener('click', () => { if (currentSpread < TOTAL_SPREADS - 1) { currentSpread++; update(); } });
// Click halves of the book to advance — scoped to the book itself
book.addEventListener('click', e => {
if (e.target.closest('.nav-btn')) return;
const rect = book.getBoundingClientRect();
if (e.clientX > rect.left + rect.width / 2) {
if (currentSpread < TOTAL_SPREADS - 1) { currentSpread++; update(); }
} else {
if (currentSpread > 0) { currentSpread--; update(); }
}
});
// Keyboard — only when pointer is inside the wrapper
let pointerInside = false;
root.addEventListener('mouseenter', () => { pointerInside = true; });
root.addEventListener('mouseleave', () => { pointerInside = false; });
document.addEventListener('keydown', e => {
if (!pointerInside) return;
if (e.key === 'ArrowRight') { nextBtn.click(); e.preventDefault(); }
if (e.key === 'ArrowLeft') { prevBtn.click(); e.preventDefault(); }
});
update();
})();Here's a working CSS 3D Design from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Page-Turn Flipbook
Source: https://codefronts.com/design-styles/css-3d/page-turn-flipbook/
A physical book with a dark leather cover, cream parchment spreads, and pages that arc through a real 0° → -180° rotateY transform with the hinge anchored at transform-origin: left center. Each spread carries genuine editorial typography — pull quotes, column rules, drop caps, colored section pages — and the gutter shadow on each face simulates the dip near the spine. Click the right half of the book to advance, the left half to go back, or use the keyboard arrows / nav buttons. Playfair Display + EB Garamond. Best for digital magazines, restaurant menus, lookbooks, interactive storytelling, brand annual reports.
## HTML
```html
<section class="cd-flb" aria-label="Page-turn flipbook demo">
<div class="card">
<div class="book-scene">
<div class="book" data-cd-flb-book>
<div class="book-shadow" aria-hidden="true"></div>
<div class="left-cover" data-cd-flb-cover>
<div class="cover-art">
<div class="cover-eyebrow">FORMA STUDIO</div>
<div class="cover-rule"></div>
<div class="cover-title">FORMA<span class="cover-vol">Vol. VI</span></div>
<div class="cover-rule"></div>
<div class="cover-sub">Design · Architecture · Thought</div>
</div>
</div>
<div class="page" data-page="0">
<div class="page-front paper-warm">
<div class="pg pg-warm">
<div class="spread-label">Chapter One</div>
<div class="spread-title sm">The Weight<br />of Silence</div>
<div class="spread-rule"></div>
<p class="spread-body">There is a moment before speech when the world holds its breath. Architecture inhabits this pause — not the buildings themselves, but the spaces they carve from air.</p>
<p class="spread-body mt">The corridor narrows, draws the eye forward. Light slants from an unseen source. To walk through is to be composed by the building.</p>
<span class="pg-num right">1</span>
<span class="spread-big-num">I</span>
</div>
</div>
<div class="page-back paper-cool">
<div class="pg pg-cool">
<div class="spread-label">Essay II</div>
<div class="spread-title xs">Form<br />Without<br />Apology</div>
<div class="spread-rule alt"></div>
<p class="spread-body alt">To design is to make a claim about the world as it should be — not as it is. The blueprint precedes the building, and in that gap lives everything called imagination.</p>
<span class="pg-num left">2</span>
</div>
</div>
</div>
<div class="page" data-page="1">
<div class="page-front paper-sage">
<div class="pg pg-sage">
<div class="spread-label">Visual Field</div>
<div class="spread-img sage-img">
<div class="circle-outer">
<div class="circle-inner"></div>
</div>
</div>
<div class="spread-pullquote sage">"Growth is not linear. It is fractal — repeating at every scale."</div>
<p class="spread-body sage-body">The forest does not plan its canopy. Each tree reaches for light by the same simple rule: grow toward the sun. The emergent whole is unplanned and irreducible.</p>
<span class="pg-num right sage">3</span>
</div>
</div>
<div class="page-back paper-rose">
<div class="pg pg-rose">
<div class="spread-label">Interlude</div>
<div class="spread-title sm rose">Red<br />Notation</div>
<div class="spread-rule rose"></div>
<p class="spread-body rose-body">Color arrives before language. Red is felt in the chest before named by the tongue. The ancient painters knew this — their ochre hands pressed flat on cave walls were not decoration. They were declaration.</p>
<span class="pg-num left rose">4</span>
</div>
</div>
</div>
<div class="page" data-page="2">
<div class="page-front paper-warm">
<div class="pg pg-warm">
<div class="spread-label">Final Study</div>
<div class="spread-title sm">Endings<br />as Form</div>
<div class="spread-rule"></div>
<p class="spread-body">Every designed thing must eventually end — the page turn, the last note, the exit from the building. The best designs make their endings feel inevitable, not arbitrary.</p>
<div class="spread-pullquote warm">"The final line is the first thing written."</div>
<span class="pg-num right">5</span>
</div>
</div>
<div class="page-back paper-ink">
<div class="pg pg-ink">
<div class="colophon-label">COLOPHON</div>
<div class="colophon-title">FORMA</div>
<div class="colophon-sub">A Journal of Design Thought</div>
<p class="colophon-body">Issue VI · Printed on FSC-certified stock · Typography set in Playfair Display and EB Garamond · All rights reserved ©</p>
<span class="pg-num left ink">6</span>
</div>
</div>
</div>
</div>
<div class="nav">
<button class="nav-btn" type="button" data-cd-flb-prev disabled>← Prev</button>
<span class="page-counter" data-cd-flb-counter>Spread 1 of 4</span>
<button class="nav-btn" type="button" data-cd-flb-next>Next →</button>
</div>
</div>
</div>
</section>
```
## CSS
```css
/* ─── 04 Page-Turn Flipbook — editorial book ───────────────── */
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;0,900;1,400;1,700&family=EB+Garamond:ital,wght@0,400;0,500;1,400;1,500&display=swap');
.cd-flb {
--cd-flb-bg: #1c1410;
position: relative;
width: 100%;
min-height: 600px;
background: var(--cd-flb-bg);
font-family: 'EB Garamond', Georgia, serif;
overflow: hidden;
perspective: 2400px;
box-sizing: border-box;
}
.cd-flb *,
.cd-flb *::before,
.cd-flb *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cd-flb .card {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 28px 16px;
}
.cd-flb .card::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(ellipse 60% 50% at 50% 40%, rgba(180,120,40,0.06) 0%, transparent 70%);
pointer-events: none;
}
.cd-flb .book-scene {
transform-style: preserve-3d;
transform: rotateX(5deg) rotateY(-4deg);
transition: transform 0.6s ease;
position: relative;
z-index: 1;
}
.cd-flb .book-scene:hover {
transform: rotateX(4deg) rotateY(-2deg);
}
.cd-flb .book {
width: 560px;
height: 360px;
position: relative;
transform-style: preserve-3d;
}
.cd-flb .book::before {
content: '';
position: absolute;
left: 50%;
top: 0;
bottom: 0;
width: 12px;
transform: translateX(-50%) translateZ(1px);
background: linear-gradient(to right, rgba(0,0,0,0.4) 0%, rgba(0,0,0,0.1) 30%, rgba(0,0,0,0.1) 70%, rgba(0,0,0,0.4) 100%);
z-index: 20;
pointer-events: none;
}
.cd-flb .page {
position: absolute;
width: 280px;
height: 360px;
top: 0;
left: 280px;
transform-origin: left center;
transform-style: preserve-3d;
transition: transform 1.1s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.cd-flb .page.flipped {
transform: rotateY(-180deg);
}
.cd-flb .page-front,
.cd-flb .page-back {
position: absolute;
inset: 0;
backface-visibility: hidden;
overflow: hidden;
}
.cd-flb .page-back {
transform: rotateY(180deg);
}
.cd-flb .left-cover {
position: absolute;
left: 0;
top: 0;
width: 280px;
height: 360px;
z-index: 0;
border-radius: 4px 0 0 4px;
overflow: hidden;
}
.cd-flb .pg {
width: 100%;
height: 100%;
padding: 30px 26px;
display: flex;
flex-direction: column;
position: relative;
}
.cd-flb .pg-warm {
color: #2a1f14;
}
.cd-flb .pg-cool {
color: #2a2432;
}
.cd-flb .pg-rose {
color: #2a1418;
}
.cd-flb .pg-sage {
color: #1a2a1e;
}
.cd-flb .pg-ink {
color: #e8d8c0;
}
.cd-flb .pg-num {
font-family: 'EB Garamond', serif;
font-size: 10px;
letter-spacing: 2px;
position: absolute;
bottom: 16px;
}
.cd-flb .pg-num.right {
right: 22px;
color: #8a6a4a;
}
.cd-flb .pg-num.left {
left: 22px;
color: #7a6898;
}
.cd-flb .pg-num.rose {
color: #8a4050;
}
.cd-flb .pg-num.sage {
color: #4a7a50;
}
.cd-flb .pg-num.ink {
color: rgba(200,180,140,0.4);
}
.cd-flb .page-front::after,
.cd-flb .page-back::after {
content: '';
position: absolute;
top: 0;
bottom: 0;
width: 40px;
pointer-events: none;
}
.cd-flb .page-front::after {
right: 0;
background: linear-gradient(to right, transparent, rgba(0,0,0,0.14));
}
.cd-flb .page-back::after {
left: 0;
background: linear-gradient(to left, transparent, rgba(0,0,0,0.14));
}
.cd-flb .left-cover::after {
content: '';
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 30px;
background: linear-gradient(to right, transparent, rgba(0,0,0,0.2));
}
.cd-flb .paper-warm {
background: #f5ece0;
}
.cd-flb .paper-cool {
background: #ede8f0;
}
.cd-flb .paper-rose {
background: #f5e8e8;
}
.cd-flb .paper-sage {
background: #e8f0e8;
}
.cd-flb .paper-ink {
background: #1a1510;
}
.cd-flb .spread-label {
font-family: 'EB Garamond', serif;
font-size: 9px;
letter-spacing: 4px;
text-transform: uppercase;
opacity: 0.4;
margin-bottom: 16px;
}
.cd-flb .spread-title {
font-family: 'Playfair Display', serif;
font-weight: 900;
line-height: 1.1;
margin-bottom: 14px;
}
.cd-flb .spread-title.sm {
font-size: 26px;
}
.cd-flb .spread-title.xs {
font-size: 22px;
}
.cd-flb .spread-title.rose {
color: #5a1828;
}
.cd-flb .spread-body {
font-family: 'EB Garamond', serif;
font-size: 13px;
line-height: 1.78;
opacity: 0.75;
}
.cd-flb .spread-body.mt {
margin-top: 10px;
}
.cd-flb .spread-body.alt {
color: #3a2a50;
}
.cd-flb .spread-body.rose-body {
color: #3a1820;
}
.cd-flb .spread-body.sage-body {
color: #2a3a26;
}
.cd-flb .spread-pullquote {
font-family: 'Playfair Display', serif;
font-style: italic;
font-size: 16px;
line-height: 1.5;
border-left: 2px solid currentColor;
padding-left: 14px;
margin: 16px 0;
opacity: 0.8;
}
.cd-flb .spread-pullquote.warm {
border-color: #8a6a4a;
color: #4a3424;
}
.cd-flb .spread-pullquote.sage {
border-color: #3a6a40;
color: #2a4a2e;
}
.cd-flb .spread-rule {
width: 40px;
height: 1px;
background: currentColor;
opacity: 0.3;
margin: 14px 0;
}
.cd-flb .spread-rule.alt {
background: #4a3868;
}
.cd-flb .spread-rule.rose {
background: #8a3040;
}
.cd-flb .spread-img {
width: 100%;
height: 100px;
border-radius: 4px;
margin: 14px 0;
overflow: hidden;
flex-shrink: 0;
}
.cd-flb .sage-img {
background: linear-gradient(135deg, #2a4a2e, #1a3020);
display: flex;
align-items: center;
justify-content: center;
}
.cd-flb .circle-outer {
width: 64px;
height: 64px;
border: 2px solid rgba(120,200,120,0.4);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.cd-flb .circle-inner {
width: 32px;
height: 32px;
background: rgba(120,200,120,0.2);
border-radius: 50%;
}
.cd-flb .spread-big-num {
font-family: 'Playfair Display', serif;
font-size: 80px;
font-weight: 900;
line-height: 0.9;
opacity: 0.07;
position: absolute;
right: 16px;
bottom: 30px;
}
/* Cover art */
.cd-flb .cover-art {
width: 100%;
height: 100%;
background: linear-gradient(148deg, #1a0f08, #0e0a06);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 26px;
border-radius: 4px 0 0 4px;
position: relative;
overflow: hidden;
}
.cd-flb .cover-art::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(ellipse 70% 60% at 50% 40%, rgba(180,120,40,0.12) 0%, transparent 70%);
}
.cd-flb .cover-eyebrow {
font-family: 'EB Garamond', serif;
font-size: 9px;
letter-spacing: 5px;
color: rgba(200,160,80,0.55);
margin-bottom: 26px;
z-index: 1;
}
.cd-flb .cover-rule {
width: 56px;
height: 1px;
background: rgba(200,160,80,0.35);
margin: 0 0 26px;
z-index: 1;
}
.cd-flb .cover-title {
font-family: 'Playfair Display', serif;
font-size: 34px;
font-weight: 900;
color: #f0e4cc;
text-align: center;
line-height: 1.1;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
}
.cd-flb .cover-vol {
font-style: italic;
font-weight: 400;
font-size: 20px;
opacity: 0.7;
margin-top: 6px;
}
.cd-flb .cover-rule:nth-of-type(2),
.cd-flb .cover-title + .cover-rule {
margin: 26px 0;
}
.cd-flb .cover-sub {
font-family: 'EB Garamond', serif;
font-style: italic;
font-size: 11px;
color: rgba(200,160,80,0.55);
letter-spacing: 1px;
z-index: 1;
}
.cd-flb .colophon-label {
font-family: 'EB Garamond', serif;
font-size: 9px;
letter-spacing: 4px;
opacity: 0.35;
margin-bottom: 18px;
}
.cd-flb .colophon-title {
font-family: 'Playfair Display', serif;
font-size: 18px;
font-weight: 700;
opacity: 0.9;
margin-bottom: 8px;
}
.cd-flb .colophon-sub {
font-family: 'Playfair Display', serif;
font-style: italic;
font-size: 12px;
opacity: 0.5;
margin-bottom: 22px;
}
.cd-flb .colophon-body {
font-family: 'EB Garamond', serif;
font-size: 11px;
line-height: 1.8;
opacity: 0.45;
}
.cd-flb .nav {
display: flex;
align-items: center;
gap: 18px;
margin-top: 26px;
position: relative;
z-index: 100;
}
.cd-flb .nav-btn {
display: flex;
align-items: center;
gap: 8px;
font-family: 'EB Garamond', serif;
font-size: 12px;
letter-spacing: 2px;
color: rgba(200,170,120,0.7);
background: none;
border: none;
cursor: pointer;
padding: 9px 18px;
border-radius: 4px;
transition: color 0.2s, background 0.2s;
}
.cd-flb .nav-btn:hover {
color: rgba(220,190,140,1);
background: rgba(200,170,120,0.08);
}
.cd-flb .nav-btn:disabled {
opacity: 0.3;
cursor: default;
}
.cd-flb .page-counter {
font-family: 'EB Garamond', serif;
font-size: 12px;
color: rgba(200,170,120,0.45);
letter-spacing: 2px;
min-width: 80px;
text-align: center;
}
.cd-flb .book-shadow {
position: absolute;
bottom: -18px;
left: 10%;
right: 10%;
height: 28px;
background: radial-gradient(ellipse at center, rgba(0,0,0,0.5) 0%, transparent 70%);
filter: blur(12px);
z-index: -1;
}
@media (max-width: 720px) {
.cd-flb .book {
width: 360px;
height: 280px;
}
.cd-flb .page,
.cd-flb .left-cover {
width: 180px;
height: 280px;
}
.cd-flb .page {
left: 180px;
}
.cd-flb .pg {
padding: 22px 18px;
}
.cd-flb .spread-title.sm {
font-size: 20px;
}
.cd-flb .spread-title.xs {
font-size: 18px;
}
.cd-flb .cover-title {
font-size: 26px;
}
}
@media (prefers-reduced-motion: reduce) {
.cd-flb .book-scene,
.cd-flb .page {
transition: none !important;
}
}
```
## JavaScript
```js
(() => {
const root = document.querySelector('.cd-flb');
if (!root) return;
const book = root.querySelector('[data-cd-flb-book]');
const prevBtn = root.querySelector('[data-cd-flb-prev]');
const nextBtn = root.querySelector('[data-cd-flb-next]');
const counter = root.querySelector('[data-cd-flb-counter]');
if (!book || !prevBtn || !nextBtn || !counter) return;
const pages = Array.from(root.querySelectorAll('.page[data-page]'));
const TOTAL_SPREADS = pages.length + 1;
let currentSpread = 0;
function update() {
pages.forEach((page, i) => {
page.classList.toggle('flipped', i < currentSpread);
page.style.zIndex = i < currentSpread ? i + 1 : pages.length - i + 4;
});
counter.textContent = `Spread ${currentSpread + 1} of ${TOTAL_SPREADS}`;
prevBtn.disabled = currentSpread === 0;
nextBtn.disabled = currentSpread === TOTAL_SPREADS - 1;
}
prevBtn.addEventListener('click', () => { if (currentSpread > 0) { currentSpread--; update(); } });
nextBtn.addEventListener('click', () => { if (currentSpread < TOTAL_SPREADS - 1) { currentSpread++; update(); } });
// Click halves of the book to advance — scoped to the book itself
book.addEventListener('click', e => {
if (e.target.closest('.nav-btn')) return;
const rect = book.getBoundingClientRect();
if (e.clientX > rect.left + rect.width / 2) {
if (currentSpread < TOTAL_SPREADS - 1) { currentSpread++; update(); }
} else {
if (currentSpread > 0) { currentSpread--; update(); }
}
});
// Keyboard — only when pointer is inside the wrapper
let pointerInside = false;
root.addEventListener('mouseenter', () => { pointerInside = true; });
root.addEventListener('mouseleave', () => { pointerInside = false; });
document.addEventListener('keydown', e => {
if (!pointerInside) return;
if (e.key === 'ArrowRight') { nextBtn.click(); e.preventDefault(); }
if (e.key === 'ArrowLeft') { prevBtn.click(); e.preventDefault(); }
});
update();
})();
```Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS 3D Design from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Page-Turn Flipbook
Source: https://codefronts.com/design-styles/css-3d/page-turn-flipbook/
A physical book with a dark leather cover, cream parchment spreads, and pages that arc through a real 0° → -180° rotateY transform with the hinge anchored at transform-origin: left center. Each spread carries genuine editorial typography — pull quotes, column rules, drop caps, colored section pages — and the gutter shadow on each face simulates the dip near the spine. Click the right half of the book to advance, the left half to go back, or use the keyboard arrows / nav buttons. Playfair Display + EB Garamond. Best for digital magazines, restaurant menus, lookbooks, interactive storytelling, brand annual reports.
## HTML
```html
<section class="cd-flb" aria-label="Page-turn flipbook demo">
<div class="card">
<div class="book-scene">
<div class="book" data-cd-flb-book>
<div class="book-shadow" aria-hidden="true"></div>
<div class="left-cover" data-cd-flb-cover>
<div class="cover-art">
<div class="cover-eyebrow">FORMA STUDIO</div>
<div class="cover-rule"></div>
<div class="cover-title">FORMA<span class="cover-vol">Vol. VI</span></div>
<div class="cover-rule"></div>
<div class="cover-sub">Design · Architecture · Thought</div>
</div>
</div>
<div class="page" data-page="0">
<div class="page-front paper-warm">
<div class="pg pg-warm">
<div class="spread-label">Chapter One</div>
<div class="spread-title sm">The Weight<br />of Silence</div>
<div class="spread-rule"></div>
<p class="spread-body">There is a moment before speech when the world holds its breath. Architecture inhabits this pause — not the buildings themselves, but the spaces they carve from air.</p>
<p class="spread-body mt">The corridor narrows, draws the eye forward. Light slants from an unseen source. To walk through is to be composed by the building.</p>
<span class="pg-num right">1</span>
<span class="spread-big-num">I</span>
</div>
</div>
<div class="page-back paper-cool">
<div class="pg pg-cool">
<div class="spread-label">Essay II</div>
<div class="spread-title xs">Form<br />Without<br />Apology</div>
<div class="spread-rule alt"></div>
<p class="spread-body alt">To design is to make a claim about the world as it should be — not as it is. The blueprint precedes the building, and in that gap lives everything called imagination.</p>
<span class="pg-num left">2</span>
</div>
</div>
</div>
<div class="page" data-page="1">
<div class="page-front paper-sage">
<div class="pg pg-sage">
<div class="spread-label">Visual Field</div>
<div class="spread-img sage-img">
<div class="circle-outer">
<div class="circle-inner"></div>
</div>
</div>
<div class="spread-pullquote sage">"Growth is not linear. It is fractal — repeating at every scale."</div>
<p class="spread-body sage-body">The forest does not plan its canopy. Each tree reaches for light by the same simple rule: grow toward the sun. The emergent whole is unplanned and irreducible.</p>
<span class="pg-num right sage">3</span>
</div>
</div>
<div class="page-back paper-rose">
<div class="pg pg-rose">
<div class="spread-label">Interlude</div>
<div class="spread-title sm rose">Red<br />Notation</div>
<div class="spread-rule rose"></div>
<p class="spread-body rose-body">Color arrives before language. Red is felt in the chest before named by the tongue. The ancient painters knew this — their ochre hands pressed flat on cave walls were not decoration. They were declaration.</p>
<span class="pg-num left rose">4</span>
</div>
</div>
</div>
<div class="page" data-page="2">
<div class="page-front paper-warm">
<div class="pg pg-warm">
<div class="spread-label">Final Study</div>
<div class="spread-title sm">Endings<br />as Form</div>
<div class="spread-rule"></div>
<p class="spread-body">Every designed thing must eventually end — the page turn, the last note, the exit from the building. The best designs make their endings feel inevitable, not arbitrary.</p>
<div class="spread-pullquote warm">"The final line is the first thing written."</div>
<span class="pg-num right">5</span>
</div>
</div>
<div class="page-back paper-ink">
<div class="pg pg-ink">
<div class="colophon-label">COLOPHON</div>
<div class="colophon-title">FORMA</div>
<div class="colophon-sub">A Journal of Design Thought</div>
<p class="colophon-body">Issue VI · Printed on FSC-certified stock · Typography set in Playfair Display and EB Garamond · All rights reserved ©</p>
<span class="pg-num left ink">6</span>
</div>
</div>
</div>
</div>
<div class="nav">
<button class="nav-btn" type="button" data-cd-flb-prev disabled>← Prev</button>
<span class="page-counter" data-cd-flb-counter>Spread 1 of 4</span>
<button class="nav-btn" type="button" data-cd-flb-next>Next →</button>
</div>
</div>
</div>
</section>
```
## CSS
```css
/* ─── 04 Page-Turn Flipbook — editorial book ───────────────── */
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,700;0,900;1,400;1,700&family=EB+Garamond:ital,wght@0,400;0,500;1,400;1,500&display=swap');
.cd-flb {
--cd-flb-bg: #1c1410;
position: relative;
width: 100%;
min-height: 600px;
background: var(--cd-flb-bg);
font-family: 'EB Garamond', Georgia, serif;
overflow: hidden;
perspective: 2400px;
box-sizing: border-box;
}
.cd-flb *,
.cd-flb *::before,
.cd-flb *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.cd-flb .card {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 28px 16px;
}
.cd-flb .card::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(ellipse 60% 50% at 50% 40%, rgba(180,120,40,0.06) 0%, transparent 70%);
pointer-events: none;
}
.cd-flb .book-scene {
transform-style: preserve-3d;
transform: rotateX(5deg) rotateY(-4deg);
transition: transform 0.6s ease;
position: relative;
z-index: 1;
}
.cd-flb .book-scene:hover {
transform: rotateX(4deg) rotateY(-2deg);
}
.cd-flb .book {
width: 560px;
height: 360px;
position: relative;
transform-style: preserve-3d;
}
.cd-flb .book::before {
content: '';
position: absolute;
left: 50%;
top: 0;
bottom: 0;
width: 12px;
transform: translateX(-50%) translateZ(1px);
background: linear-gradient(to right, rgba(0,0,0,0.4) 0%, rgba(0,0,0,0.1) 30%, rgba(0,0,0,0.1) 70%, rgba(0,0,0,0.4) 100%);
z-index: 20;
pointer-events: none;
}
.cd-flb .page {
position: absolute;
width: 280px;
height: 360px;
top: 0;
left: 280px;
transform-origin: left center;
transform-style: preserve-3d;
transition: transform 1.1s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.cd-flb .page.flipped {
transform: rotateY(-180deg);
}
.cd-flb .page-front,
.cd-flb .page-back {
position: absolute;
inset: 0;
backface-visibility: hidden;
overflow: hidden;
}
.cd-flb .page-back {
transform: rotateY(180deg);
}
.cd-flb .left-cover {
position: absolute;
left: 0;
top: 0;
width: 280px;
height: 360px;
z-index: 0;
border-radius: 4px 0 0 4px;
overflow: hidden;
}
.cd-flb .pg {
width: 100%;
height: 100%;
padding: 30px 26px;
display: flex;
flex-direction: column;
position: relative;
}
.cd-flb .pg-warm {
color: #2a1f14;
}
.cd-flb .pg-cool {
color: #2a2432;
}
.cd-flb .pg-rose {
color: #2a1418;
}
.cd-flb .pg-sage {
color: #1a2a1e;
}
.cd-flb .pg-ink {
color: #e8d8c0;
}
.cd-flb .pg-num {
font-family: 'EB Garamond', serif;
font-size: 10px;
letter-spacing: 2px;
position: absolute;
bottom: 16px;
}
.cd-flb .pg-num.right {
right: 22px;
color: #8a6a4a;
}
.cd-flb .pg-num.left {
left: 22px;
color: #7a6898;
}
.cd-flb .pg-num.rose {
color: #8a4050;
}
.cd-flb .pg-num.sage {
color: #4a7a50;
}
.cd-flb .pg-num.ink {
color: rgba(200,180,140,0.4);
}
.cd-flb .page-front::after,
.cd-flb .page-back::after {
content: '';
position: absolute;
top: 0;
bottom: 0;
width: 40px;
pointer-events: none;
}
.cd-flb .page-front::after {
right: 0;
background: linear-gradient(to right, transparent, rgba(0,0,0,0.14));
}
.cd-flb .page-back::after {
left: 0;
background: linear-gradient(to left, transparent, rgba(0,0,0,0.14));
}
.cd-flb .left-cover::after {
content: '';
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 30px;
background: linear-gradient(to right, transparent, rgba(0,0,0,0.2));
}
.cd-flb .paper-warm {
background: #f5ece0;
}
.cd-flb .paper-cool {
background: #ede8f0;
}
.cd-flb .paper-rose {
background: #f5e8e8;
}
.cd-flb .paper-sage {
background: #e8f0e8;
}
.cd-flb .paper-ink {
background: #1a1510;
}
.cd-flb .spread-label {
font-family: 'EB Garamond', serif;
font-size: 9px;
letter-spacing: 4px;
text-transform: uppercase;
opacity: 0.4;
margin-bottom: 16px;
}
.cd-flb .spread-title {
font-family: 'Playfair Display', serif;
font-weight: 900;
line-height: 1.1;
margin-bottom: 14px;
}
.cd-flb .spread-title.sm {
font-size: 26px;
}
.cd-flb .spread-title.xs {
font-size: 22px;
}
.cd-flb .spread-title.rose {
color: #5a1828;
}
.cd-flb .spread-body {
font-family: 'EB Garamond', serif;
font-size: 13px;
line-height: 1.78;
opacity: 0.75;
}
.cd-flb .spread-body.mt {
margin-top: 10px;
}
.cd-flb .spread-body.alt {
color: #3a2a50;
}
.cd-flb .spread-body.rose-body {
color: #3a1820;
}
.cd-flb .spread-body.sage-body {
color: #2a3a26;
}
.cd-flb .spread-pullquote {
font-family: 'Playfair Display', serif;
font-style: italic;
font-size: 16px;
line-height: 1.5;
border-left: 2px solid currentColor;
padding-left: 14px;
margin: 16px 0;
opacity: 0.8;
}
.cd-flb .spread-pullquote.warm {
border-color: #8a6a4a;
color: #4a3424;
}
.cd-flb .spread-pullquote.sage {
border-color: #3a6a40;
color: #2a4a2e;
}
.cd-flb .spread-rule {
width: 40px;
height: 1px;
background: currentColor;
opacity: 0.3;
margin: 14px 0;
}
.cd-flb .spread-rule.alt {
background: #4a3868;
}
.cd-flb .spread-rule.rose {
background: #8a3040;
}
.cd-flb .spread-img {
width: 100%;
height: 100px;
border-radius: 4px;
margin: 14px 0;
overflow: hidden;
flex-shrink: 0;
}
.cd-flb .sage-img {
background: linear-gradient(135deg, #2a4a2e, #1a3020);
display: flex;
align-items: center;
justify-content: center;
}
.cd-flb .circle-outer {
width: 64px;
height: 64px;
border: 2px solid rgba(120,200,120,0.4);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.cd-flb .circle-inner {
width: 32px;
height: 32px;
background: rgba(120,200,120,0.2);
border-radius: 50%;
}
.cd-flb .spread-big-num {
font-family: 'Playfair Display', serif;
font-size: 80px;
font-weight: 900;
line-height: 0.9;
opacity: 0.07;
position: absolute;
right: 16px;
bottom: 30px;
}
/* Cover art */
.cd-flb .cover-art {
width: 100%;
height: 100%;
background: linear-gradient(148deg, #1a0f08, #0e0a06);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 26px;
border-radius: 4px 0 0 4px;
position: relative;
overflow: hidden;
}
.cd-flb .cover-art::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(ellipse 70% 60% at 50% 40%, rgba(180,120,40,0.12) 0%, transparent 70%);
}
.cd-flb .cover-eyebrow {
font-family: 'EB Garamond', serif;
font-size: 9px;
letter-spacing: 5px;
color: rgba(200,160,80,0.55);
margin-bottom: 26px;
z-index: 1;
}
.cd-flb .cover-rule {
width: 56px;
height: 1px;
background: rgba(200,160,80,0.35);
margin: 0 0 26px;
z-index: 1;
}
.cd-flb .cover-title {
font-family: 'Playfair Display', serif;
font-size: 34px;
font-weight: 900;
color: #f0e4cc;
text-align: center;
line-height: 1.1;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
}
.cd-flb .cover-vol {
font-style: italic;
font-weight: 400;
font-size: 20px;
opacity: 0.7;
margin-top: 6px;
}
.cd-flb .cover-rule:nth-of-type(2),
.cd-flb .cover-title + .cover-rule {
margin: 26px 0;
}
.cd-flb .cover-sub {
font-family: 'EB Garamond', serif;
font-style: italic;
font-size: 11px;
color: rgba(200,160,80,0.55);
letter-spacing: 1px;
z-index: 1;
}
.cd-flb .colophon-label {
font-family: 'EB Garamond', serif;
font-size: 9px;
letter-spacing: 4px;
opacity: 0.35;
margin-bottom: 18px;
}
.cd-flb .colophon-title {
font-family: 'Playfair Display', serif;
font-size: 18px;
font-weight: 700;
opacity: 0.9;
margin-bottom: 8px;
}
.cd-flb .colophon-sub {
font-family: 'Playfair Display', serif;
font-style: italic;
font-size: 12px;
opacity: 0.5;
margin-bottom: 22px;
}
.cd-flb .colophon-body {
font-family: 'EB Garamond', serif;
font-size: 11px;
line-height: 1.8;
opacity: 0.45;
}
.cd-flb .nav {
display: flex;
align-items: center;
gap: 18px;
margin-top: 26px;
position: relative;
z-index: 100;
}
.cd-flb .nav-btn {
display: flex;
align-items: center;
gap: 8px;
font-family: 'EB Garamond', serif;
font-size: 12px;
letter-spacing: 2px;
color: rgba(200,170,120,0.7);
background: none;
border: none;
cursor: pointer;
padding: 9px 18px;
border-radius: 4px;
transition: color 0.2s, background 0.2s;
}
.cd-flb .nav-btn:hover {
color: rgba(220,190,140,1);
background: rgba(200,170,120,0.08);
}
.cd-flb .nav-btn:disabled {
opacity: 0.3;
cursor: default;
}
.cd-flb .page-counter {
font-family: 'EB Garamond', serif;
font-size: 12px;
color: rgba(200,170,120,0.45);
letter-spacing: 2px;
min-width: 80px;
text-align: center;
}
.cd-flb .book-shadow {
position: absolute;
bottom: -18px;
left: 10%;
right: 10%;
height: 28px;
background: radial-gradient(ellipse at center, rgba(0,0,0,0.5) 0%, transparent 70%);
filter: blur(12px);
z-index: -1;
}
@media (max-width: 720px) {
.cd-flb .book {
width: 360px;
height: 280px;
}
.cd-flb .page,
.cd-flb .left-cover {
width: 180px;
height: 280px;
}
.cd-flb .page {
left: 180px;
}
.cd-flb .pg {
padding: 22px 18px;
}
.cd-flb .spread-title.sm {
font-size: 20px;
}
.cd-flb .spread-title.xs {
font-size: 18px;
}
.cd-flb .cover-title {
font-size: 26px;
}
}
@media (prefers-reduced-motion: reduce) {
.cd-flb .book-scene,
.cd-flb .page {
transition: none !important;
}
}
```
## JavaScript
```js
(() => {
const root = document.querySelector('.cd-flb');
if (!root) return;
const book = root.querySelector('[data-cd-flb-book]');
const prevBtn = root.querySelector('[data-cd-flb-prev]');
const nextBtn = root.querySelector('[data-cd-flb-next]');
const counter = root.querySelector('[data-cd-flb-counter]');
if (!book || !prevBtn || !nextBtn || !counter) return;
const pages = Array.from(root.querySelectorAll('.page[data-page]'));
const TOTAL_SPREADS = pages.length + 1;
let currentSpread = 0;
function update() {
pages.forEach((page, i) => {
page.classList.toggle('flipped', i < currentSpread);
page.style.zIndex = i < currentSpread ? i + 1 : pages.length - i + 4;
});
counter.textContent = `Spread ${currentSpread + 1} of ${TOTAL_SPREADS}`;
prevBtn.disabled = currentSpread === 0;
nextBtn.disabled = currentSpread === TOTAL_SPREADS - 1;
}
prevBtn.addEventListener('click', () => { if (currentSpread > 0) { currentSpread--; update(); } });
nextBtn.addEventListener('click', () => { if (currentSpread < TOTAL_SPREADS - 1) { currentSpread++; update(); } });
// Click halves of the book to advance — scoped to the book itself
book.addEventListener('click', e => {
if (e.target.closest('.nav-btn')) return;
const rect = book.getBoundingClientRect();
if (e.clientX > rect.left + rect.width / 2) {
if (currentSpread < TOTAL_SPREADS - 1) { currentSpread++; update(); }
} else {
if (currentSpread > 0) { currentSpread--; update(); }
}
});
// Keyboard — only when pointer is inside the wrapper
let pointerInside = false;
root.addEventListener('mouseenter', () => { pointerInside = true; });
root.addEventListener('mouseleave', () => { pointerInside = false; });
document.addEventListener('keydown', e => {
if (!pointerInside) return;
if (e.key === 'ArrowRight') { nextBtn.click(); e.preventDefault(); }
if (e.key === 'ArrowLeft') { prevBtn.click(); e.preventDefault(); }
});
update();
})();
```