11 CSS Page Transitions07 / 11
Ink Bleed
A Canvas ink drop radiates organically from the centre with seismic roughness — like sumi ink spreading on wet paper, covering, then receding. Set in a Japanese ceramics studio.
Published
This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.
The code
<canvas id="inkCanvas"></canvas>
<div class="pages">
<div class="page active" id="p0">
<div class="p0-left">
<div class="studio-mark">白泥窯 · Hakudei Kiln</div>
<div class="kanji-bg">器</div>
<div class="p0-hero">
<div class="p0-tagline">Handmade · Raku · Seasonal</div>
<h1 class="p0-title">Things made<br>to last</h1>
<div class="p0-title-jp">一期一会の器</div>
<p class="p0-body">Each piece is made once, in the way the clay demands. We do not repeat ourselves. We do not sell in quantity. We work with fire and patience.</p>
</div>
<div class="p0-footer">
<div class="season-badge">Autumn 2025</div>
<div style="font-size:11px;font-style:italic;color:var(--smoke)">Kyoto, Japan</div>
</div>
</div>
<div class="p0-right">
<div class="brush-art">
<div class="bowl-art">
<div class="ceramic-ring ring-1"></div>
<div class="ceramic-ring ring-2"></div>
<div class="ceramic-ring ring-3"></div>
<div class="ring-inner"></div>
<div class="stroke stroke-1"></div>
<div class="stroke stroke-2"></div>
<div class="stroke stroke-3"></div>
</div>
</div>
<div class="p0-caption">茶碗 · Chawan · Tea Bowl · Shino glaze · 2025</div>
</div>
</div>
<div class="page" id="p1">
<div class="p1-bg">器</div>
<div class="p1-eyebrow">Autumn Collection 2025</div>
<h2 class="p1-title">New work<br>from the kiln</h2>
<div class="p1-grid">
<div class="ceramic-card">
<div class="ceramic-thumb"><div class="c-shape"></div></div>
<div class="ceramic-name">Shino Chawan</div>
<div class="ceramic-series">Tea Series · No.01</div>
<div class="ceramic-price">¥ 68,000</div>
</div>
<div class="ceramic-card">
<div class="ceramic-thumb" style="aspect-ratio:1"><div class="c-shape" style="border-radius:50%"></div></div>
<div class="ceramic-name">Ash Katakuchi</div>
<div class="ceramic-series">Vessel Series · No.04</div>
<div class="ceramic-price">¥ 42,000</div>
</div>
<div class="ceramic-card">
<div class="ceramic-thumb"><div class="c-shape" style="height:100px;border-radius:20% 20% 50% 50%"></div></div>
<div class="ceramic-name">Raku Guinomi</div>
<div class="ceramic-series">Sake Series · No.07</div>
<div class="ceramic-price">¥ 24,000</div>
</div>
<div class="ceramic-card">
<div class="ceramic-thumb"><div class="c-shape" style="width:80px;height:60px;border-radius:50%"></div></div>
<div class="ceramic-name">Oribe Hachi</div>
<div class="ceramic-series">Bowl Series · No.02</div>
<div class="ceramic-price">¥ 38,000</div>
</div>
</div>
</div>
<div class="page" id="p2">
<div class="p2-left">
<div class="p2-eyebrow">The Process</div>
<h2 class="p2-title">Made by<br>hand and<br>fire</h2>
<p class="p2-body">Nothing here is hurried. The kiln dictates the schedule, not the calendar. Each firing takes three days and reveals results that cannot be predicted or reproduced.</p>
<div class="process-list">
<div class="proc-item"><div class="proc-num">一</div><div class="proc-text">Clay reclaimed from mountain sources and aged for one year</div></div>
<div class="proc-item"><div class="proc-num">二</div><div class="proc-text">Hand-thrown or slab-built, each piece unique in form</div></div>
<div class="proc-item"><div class="proc-num">三</div><div class="proc-text">Wood-fired in anagama kiln at cone 12 over 60 hours</div></div>
<div class="proc-item"><div class="proc-num">四</div><div class="proc-text">Unboxed by hand and inspected. Most pieces are not kept.</div></div>
</div>
</div>
<div class="p2-right">
<div class="form-lg"></div>
<div class="form-sm"></div>
<div style="font-size:10px;letter-spacing:0.2em;color:var(--smoke);text-align:center;font-style:italic">木灰釉薬 · Wood-ash glaze</div>
</div>
</div>
<div class="page" id="p3">
<div class="p3-bg"></div>
<div class="p3-content">
<div class="p3-jp">白泥窯 · 一九八九年創業</div>
<h2 class="p3-title">Making is<br>a form of<br><em>listening</em></h2>
<blockquote class="p3-quote">The clay knows what it wants to become. Our work is only to be present for long enough to hear it — and skilled enough to help it arrive.</blockquote>
<div class="p3-meta">
<div class="p3-meta-item"><div class="p3-meta-num">36</div><div class="p3-meta-label">Years at the kiln</div></div>
<div class="p3-meta-item"><div class="p3-meta-num">4</div><div class="p3-meta-label">Annual firings</div></div>
<div class="p3-meta-item"><div class="p3-meta-num">~60</div><div class="p3-meta-label">Pieces kept per year</div></div>
</div>
</div>
</div>
</div>
<div class="dot-nav" id="dotNav">
<button class="dot active" data-i="0"></button>
<button class="dot" data-i="1"></button>
<button class="dot" data-i="2"></button>
<button class="dot" data-i="3"></button>
</div><canvas id="inkCanvas"></canvas>
<div class="pages">
<div class="page active" id="p0">
<div class="p0-left">
<div class="studio-mark">白泥窯 · Hakudei Kiln</div>
<div class="kanji-bg">器</div>
<div class="p0-hero">
<div class="p0-tagline">Handmade · Raku · Seasonal</div>
<h1 class="p0-title">Things made<br>to last</h1>
<div class="p0-title-jp">一期一会の器</div>
<p class="p0-body">Each piece is made once, in the way the clay demands. We do not repeat ourselves. We do not sell in quantity. We work with fire and patience.</p>
</div>
<div class="p0-footer">
<div class="season-badge">Autumn 2025</div>
<div style="font-size:11px;font-style:italic;color:var(--smoke)">Kyoto, Japan</div>
</div>
</div>
<div class="p0-right">
<div class="brush-art">
<div class="bowl-art">
<div class="ceramic-ring ring-1"></div>
<div class="ceramic-ring ring-2"></div>
<div class="ceramic-ring ring-3"></div>
<div class="ring-inner"></div>
<div class="stroke stroke-1"></div>
<div class="stroke stroke-2"></div>
<div class="stroke stroke-3"></div>
</div>
</div>
<div class="p0-caption">茶碗 · Chawan · Tea Bowl · Shino glaze · 2025</div>
</div>
</div>
<div class="page" id="p1">
<div class="p1-bg">器</div>
<div class="p1-eyebrow">Autumn Collection 2025</div>
<h2 class="p1-title">New work<br>from the kiln</h2>
<div class="p1-grid">
<div class="ceramic-card">
<div class="ceramic-thumb"><div class="c-shape"></div></div>
<div class="ceramic-name">Shino Chawan</div>
<div class="ceramic-series">Tea Series · No.01</div>
<div class="ceramic-price">¥ 68,000</div>
</div>
<div class="ceramic-card">
<div class="ceramic-thumb" style="aspect-ratio:1"><div class="c-shape" style="border-radius:50%"></div></div>
<div class="ceramic-name">Ash Katakuchi</div>
<div class="ceramic-series">Vessel Series · No.04</div>
<div class="ceramic-price">¥ 42,000</div>
</div>
<div class="ceramic-card">
<div class="ceramic-thumb"><div class="c-shape" style="height:100px;border-radius:20% 20% 50% 50%"></div></div>
<div class="ceramic-name">Raku Guinomi</div>
<div class="ceramic-series">Sake Series · No.07</div>
<div class="ceramic-price">¥ 24,000</div>
</div>
<div class="ceramic-card">
<div class="ceramic-thumb"><div class="c-shape" style="width:80px;height:60px;border-radius:50%"></div></div>
<div class="ceramic-name">Oribe Hachi</div>
<div class="ceramic-series">Bowl Series · No.02</div>
<div class="ceramic-price">¥ 38,000</div>
</div>
</div>
</div>
<div class="page" id="p2">
<div class="p2-left">
<div class="p2-eyebrow">The Process</div>
<h2 class="p2-title">Made by<br>hand and<br>fire</h2>
<p class="p2-body">Nothing here is hurried. The kiln dictates the schedule, not the calendar. Each firing takes three days and reveals results that cannot be predicted or reproduced.</p>
<div class="process-list">
<div class="proc-item"><div class="proc-num">一</div><div class="proc-text">Clay reclaimed from mountain sources and aged for one year</div></div>
<div class="proc-item"><div class="proc-num">二</div><div class="proc-text">Hand-thrown or slab-built, each piece unique in form</div></div>
<div class="proc-item"><div class="proc-num">三</div><div class="proc-text">Wood-fired in anagama kiln at cone 12 over 60 hours</div></div>
<div class="proc-item"><div class="proc-num">四</div><div class="proc-text">Unboxed by hand and inspected. Most pieces are not kept.</div></div>
</div>
</div>
<div class="p2-right">
<div class="form-lg"></div>
<div class="form-sm"></div>
<div style="font-size:10px;letter-spacing:0.2em;color:var(--smoke);text-align:center;font-style:italic">木灰釉薬 · Wood-ash glaze</div>
</div>
</div>
<div class="page" id="p3">
<div class="p3-bg"></div>
<div class="p3-content">
<div class="p3-jp">白泥窯 · 一九八九年創業</div>
<h2 class="p3-title">Making is<br>a form of<br><em>listening</em></h2>
<blockquote class="p3-quote">The clay knows what it wants to become. Our work is only to be present for long enough to hear it — and skilled enough to help it arrive.</blockquote>
<div class="p3-meta">
<div class="p3-meta-item"><div class="p3-meta-num">36</div><div class="p3-meta-label">Years at the kiln</div></div>
<div class="p3-meta-item"><div class="p3-meta-num">4</div><div class="p3-meta-label">Annual firings</div></div>
<div class="p3-meta-item"><div class="p3-meta-num">~60</div><div class="p3-meta-label">Pieces kept per year</div></div>
</div>
</div>
</div>
</div>
<div class="dot-nav" id="dotNav">
<button class="dot active" data-i="0"></button>
<button class="dot" data-i="1"></button>
<button class="dot" data-i="2"></button>
<button class="dot" data-i="3"></button>
</div>*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--ash: #e8e2d8;
--pale: #f4f0ea;
--clay: #c4a882;
--smoke: #8a8070;
--ink: #1a1410;
--charcoal: #2a2420;
}
html,
body {
height: 100%;
overflow: hidden;
background: var(--pale);
font-family: 'Cormorant',serif;
color: var(--ink);
}
/* Ink canvas overlay */
#inkCanvas {
position: fixed;
inset: 0;
z-index: 800;
pointer-events: none;
}
/* Pages */
.pages {
position: fixed;
inset: 0;
}
.page {
position: absolute;
inset: 0;
visibility: hidden;
pointer-events: none;
}
.page.active {
visibility: visible;
pointer-events: auto;
}
/* PAGE 0 — STUDIO */
#p0 {
background: var(--pale);
display: grid;
grid-template-columns: 1fr 1fr;
}
.p0-left {
padding: 80px 70px;
display: flex;
flex-direction: column;
justify-content: space-between;
border-right: 1px solid rgba(26,20,16,0.08);
position: relative;
}
.studio-mark {
font-family: 'Noto Serif JP',serif;
font-size: 13px;
letter-spacing: 0.2em;
color: rgba(26,20,16,0.3);
font-weight: 200;
}
.p0-hero {
flex: 1;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding-bottom: 20px;
}
.kanji-bg {
position: absolute;
font-family: 'Noto Serif JP',serif;
font-size: 40vw;
font-weight: 200;
color: rgba(26,20,16,0.025);
line-height: 1;
top: 50%;
left: 50%;
transform: translate(-35%,-55%);
pointer-events: none;
user-select: none;
}
.p0-tagline {
font-size: 9px;
letter-spacing: 0.35em;
text-transform: uppercase;
color: var(--smoke);
margin-bottom: 20px;
}
.p0-title {
font-size: clamp(48px,7vw,88px);
font-weight: 300;
line-height: 0.95;
letter-spacing: -0.02em;
color: var(--ink);
margin-bottom: 20px;
position: relative;
}
.p0-title-jp {
font-family: 'Noto Serif JP',serif;
font-size: 11px;
letter-spacing: 0.2em;
color: var(--clay);
font-weight: 300;
margin-bottom: 32px;
}
.p0-body {
font-size: 15px;
line-height: 1.8;
font-weight: 300;
color: rgba(26,20,16,0.5);
max-width: 360px;
font-style: italic;
}
.p0-footer {
display: flex;
align-items: center;
justify-content: space-between;
}
.season-badge {
font-size: 9px;
letter-spacing: 0.25em;
text-transform: uppercase;
color: var(--clay);
border: 1px solid rgba(196,168,130,0.4);
padding: 6px 16px;
}
.p0-right {
display: flex;
flex-direction: column;
background: var(--ash);
position: relative;
overflow: hidden;
}
/* Sumi-e brush artwork area */
.brush-art {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.bowl-art {
position: relative;
width: 240px;
height: 240px;
}
.ceramic-ring {
position: absolute;
border-radius: 50%;
border: 1px solid rgba(26,20,16,0.12);
}
.ring-1 {
inset: 0;
background: rgba(196,168,130,0.15);
}
.ring-2 {
inset: 16px;
background: rgba(196,168,130,0.1);
border-color: rgba(26,20,16,0.08);
}
.ring-3 {
inset: 40px;
background: rgba(196,168,130,0.08);
border-color: rgba(26,20,16,0.05);
}
.ring-inner {
position: absolute;
inset: 80px;
border-radius: 50%;
background: var(--ink);
opacity: 0.06;
}
.stroke {
position: absolute;
background: rgba(26,20,16,0.12);
border-radius: 50% 20% 50% 20%;
transform-origin: center;
}
.stroke-1 {
width: 3px;
height: 80px;
top: -40px;
left: 50%;
transform: translateX(-50%) rotate(-10deg);
}
.stroke-2 {
width: 2px;
height: 60px;
top: 20px;
right: 20px;
transform: rotate(35deg);
opacity: 0.6;
}
.stroke-3 {
width: 2px;
height: 40px;
bottom: 20px;
left: 24px;
transform: rotate(-20deg);
opacity: 0.4;
}
.p0-caption {
padding: 32px 48px;
border-top: 1px solid rgba(26,20,16,0.08);
font-size: 11px;
letter-spacing: 0.12em;
color: rgba(26,20,16,0.35);
text-align: center;
font-style: italic;
}
/* PAGE 1 — COLLECTION */
#p1 {
background: var(--charcoal);
color: var(--ash);
display: flex;
flex-direction: column;
padding: 80px;
position: relative;
overflow: hidden;
}
.p1-bg {
position: absolute;
font-family: 'Noto Serif JP',serif;
font-size: 500px;
font-weight: 200;
color: rgba(255,255,255,0.015);
line-height: 1;
bottom: -100px;
right: -60px;
pointer-events: none;
}
.p1-eyebrow {
font-size: 9px;
letter-spacing: 0.35em;
text-transform: uppercase;
color: var(--clay);
margin-bottom: 40px;
position: relative;
z-index: 1;
display: flex;
align-items: center;
gap: 12px;
}
.p1-eyebrow::after {
content: '';
flex: 1;
height: 1px;
background: rgba(196,168,130,0.2);
}
.p1-title {
font-size: clamp(40px,6vw,72px);
font-weight: 300;
color: var(--ash);
letter-spacing: -0.02em;
line-height: 1.05;
margin-bottom: 60px;
position: relative;
z-index: 1;
}
.p1-grid {
display: grid;
grid-template-columns: repeat(4,1fr);
gap: 24px;
flex: 1;
align-content: start;
position: relative;
z-index: 1;
}
.ceramic-card {
display: flex;
flex-direction: column;
gap: 12px;
cursor: pointer;
transition: transform .3s;
}
.ceramic-card:hover {
transform: translateY(-4px);
}
.ceramic-thumb {
aspect-ratio: 3/4;
background: rgba(255,255,255,0.03);
border: 1px solid rgba(232,226,216,0.08);
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
transition: border-color .3s;
}
.ceramic-card:hover .ceramic-thumb {
border-color: rgba(196,168,130,0.3);
}
.c-shape {
width: 60px;
height: 80px;
background: linear-gradient(160deg,rgba(196,168,130,0.3),rgba(196,168,130,0.1));
border-radius: 50% 50% 40% 40%;
border: 1px solid rgba(196,168,130,0.2);
position: relative;
}
.c-shape::before {
content: '';
position: absolute;
top: -8px;
left: 50%;
transform: translateX(-50%);
width: 24px;
height: 12px;
background: rgba(196,168,130,0.25);
border-radius: 50% 50% 0 0;
border: 1px solid rgba(196,168,130,0.2);
border-bottom: none;
}
.ceramic-name {
font-size: 13px;
font-weight: 300;
color: var(--ash);
letter-spacing: 0.04em;
}
.ceramic-series {
font-size: 9px;
letter-spacing: 0.2em;
text-transform: uppercase;
color: rgba(232,226,216,0.3);
}
.ceramic-price {
font-size: 11px;
color: var(--clay);
letter-spacing: 0.08em;
}
/* PAGE 2 — PROCESS */
#p2 {
background: var(--pale);
display: grid;
grid-template-columns: 480px 1fr;
}
.p2-left {
padding: 80px 70px;
display: flex;
flex-direction: column;
justify-content: center;
border-right: 1px solid rgba(26,20,16,0.08);
}
.p2-eyebrow {
font-size: 9px;
letter-spacing: 0.35em;
text-transform: uppercase;
color: var(--clay);
margin-bottom: 28px;
}
.p2-title {
font-size: clamp(36px,5vw,64px);
font-weight: 300;
color: var(--ink);
letter-spacing: -0.02em;
line-height: 1.05;
margin-bottom: 24px;
}
.p2-body {
font-size: 15px;
line-height: 1.85;
font-weight: 300;
font-style: italic;
color: rgba(26,20,16,0.5);
margin-bottom: 48px;
}
.process-list {
display: flex;
flex-direction: column;
gap: 20px;
}
.proc-item {
display: flex;
align-items: flex-start;
gap: 20px;
padding-bottom: 20px;
border-bottom: 1px solid rgba(26,20,16,0.06);
}
.proc-num {
font-family: 'Noto Serif JP',serif;
font-size: 9px;
color: var(--clay);
width: 20px;
flex-shrink: 0;
padding-top: 2px;
font-weight: 300;
}
.proc-text {
font-size: 13px;
line-height: 1.7;
color: rgba(26,20,16,0.5);
letter-spacing: 0.03em;
}
.p2-right {
background: var(--ash);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px;
position: relative;
overflow: hidden;
gap: 48px;
}
.form-lg {
width: 160px;
height: 220px;
background: linear-gradient(160deg,rgba(196,168,130,0.4),rgba(26,20,16,0.06));
border-radius: 50% 50% 40% 40%;
border: 1px solid rgba(196,168,130,0.3);
box-shadow: 12px 24px 40px rgba(26,20,16,0.08);
}
.form-sm {
width: 100px;
height: 60px;
background: linear-gradient(160deg,rgba(196,168,130,0.3),rgba(26,20,16,0.04));
border-radius: 50%;
border: 1px solid rgba(196,168,130,0.2);
}
/* PAGE 3 — ABOUT */
#p3 {
background: var(--ink);
color: var(--ash);
display: flex;
align-items: center;
justify-content: center;
padding: 80px;
position: relative;
overflow: hidden;
}
.p3-bg {
position: absolute;
inset: 0;
background: radial-gradient(ellipse 60% 60% at 30% 50%,rgba(196,168,130,0.04),transparent);
}
.p3-content {
max-width: 640px;
position: relative;
}
.p3-jp {
font-family: 'Noto Serif JP',serif;
font-size: 11px;
letter-spacing: 0.3em;
font-weight: 200;
color: rgba(196,168,130,0.5);
margin-bottom: 32px;
}
.p3-title {
font-size: clamp(36px,6vw,72px);
font-weight: 300;
color: var(--ash);
line-height: 1.05;
letter-spacing: -0.02em;
margin-bottom: 32px;
}
.p3-title em {
font-style: italic;
color: var(--clay);
}
.p3-quote {
font-size: 17px;
line-height: 1.8;
font-style: italic;
font-weight: 300;
color: rgba(232,226,216,0.55);
border-left: 2px solid rgba(196,168,130,0.3);
padding-left: 24px;
margin-bottom: 40px;
}
.p3-meta {
display: flex;
gap: 48px;
padding-top: 32px;
border-top: 1px solid rgba(232,226,216,0.08);
}
.p3-meta-item {
display: flex;
flex-direction: column;
gap: 6px;
}
.p3-meta-num {
font-size: 32px;
font-weight: 300;
color: var(--clay);
}
.p3-meta-label {
font-size: 9px;
letter-spacing: 0.25em;
text-transform: uppercase;
color: rgba(232,226,216,0.25);
}
/* Nav */
.dot-nav {
position: fixed;
bottom: 40px;
left: 50%;
transform: translateX(-50%);
z-index: 900;
display: flex;
gap: 10px;
align-items: center;
}
.dot {
width: 5px;
height: 5px;
border-radius: 50%;
background: rgba(26,20,16,0.2);
border: none;
cursor: pointer;
transition: transform .35s,background .25s;
}
.dot.on-dark {
background: rgba(232,226,216,0.2);
}
.dot.active {
transform: scale(1.8);
background: var(--clay);
}
.dot.on-dark.active {
background: var(--clay);
}
@media (max-width: 768px) {
#p0,
#p2 {
grid-template-columns: 1fr;
}
.p0-right,
.p2-right {
display: none;
}
.p0-left,
.p2-left,
.p3-content {
padding: 48px 28px;
}
#p1 {
padding: 48px 28px;
}
.p1-grid {
grid-template-columns: 1fr 1fr;
}
.p3-meta {
gap: 24px;
}
}*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--ash: #e8e2d8;
--pale: #f4f0ea;
--clay: #c4a882;
--smoke: #8a8070;
--ink: #1a1410;
--charcoal: #2a2420;
}
html,
body {
height: 100%;
overflow: hidden;
background: var(--pale);
font-family: 'Cormorant',serif;
color: var(--ink);
}
/* Ink canvas overlay */
#inkCanvas {
position: fixed;
inset: 0;
z-index: 800;
pointer-events: none;
}
/* Pages */
.pages {
position: fixed;
inset: 0;
}
.page {
position: absolute;
inset: 0;
visibility: hidden;
pointer-events: none;
}
.page.active {
visibility: visible;
pointer-events: auto;
}
/* PAGE 0 — STUDIO */
#p0 {
background: var(--pale);
display: grid;
grid-template-columns: 1fr 1fr;
}
.p0-left {
padding: 80px 70px;
display: flex;
flex-direction: column;
justify-content: space-between;
border-right: 1px solid rgba(26,20,16,0.08);
position: relative;
}
.studio-mark {
font-family: 'Noto Serif JP',serif;
font-size: 13px;
letter-spacing: 0.2em;
color: rgba(26,20,16,0.3);
font-weight: 200;
}
.p0-hero {
flex: 1;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding-bottom: 20px;
}
.kanji-bg {
position: absolute;
font-family: 'Noto Serif JP',serif;
font-size: 40vw;
font-weight: 200;
color: rgba(26,20,16,0.025);
line-height: 1;
top: 50%;
left: 50%;
transform: translate(-35%,-55%);
pointer-events: none;
user-select: none;
}
.p0-tagline {
font-size: 9px;
letter-spacing: 0.35em;
text-transform: uppercase;
color: var(--smoke);
margin-bottom: 20px;
}
.p0-title {
font-size: clamp(48px,7vw,88px);
font-weight: 300;
line-height: 0.95;
letter-spacing: -0.02em;
color: var(--ink);
margin-bottom: 20px;
position: relative;
}
.p0-title-jp {
font-family: 'Noto Serif JP',serif;
font-size: 11px;
letter-spacing: 0.2em;
color: var(--clay);
font-weight: 300;
margin-bottom: 32px;
}
.p0-body {
font-size: 15px;
line-height: 1.8;
font-weight: 300;
color: rgba(26,20,16,0.5);
max-width: 360px;
font-style: italic;
}
.p0-footer {
display: flex;
align-items: center;
justify-content: space-between;
}
.season-badge {
font-size: 9px;
letter-spacing: 0.25em;
text-transform: uppercase;
color: var(--clay);
border: 1px solid rgba(196,168,130,0.4);
padding: 6px 16px;
}
.p0-right {
display: flex;
flex-direction: column;
background: var(--ash);
position: relative;
overflow: hidden;
}
/* Sumi-e brush artwork area */
.brush-art {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.bowl-art {
position: relative;
width: 240px;
height: 240px;
}
.ceramic-ring {
position: absolute;
border-radius: 50%;
border: 1px solid rgba(26,20,16,0.12);
}
.ring-1 {
inset: 0;
background: rgba(196,168,130,0.15);
}
.ring-2 {
inset: 16px;
background: rgba(196,168,130,0.1);
border-color: rgba(26,20,16,0.08);
}
.ring-3 {
inset: 40px;
background: rgba(196,168,130,0.08);
border-color: rgba(26,20,16,0.05);
}
.ring-inner {
position: absolute;
inset: 80px;
border-radius: 50%;
background: var(--ink);
opacity: 0.06;
}
.stroke {
position: absolute;
background: rgba(26,20,16,0.12);
border-radius: 50% 20% 50% 20%;
transform-origin: center;
}
.stroke-1 {
width: 3px;
height: 80px;
top: -40px;
left: 50%;
transform: translateX(-50%) rotate(-10deg);
}
.stroke-2 {
width: 2px;
height: 60px;
top: 20px;
right: 20px;
transform: rotate(35deg);
opacity: 0.6;
}
.stroke-3 {
width: 2px;
height: 40px;
bottom: 20px;
left: 24px;
transform: rotate(-20deg);
opacity: 0.4;
}
.p0-caption {
padding: 32px 48px;
border-top: 1px solid rgba(26,20,16,0.08);
font-size: 11px;
letter-spacing: 0.12em;
color: rgba(26,20,16,0.35);
text-align: center;
font-style: italic;
}
/* PAGE 1 — COLLECTION */
#p1 {
background: var(--charcoal);
color: var(--ash);
display: flex;
flex-direction: column;
padding: 80px;
position: relative;
overflow: hidden;
}
.p1-bg {
position: absolute;
font-family: 'Noto Serif JP',serif;
font-size: 500px;
font-weight: 200;
color: rgba(255,255,255,0.015);
line-height: 1;
bottom: -100px;
right: -60px;
pointer-events: none;
}
.p1-eyebrow {
font-size: 9px;
letter-spacing: 0.35em;
text-transform: uppercase;
color: var(--clay);
margin-bottom: 40px;
position: relative;
z-index: 1;
display: flex;
align-items: center;
gap: 12px;
}
.p1-eyebrow::after {
content: '';
flex: 1;
height: 1px;
background: rgba(196,168,130,0.2);
}
.p1-title {
font-size: clamp(40px,6vw,72px);
font-weight: 300;
color: var(--ash);
letter-spacing: -0.02em;
line-height: 1.05;
margin-bottom: 60px;
position: relative;
z-index: 1;
}
.p1-grid {
display: grid;
grid-template-columns: repeat(4,1fr);
gap: 24px;
flex: 1;
align-content: start;
position: relative;
z-index: 1;
}
.ceramic-card {
display: flex;
flex-direction: column;
gap: 12px;
cursor: pointer;
transition: transform .3s;
}
.ceramic-card:hover {
transform: translateY(-4px);
}
.ceramic-thumb {
aspect-ratio: 3/4;
background: rgba(255,255,255,0.03);
border: 1px solid rgba(232,226,216,0.08);
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
transition: border-color .3s;
}
.ceramic-card:hover .ceramic-thumb {
border-color: rgba(196,168,130,0.3);
}
.c-shape {
width: 60px;
height: 80px;
background: linear-gradient(160deg,rgba(196,168,130,0.3),rgba(196,168,130,0.1));
border-radius: 50% 50% 40% 40%;
border: 1px solid rgba(196,168,130,0.2);
position: relative;
}
.c-shape::before {
content: '';
position: absolute;
top: -8px;
left: 50%;
transform: translateX(-50%);
width: 24px;
height: 12px;
background: rgba(196,168,130,0.25);
border-radius: 50% 50% 0 0;
border: 1px solid rgba(196,168,130,0.2);
border-bottom: none;
}
.ceramic-name {
font-size: 13px;
font-weight: 300;
color: var(--ash);
letter-spacing: 0.04em;
}
.ceramic-series {
font-size: 9px;
letter-spacing: 0.2em;
text-transform: uppercase;
color: rgba(232,226,216,0.3);
}
.ceramic-price {
font-size: 11px;
color: var(--clay);
letter-spacing: 0.08em;
}
/* PAGE 2 — PROCESS */
#p2 {
background: var(--pale);
display: grid;
grid-template-columns: 480px 1fr;
}
.p2-left {
padding: 80px 70px;
display: flex;
flex-direction: column;
justify-content: center;
border-right: 1px solid rgba(26,20,16,0.08);
}
.p2-eyebrow {
font-size: 9px;
letter-spacing: 0.35em;
text-transform: uppercase;
color: var(--clay);
margin-bottom: 28px;
}
.p2-title {
font-size: clamp(36px,5vw,64px);
font-weight: 300;
color: var(--ink);
letter-spacing: -0.02em;
line-height: 1.05;
margin-bottom: 24px;
}
.p2-body {
font-size: 15px;
line-height: 1.85;
font-weight: 300;
font-style: italic;
color: rgba(26,20,16,0.5);
margin-bottom: 48px;
}
.process-list {
display: flex;
flex-direction: column;
gap: 20px;
}
.proc-item {
display: flex;
align-items: flex-start;
gap: 20px;
padding-bottom: 20px;
border-bottom: 1px solid rgba(26,20,16,0.06);
}
.proc-num {
font-family: 'Noto Serif JP',serif;
font-size: 9px;
color: var(--clay);
width: 20px;
flex-shrink: 0;
padding-top: 2px;
font-weight: 300;
}
.proc-text {
font-size: 13px;
line-height: 1.7;
color: rgba(26,20,16,0.5);
letter-spacing: 0.03em;
}
.p2-right {
background: var(--ash);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px;
position: relative;
overflow: hidden;
gap: 48px;
}
.form-lg {
width: 160px;
height: 220px;
background: linear-gradient(160deg,rgba(196,168,130,0.4),rgba(26,20,16,0.06));
border-radius: 50% 50% 40% 40%;
border: 1px solid rgba(196,168,130,0.3);
box-shadow: 12px 24px 40px rgba(26,20,16,0.08);
}
.form-sm {
width: 100px;
height: 60px;
background: linear-gradient(160deg,rgba(196,168,130,0.3),rgba(26,20,16,0.04));
border-radius: 50%;
border: 1px solid rgba(196,168,130,0.2);
}
/* PAGE 3 — ABOUT */
#p3 {
background: var(--ink);
color: var(--ash);
display: flex;
align-items: center;
justify-content: center;
padding: 80px;
position: relative;
overflow: hidden;
}
.p3-bg {
position: absolute;
inset: 0;
background: radial-gradient(ellipse 60% 60% at 30% 50%,rgba(196,168,130,0.04),transparent);
}
.p3-content {
max-width: 640px;
position: relative;
}
.p3-jp {
font-family: 'Noto Serif JP',serif;
font-size: 11px;
letter-spacing: 0.3em;
font-weight: 200;
color: rgba(196,168,130,0.5);
margin-bottom: 32px;
}
.p3-title {
font-size: clamp(36px,6vw,72px);
font-weight: 300;
color: var(--ash);
line-height: 1.05;
letter-spacing: -0.02em;
margin-bottom: 32px;
}
.p3-title em {
font-style: italic;
color: var(--clay);
}
.p3-quote {
font-size: 17px;
line-height: 1.8;
font-style: italic;
font-weight: 300;
color: rgba(232,226,216,0.55);
border-left: 2px solid rgba(196,168,130,0.3);
padding-left: 24px;
margin-bottom: 40px;
}
.p3-meta {
display: flex;
gap: 48px;
padding-top: 32px;
border-top: 1px solid rgba(232,226,216,0.08);
}
.p3-meta-item {
display: flex;
flex-direction: column;
gap: 6px;
}
.p3-meta-num {
font-size: 32px;
font-weight: 300;
color: var(--clay);
}
.p3-meta-label {
font-size: 9px;
letter-spacing: 0.25em;
text-transform: uppercase;
color: rgba(232,226,216,0.25);
}
/* Nav */
.dot-nav {
position: fixed;
bottom: 40px;
left: 50%;
transform: translateX(-50%);
z-index: 900;
display: flex;
gap: 10px;
align-items: center;
}
.dot {
width: 5px;
height: 5px;
border-radius: 50%;
background: rgba(26,20,16,0.2);
border: none;
cursor: pointer;
transition: transform .35s,background .25s;
}
.dot.on-dark {
background: rgba(232,226,216,0.2);
}
.dot.active {
transform: scale(1.8);
background: var(--clay);
}
.dot.on-dark.active {
background: var(--clay);
}
@media (max-width: 768px) {
#p0,
#p2 {
grid-template-columns: 1fr;
}
.p0-right,
.p2-right {
display: none;
}
.p0-left,
.p2-left,
.p3-content {
padding: 48px 28px;
}
#p1 {
padding: 48px 28px;
}
.p1-grid {
grid-template-columns: 1fr 1fr;
}
.p3-meta {
gap: 24px;
}
}const canvas=document.getElementById('inkCanvas');
const ctx=canvas.getContext('2d');
const pages=document.querySelectorAll('.page');
const dots=document.querySelectorAll('.dot');
const darkPages=[1,3];
let cur=0,busy=false;
function resize(){
canvas.width=window.innerWidth;
canvas.height=window.innerHeight;
}
resize();
window.addEventListener('resize',resize);
function inkTransition(x,y,color,midCallback,done){
const W=canvas.width,H=canvas.height;
const maxR=Math.sqrt(W*W+H*H)*0.6;
let r=0,phase=0;
const speed=28;
const roughnessSeeds=Array.from({length:16},(_,i)=>({
angle:i/16*Math.PI*2,
amp:Math.random()*0.15+0.05,
freq:Math.random()*3+2,
phase:Math.random()*Math.PI*2
}));
function draw(){
ctx.clearRect(0,0,W,H);
if(phase===0&&r>maxR*0.45&&!midCallback._called){
midCallback();midCallback._called=true;
}
if(phase===1&&r<=0){
ctx.clearRect(0,0,W,H);done&&done();return;
}
ctx.beginPath();
const steps=120;
for(let i=0;i<=steps;i++){
const a=i/steps*Math.PI*2;
let rad=r;
roughnessSeeds.forEach(s=>{
rad+=r*s.amp*Math.sin(a*s.freq+s.phase+(phase===0?r*0.01:-r*0.01));
});
rad=Math.max(0,rad);
const px=x+Math.cos(a)*rad;
const py=y+Math.sin(a)*rad;
i===0?ctx.moveTo(px,py):ctx.lineTo(px,py);
}
ctx.closePath();
const g=ctx.createRadialGradient(x,y,0,x,y,r*1.1);
g.addColorStop(0,color);
g.addColorStop(0.7,color);
g.addColorStop(1,'transparent');
ctx.fillStyle=g;
ctx.fill();
ctx.beginPath();
for(let i=0;i<=steps;i++){
const a=i/steps*Math.PI*2;
let rad=r;
roughnessSeeds.forEach(s=>{rad+=r*s.amp*Math.sin(a*s.freq+s.phase+(phase===0?r*0.01:-r*0.01));});
const edgeR=rad*0.92;
const px=x+Math.cos(a)*edgeR;
const py=y+Math.sin(a)*edgeR;
i===0?ctx.moveTo(px,py):ctx.lineTo(px,py);
}
ctx.closePath();
ctx.fillStyle='rgba(0,0,0,0.08)';
ctx.fill();
if(phase===0){
r+=speed;
if(r>=maxR){phase=1;}
}else{
r-=speed*1.4;
}
requestAnimationFrame(draw);
}
draw();
}
const inkColors=['#1a1410','#e8e2d8','#1a1410','#1a1410'];
function goTo(n,ex,ey){
if(busy||n===cur)return;
busy=true;
const prev=cur;cur=n;
const x=ex||window.innerWidth/2;
const y=ey||window.innerHeight/2;
const color=inkColors[cur];
const mid=()=>{
pages[prev].classList.remove('active');
pages[cur].classList.add('active');
dots.forEach((d,i)=>{
d.classList.toggle('active',i===cur);
d.classList.toggle('on-dark',darkPages.includes(cur));
});
};
mid._called=false;
inkTransition(x,y,color,mid,()=>{busy=false;});
}
dots.forEach(d=>d.addEventListener('click',e=>{
const r=e.currentTarget.getBoundingClientRect();
goTo(+d.dataset.i,r.left+r.width/2,r.top+r.height/2);
}));
let wt=0;
window.addEventListener('wheel',e=>{
const now=Date.now();if(now-wt<1000)return;wt=now;
goTo(Math.max(0,Math.min(3,cur+(e.deltaY>0?1:-1))),window.innerWidth/2,window.innerHeight/2);
},{passive:true});
document.addEventListener('keydown',e=>{
const c=window.innerWidth/2,m=window.innerHeight/2;
if(e.key==='ArrowRight')goTo(Math.min(3,cur+1),c,m);
if(e.key==='ArrowLeft')goTo(Math.max(0,cur-1),c,m);
});const canvas=document.getElementById('inkCanvas');
const ctx=canvas.getContext('2d');
const pages=document.querySelectorAll('.page');
const dots=document.querySelectorAll('.dot');
const darkPages=[1,3];
let cur=0,busy=false;
function resize(){
canvas.width=window.innerWidth;
canvas.height=window.innerHeight;
}
resize();
window.addEventListener('resize',resize);
function inkTransition(x,y,color,midCallback,done){
const W=canvas.width,H=canvas.height;
const maxR=Math.sqrt(W*W+H*H)*0.6;
let r=0,phase=0;
const speed=28;
const roughnessSeeds=Array.from({length:16},(_,i)=>({
angle:i/16*Math.PI*2,
amp:Math.random()*0.15+0.05,
freq:Math.random()*3+2,
phase:Math.random()*Math.PI*2
}));
function draw(){
ctx.clearRect(0,0,W,H);
if(phase===0&&r>maxR*0.45&&!midCallback._called){
midCallback();midCallback._called=true;
}
if(phase===1&&r<=0){
ctx.clearRect(0,0,W,H);done&&done();return;
}
ctx.beginPath();
const steps=120;
for(let i=0;i<=steps;i++){
const a=i/steps*Math.PI*2;
let rad=r;
roughnessSeeds.forEach(s=>{
rad+=r*s.amp*Math.sin(a*s.freq+s.phase+(phase===0?r*0.01:-r*0.01));
});
rad=Math.max(0,rad);
const px=x+Math.cos(a)*rad;
const py=y+Math.sin(a)*rad;
i===0?ctx.moveTo(px,py):ctx.lineTo(px,py);
}
ctx.closePath();
const g=ctx.createRadialGradient(x,y,0,x,y,r*1.1);
g.addColorStop(0,color);
g.addColorStop(0.7,color);
g.addColorStop(1,'transparent');
ctx.fillStyle=g;
ctx.fill();
ctx.beginPath();
for(let i=0;i<=steps;i++){
const a=i/steps*Math.PI*2;
let rad=r;
roughnessSeeds.forEach(s=>{rad+=r*s.amp*Math.sin(a*s.freq+s.phase+(phase===0?r*0.01:-r*0.01));});
const edgeR=rad*0.92;
const px=x+Math.cos(a)*edgeR;
const py=y+Math.sin(a)*edgeR;
i===0?ctx.moveTo(px,py):ctx.lineTo(px,py);
}
ctx.closePath();
ctx.fillStyle='rgba(0,0,0,0.08)';
ctx.fill();
if(phase===0){
r+=speed;
if(r>=maxR){phase=1;}
}else{
r-=speed*1.4;
}
requestAnimationFrame(draw);
}
draw();
}
const inkColors=['#1a1410','#e8e2d8','#1a1410','#1a1410'];
function goTo(n,ex,ey){
if(busy||n===cur)return;
busy=true;
const prev=cur;cur=n;
const x=ex||window.innerWidth/2;
const y=ey||window.innerHeight/2;
const color=inkColors[cur];
const mid=()=>{
pages[prev].classList.remove('active');
pages[cur].classList.add('active');
dots.forEach((d,i)=>{
d.classList.toggle('active',i===cur);
d.classList.toggle('on-dark',darkPages.includes(cur));
});
};
mid._called=false;
inkTransition(x,y,color,mid,()=>{busy=false;});
}
dots.forEach(d=>d.addEventListener('click',e=>{
const r=e.currentTarget.getBoundingClientRect();
goTo(+d.dataset.i,r.left+r.width/2,r.top+r.height/2);
}));
let wt=0;
window.addEventListener('wheel',e=>{
const now=Date.now();if(now-wt<1000)return;wt=now;
goTo(Math.max(0,Math.min(3,cur+(e.deltaY>0?1:-1))),window.innerWidth/2,window.innerHeight/2);
},{passive:true});
document.addEventListener('keydown',e=>{
const c=window.innerWidth/2,m=window.innerHeight/2;
if(e.key==='ArrowRight')goTo(Math.min(3,cur+1),c,m);
if(e.key==='ArrowLeft')goTo(Math.max(0,cur-1),c,m);
});Here's a working CSS Page Transition 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: Ink Bleed
Source: https://codefronts.com/motion/css-page-transitions/ink-bleed/
A Canvas ink drop radiates organically from the centre with seismic roughness — like sumi ink spreading on wet paper, covering, then receding. Set in a Japanese ceramics studio.
## HTML
```html
<canvas id="inkCanvas"></canvas>
<div class="pages">
<div class="page active" id="p0">
<div class="p0-left">
<div class="studio-mark">白泥窯 · Hakudei Kiln</div>
<div class="kanji-bg">器</div>
<div class="p0-hero">
<div class="p0-tagline">Handmade · Raku · Seasonal</div>
<h1 class="p0-title">Things made<br>to last</h1>
<div class="p0-title-jp">一期一会の器</div>
<p class="p0-body">Each piece is made once, in the way the clay demands. We do not repeat ourselves. We do not sell in quantity. We work with fire and patience.</p>
</div>
<div class="p0-footer">
<div class="season-badge">Autumn 2025</div>
<div style="font-size:11px;font-style:italic;color:var(--smoke)">Kyoto, Japan</div>
</div>
</div>
<div class="p0-right">
<div class="brush-art">
<div class="bowl-art">
<div class="ceramic-ring ring-1"></div>
<div class="ceramic-ring ring-2"></div>
<div class="ceramic-ring ring-3"></div>
<div class="ring-inner"></div>
<div class="stroke stroke-1"></div>
<div class="stroke stroke-2"></div>
<div class="stroke stroke-3"></div>
</div>
</div>
<div class="p0-caption">茶碗 · Chawan · Tea Bowl · Shino glaze · 2025</div>
</div>
</div>
<div class="page" id="p1">
<div class="p1-bg">器</div>
<div class="p1-eyebrow">Autumn Collection 2025</div>
<h2 class="p1-title">New work<br>from the kiln</h2>
<div class="p1-grid">
<div class="ceramic-card">
<div class="ceramic-thumb"><div class="c-shape"></div></div>
<div class="ceramic-name">Shino Chawan</div>
<div class="ceramic-series">Tea Series · No.01</div>
<div class="ceramic-price">¥ 68,000</div>
</div>
<div class="ceramic-card">
<div class="ceramic-thumb" style="aspect-ratio:1"><div class="c-shape" style="border-radius:50%"></div></div>
<div class="ceramic-name">Ash Katakuchi</div>
<div class="ceramic-series">Vessel Series · No.04</div>
<div class="ceramic-price">¥ 42,000</div>
</div>
<div class="ceramic-card">
<div class="ceramic-thumb"><div class="c-shape" style="height:100px;border-radius:20% 20% 50% 50%"></div></div>
<div class="ceramic-name">Raku Guinomi</div>
<div class="ceramic-series">Sake Series · No.07</div>
<div class="ceramic-price">¥ 24,000</div>
</div>
<div class="ceramic-card">
<div class="ceramic-thumb"><div class="c-shape" style="width:80px;height:60px;border-radius:50%"></div></div>
<div class="ceramic-name">Oribe Hachi</div>
<div class="ceramic-series">Bowl Series · No.02</div>
<div class="ceramic-price">¥ 38,000</div>
</div>
</div>
</div>
<div class="page" id="p2">
<div class="p2-left">
<div class="p2-eyebrow">The Process</div>
<h2 class="p2-title">Made by<br>hand and<br>fire</h2>
<p class="p2-body">Nothing here is hurried. The kiln dictates the schedule, not the calendar. Each firing takes three days and reveals results that cannot be predicted or reproduced.</p>
<div class="process-list">
<div class="proc-item"><div class="proc-num">一</div><div class="proc-text">Clay reclaimed from mountain sources and aged for one year</div></div>
<div class="proc-item"><div class="proc-num">二</div><div class="proc-text">Hand-thrown or slab-built, each piece unique in form</div></div>
<div class="proc-item"><div class="proc-num">三</div><div class="proc-text">Wood-fired in anagama kiln at cone 12 over 60 hours</div></div>
<div class="proc-item"><div class="proc-num">四</div><div class="proc-text">Unboxed by hand and inspected. Most pieces are not kept.</div></div>
</div>
</div>
<div class="p2-right">
<div class="form-lg"></div>
<div class="form-sm"></div>
<div style="font-size:10px;letter-spacing:0.2em;color:var(--smoke);text-align:center;font-style:italic">木灰釉薬 · Wood-ash glaze</div>
</div>
</div>
<div class="page" id="p3">
<div class="p3-bg"></div>
<div class="p3-content">
<div class="p3-jp">白泥窯 · 一九八九年創業</div>
<h2 class="p3-title">Making is<br>a form of<br><em>listening</em></h2>
<blockquote class="p3-quote">The clay knows what it wants to become. Our work is only to be present for long enough to hear it — and skilled enough to help it arrive.</blockquote>
<div class="p3-meta">
<div class="p3-meta-item"><div class="p3-meta-num">36</div><div class="p3-meta-label">Years at the kiln</div></div>
<div class="p3-meta-item"><div class="p3-meta-num">4</div><div class="p3-meta-label">Annual firings</div></div>
<div class="p3-meta-item"><div class="p3-meta-num">~60</div><div class="p3-meta-label">Pieces kept per year</div></div>
</div>
</div>
</div>
</div>
<div class="dot-nav" id="dotNav">
<button class="dot active" data-i="0"></button>
<button class="dot" data-i="1"></button>
<button class="dot" data-i="2"></button>
<button class="dot" data-i="3"></button>
</div>
```
## CSS
```css
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--ash: #e8e2d8;
--pale: #f4f0ea;
--clay: #c4a882;
--smoke: #8a8070;
--ink: #1a1410;
--charcoal: #2a2420;
}
html,
body {
height: 100%;
overflow: hidden;
background: var(--pale);
font-family: 'Cormorant',serif;
color: var(--ink);
}
/* Ink canvas overlay */
#inkCanvas {
position: fixed;
inset: 0;
z-index: 800;
pointer-events: none;
}
/* Pages */
.pages {
position: fixed;
inset: 0;
}
.page {
position: absolute;
inset: 0;
visibility: hidden;
pointer-events: none;
}
.page.active {
visibility: visible;
pointer-events: auto;
}
/* PAGE 0 — STUDIO */
#p0 {
background: var(--pale);
display: grid;
grid-template-columns: 1fr 1fr;
}
.p0-left {
padding: 80px 70px;
display: flex;
flex-direction: column;
justify-content: space-between;
border-right: 1px solid rgba(26,20,16,0.08);
position: relative;
}
.studio-mark {
font-family: 'Noto Serif JP',serif;
font-size: 13px;
letter-spacing: 0.2em;
color: rgba(26,20,16,0.3);
font-weight: 200;
}
.p0-hero {
flex: 1;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding-bottom: 20px;
}
.kanji-bg {
position: absolute;
font-family: 'Noto Serif JP',serif;
font-size: 40vw;
font-weight: 200;
color: rgba(26,20,16,0.025);
line-height: 1;
top: 50%;
left: 50%;
transform: translate(-35%,-55%);
pointer-events: none;
user-select: none;
}
.p0-tagline {
font-size: 9px;
letter-spacing: 0.35em;
text-transform: uppercase;
color: var(--smoke);
margin-bottom: 20px;
}
.p0-title {
font-size: clamp(48px,7vw,88px);
font-weight: 300;
line-height: 0.95;
letter-spacing: -0.02em;
color: var(--ink);
margin-bottom: 20px;
position: relative;
}
.p0-title-jp {
font-family: 'Noto Serif JP',serif;
font-size: 11px;
letter-spacing: 0.2em;
color: var(--clay);
font-weight: 300;
margin-bottom: 32px;
}
.p0-body {
font-size: 15px;
line-height: 1.8;
font-weight: 300;
color: rgba(26,20,16,0.5);
max-width: 360px;
font-style: italic;
}
.p0-footer {
display: flex;
align-items: center;
justify-content: space-between;
}
.season-badge {
font-size: 9px;
letter-spacing: 0.25em;
text-transform: uppercase;
color: var(--clay);
border: 1px solid rgba(196,168,130,0.4);
padding: 6px 16px;
}
.p0-right {
display: flex;
flex-direction: column;
background: var(--ash);
position: relative;
overflow: hidden;
}
/* Sumi-e brush artwork area */
.brush-art {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.bowl-art {
position: relative;
width: 240px;
height: 240px;
}
.ceramic-ring {
position: absolute;
border-radius: 50%;
border: 1px solid rgba(26,20,16,0.12);
}
.ring-1 {
inset: 0;
background: rgba(196,168,130,0.15);
}
.ring-2 {
inset: 16px;
background: rgba(196,168,130,0.1);
border-color: rgba(26,20,16,0.08);
}
.ring-3 {
inset: 40px;
background: rgba(196,168,130,0.08);
border-color: rgba(26,20,16,0.05);
}
.ring-inner {
position: absolute;
inset: 80px;
border-radius: 50%;
background: var(--ink);
opacity: 0.06;
}
.stroke {
position: absolute;
background: rgba(26,20,16,0.12);
border-radius: 50% 20% 50% 20%;
transform-origin: center;
}
.stroke-1 {
width: 3px;
height: 80px;
top: -40px;
left: 50%;
transform: translateX(-50%) rotate(-10deg);
}
.stroke-2 {
width: 2px;
height: 60px;
top: 20px;
right: 20px;
transform: rotate(35deg);
opacity: 0.6;
}
.stroke-3 {
width: 2px;
height: 40px;
bottom: 20px;
left: 24px;
transform: rotate(-20deg);
opacity: 0.4;
}
.p0-caption {
padding: 32px 48px;
border-top: 1px solid rgba(26,20,16,0.08);
font-size: 11px;
letter-spacing: 0.12em;
color: rgba(26,20,16,0.35);
text-align: center;
font-style: italic;
}
/* PAGE 1 — COLLECTION */
#p1 {
background: var(--charcoal);
color: var(--ash);
display: flex;
flex-direction: column;
padding: 80px;
position: relative;
overflow: hidden;
}
.p1-bg {
position: absolute;
font-family: 'Noto Serif JP',serif;
font-size: 500px;
font-weight: 200;
color: rgba(255,255,255,0.015);
line-height: 1;
bottom: -100px;
right: -60px;
pointer-events: none;
}
.p1-eyebrow {
font-size: 9px;
letter-spacing: 0.35em;
text-transform: uppercase;
color: var(--clay);
margin-bottom: 40px;
position: relative;
z-index: 1;
display: flex;
align-items: center;
gap: 12px;
}
.p1-eyebrow::after {
content: '';
flex: 1;
height: 1px;
background: rgba(196,168,130,0.2);
}
.p1-title {
font-size: clamp(40px,6vw,72px);
font-weight: 300;
color: var(--ash);
letter-spacing: -0.02em;
line-height: 1.05;
margin-bottom: 60px;
position: relative;
z-index: 1;
}
.p1-grid {
display: grid;
grid-template-columns: repeat(4,1fr);
gap: 24px;
flex: 1;
align-content: start;
position: relative;
z-index: 1;
}
.ceramic-card {
display: flex;
flex-direction: column;
gap: 12px;
cursor: pointer;
transition: transform .3s;
}
.ceramic-card:hover {
transform: translateY(-4px);
}
.ceramic-thumb {
aspect-ratio: 3/4;
background: rgba(255,255,255,0.03);
border: 1px solid rgba(232,226,216,0.08);
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
transition: border-color .3s;
}
.ceramic-card:hover .ceramic-thumb {
border-color: rgba(196,168,130,0.3);
}
.c-shape {
width: 60px;
height: 80px;
background: linear-gradient(160deg,rgba(196,168,130,0.3),rgba(196,168,130,0.1));
border-radius: 50% 50% 40% 40%;
border: 1px solid rgba(196,168,130,0.2);
position: relative;
}
.c-shape::before {
content: '';
position: absolute;
top: -8px;
left: 50%;
transform: translateX(-50%);
width: 24px;
height: 12px;
background: rgba(196,168,130,0.25);
border-radius: 50% 50% 0 0;
border: 1px solid rgba(196,168,130,0.2);
border-bottom: none;
}
.ceramic-name {
font-size: 13px;
font-weight: 300;
color: var(--ash);
letter-spacing: 0.04em;
}
.ceramic-series {
font-size: 9px;
letter-spacing: 0.2em;
text-transform: uppercase;
color: rgba(232,226,216,0.3);
}
.ceramic-price {
font-size: 11px;
color: var(--clay);
letter-spacing: 0.08em;
}
/* PAGE 2 — PROCESS */
#p2 {
background: var(--pale);
display: grid;
grid-template-columns: 480px 1fr;
}
.p2-left {
padding: 80px 70px;
display: flex;
flex-direction: column;
justify-content: center;
border-right: 1px solid rgba(26,20,16,0.08);
}
.p2-eyebrow {
font-size: 9px;
letter-spacing: 0.35em;
text-transform: uppercase;
color: var(--clay);
margin-bottom: 28px;
}
.p2-title {
font-size: clamp(36px,5vw,64px);
font-weight: 300;
color: var(--ink);
letter-spacing: -0.02em;
line-height: 1.05;
margin-bottom: 24px;
}
.p2-body {
font-size: 15px;
line-height: 1.85;
font-weight: 300;
font-style: italic;
color: rgba(26,20,16,0.5);
margin-bottom: 48px;
}
.process-list {
display: flex;
flex-direction: column;
gap: 20px;
}
.proc-item {
display: flex;
align-items: flex-start;
gap: 20px;
padding-bottom: 20px;
border-bottom: 1px solid rgba(26,20,16,0.06);
}
.proc-num {
font-family: 'Noto Serif JP',serif;
font-size: 9px;
color: var(--clay);
width: 20px;
flex-shrink: 0;
padding-top: 2px;
font-weight: 300;
}
.proc-text {
font-size: 13px;
line-height: 1.7;
color: rgba(26,20,16,0.5);
letter-spacing: 0.03em;
}
.p2-right {
background: var(--ash);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px;
position: relative;
overflow: hidden;
gap: 48px;
}
.form-lg {
width: 160px;
height: 220px;
background: linear-gradient(160deg,rgba(196,168,130,0.4),rgba(26,20,16,0.06));
border-radius: 50% 50% 40% 40%;
border: 1px solid rgba(196,168,130,0.3);
box-shadow: 12px 24px 40px rgba(26,20,16,0.08);
}
.form-sm {
width: 100px;
height: 60px;
background: linear-gradient(160deg,rgba(196,168,130,0.3),rgba(26,20,16,0.04));
border-radius: 50%;
border: 1px solid rgba(196,168,130,0.2);
}
/* PAGE 3 — ABOUT */
#p3 {
background: var(--ink);
color: var(--ash);
display: flex;
align-items: center;
justify-content: center;
padding: 80px;
position: relative;
overflow: hidden;
}
.p3-bg {
position: absolute;
inset: 0;
background: radial-gradient(ellipse 60% 60% at 30% 50%,rgba(196,168,130,0.04),transparent);
}
.p3-content {
max-width: 640px;
position: relative;
}
.p3-jp {
font-family: 'Noto Serif JP',serif;
font-size: 11px;
letter-spacing: 0.3em;
font-weight: 200;
color: rgba(196,168,130,0.5);
margin-bottom: 32px;
}
.p3-title {
font-size: clamp(36px,6vw,72px);
font-weight: 300;
color: var(--ash);
line-height: 1.05;
letter-spacing: -0.02em;
margin-bottom: 32px;
}
.p3-title em {
font-style: italic;
color: var(--clay);
}
.p3-quote {
font-size: 17px;
line-height: 1.8;
font-style: italic;
font-weight: 300;
color: rgba(232,226,216,0.55);
border-left: 2px solid rgba(196,168,130,0.3);
padding-left: 24px;
margin-bottom: 40px;
}
.p3-meta {
display: flex;
gap: 48px;
padding-top: 32px;
border-top: 1px solid rgba(232,226,216,0.08);
}
.p3-meta-item {
display: flex;
flex-direction: column;
gap: 6px;
}
.p3-meta-num {
font-size: 32px;
font-weight: 300;
color: var(--clay);
}
.p3-meta-label {
font-size: 9px;
letter-spacing: 0.25em;
text-transform: uppercase;
color: rgba(232,226,216,0.25);
}
/* Nav */
.dot-nav {
position: fixed;
bottom: 40px;
left: 50%;
transform: translateX(-50%);
z-index: 900;
display: flex;
gap: 10px;
align-items: center;
}
.dot {
width: 5px;
height: 5px;
border-radius: 50%;
background: rgba(26,20,16,0.2);
border: none;
cursor: pointer;
transition: transform .35s,background .25s;
}
.dot.on-dark {
background: rgba(232,226,216,0.2);
}
.dot.active {
transform: scale(1.8);
background: var(--clay);
}
.dot.on-dark.active {
background: var(--clay);
}
@media (max-width: 768px) {
#p0,
#p2 {
grid-template-columns: 1fr;
}
.p0-right,
.p2-right {
display: none;
}
.p0-left,
.p2-left,
.p3-content {
padding: 48px 28px;
}
#p1 {
padding: 48px 28px;
}
.p1-grid {
grid-template-columns: 1fr 1fr;
}
.p3-meta {
gap: 24px;
}
}
```
## JavaScript
```js
const canvas=document.getElementById('inkCanvas');
const ctx=canvas.getContext('2d');
const pages=document.querySelectorAll('.page');
const dots=document.querySelectorAll('.dot');
const darkPages=[1,3];
let cur=0,busy=false;
function resize(){
canvas.width=window.innerWidth;
canvas.height=window.innerHeight;
}
resize();
window.addEventListener('resize',resize);
function inkTransition(x,y,color,midCallback,done){
const W=canvas.width,H=canvas.height;
const maxR=Math.sqrt(W*W+H*H)*0.6;
let r=0,phase=0;
const speed=28;
const roughnessSeeds=Array.from({length:16},(_,i)=>({
angle:i/16*Math.PI*2,
amp:Math.random()*0.15+0.05,
freq:Math.random()*3+2,
phase:Math.random()*Math.PI*2
}));
function draw(){
ctx.clearRect(0,0,W,H);
if(phase===0&&r>maxR*0.45&&!midCallback._called){
midCallback();midCallback._called=true;
}
if(phase===1&&r<=0){
ctx.clearRect(0,0,W,H);done&&done();return;
}
ctx.beginPath();
const steps=120;
for(let i=0;i<=steps;i++){
const a=i/steps*Math.PI*2;
let rad=r;
roughnessSeeds.forEach(s=>{
rad+=r*s.amp*Math.sin(a*s.freq+s.phase+(phase===0?r*0.01:-r*0.01));
});
rad=Math.max(0,rad);
const px=x+Math.cos(a)*rad;
const py=y+Math.sin(a)*rad;
i===0?ctx.moveTo(px,py):ctx.lineTo(px,py);
}
ctx.closePath();
const g=ctx.createRadialGradient(x,y,0,x,y,r*1.1);
g.addColorStop(0,color);
g.addColorStop(0.7,color);
g.addColorStop(1,'transparent');
ctx.fillStyle=g;
ctx.fill();
ctx.beginPath();
for(let i=0;i<=steps;i++){
const a=i/steps*Math.PI*2;
let rad=r;
roughnessSeeds.forEach(s=>{rad+=r*s.amp*Math.sin(a*s.freq+s.phase+(phase===0?r*0.01:-r*0.01));});
const edgeR=rad*0.92;
const px=x+Math.cos(a)*edgeR;
const py=y+Math.sin(a)*edgeR;
i===0?ctx.moveTo(px,py):ctx.lineTo(px,py);
}
ctx.closePath();
ctx.fillStyle='rgba(0,0,0,0.08)';
ctx.fill();
if(phase===0){
r+=speed;
if(r>=maxR){phase=1;}
}else{
r-=speed*1.4;
}
requestAnimationFrame(draw);
}
draw();
}
const inkColors=['#1a1410','#e8e2d8','#1a1410','#1a1410'];
function goTo(n,ex,ey){
if(busy||n===cur)return;
busy=true;
const prev=cur;cur=n;
const x=ex||window.innerWidth/2;
const y=ey||window.innerHeight/2;
const color=inkColors[cur];
const mid=()=>{
pages[prev].classList.remove('active');
pages[cur].classList.add('active');
dots.forEach((d,i)=>{
d.classList.toggle('active',i===cur);
d.classList.toggle('on-dark',darkPages.includes(cur));
});
};
mid._called=false;
inkTransition(x,y,color,mid,()=>{busy=false;});
}
dots.forEach(d=>d.addEventListener('click',e=>{
const r=e.currentTarget.getBoundingClientRect();
goTo(+d.dataset.i,r.left+r.width/2,r.top+r.height/2);
}));
let wt=0;
window.addEventListener('wheel',e=>{
const now=Date.now();if(now-wt<1000)return;wt=now;
goTo(Math.max(0,Math.min(3,cur+(e.deltaY>0?1:-1))),window.innerWidth/2,window.innerHeight/2);
},{passive:true});
document.addEventListener('keydown',e=>{
const c=window.innerWidth/2,m=window.innerHeight/2;
if(e.key==='ArrowRight')goTo(Math.min(3,cur+1),c,m);
if(e.key==='ArrowLeft')goTo(Math.max(0,cur-1),c,m);
});
```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 Page Transition 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: Ink Bleed
Source: https://codefronts.com/motion/css-page-transitions/ink-bleed/
A Canvas ink drop radiates organically from the centre with seismic roughness — like sumi ink spreading on wet paper, covering, then receding. Set in a Japanese ceramics studio.
## HTML
```html
<canvas id="inkCanvas"></canvas>
<div class="pages">
<div class="page active" id="p0">
<div class="p0-left">
<div class="studio-mark">白泥窯 · Hakudei Kiln</div>
<div class="kanji-bg">器</div>
<div class="p0-hero">
<div class="p0-tagline">Handmade · Raku · Seasonal</div>
<h1 class="p0-title">Things made<br>to last</h1>
<div class="p0-title-jp">一期一会の器</div>
<p class="p0-body">Each piece is made once, in the way the clay demands. We do not repeat ourselves. We do not sell in quantity. We work with fire and patience.</p>
</div>
<div class="p0-footer">
<div class="season-badge">Autumn 2025</div>
<div style="font-size:11px;font-style:italic;color:var(--smoke)">Kyoto, Japan</div>
</div>
</div>
<div class="p0-right">
<div class="brush-art">
<div class="bowl-art">
<div class="ceramic-ring ring-1"></div>
<div class="ceramic-ring ring-2"></div>
<div class="ceramic-ring ring-3"></div>
<div class="ring-inner"></div>
<div class="stroke stroke-1"></div>
<div class="stroke stroke-2"></div>
<div class="stroke stroke-3"></div>
</div>
</div>
<div class="p0-caption">茶碗 · Chawan · Tea Bowl · Shino glaze · 2025</div>
</div>
</div>
<div class="page" id="p1">
<div class="p1-bg">器</div>
<div class="p1-eyebrow">Autumn Collection 2025</div>
<h2 class="p1-title">New work<br>from the kiln</h2>
<div class="p1-grid">
<div class="ceramic-card">
<div class="ceramic-thumb"><div class="c-shape"></div></div>
<div class="ceramic-name">Shino Chawan</div>
<div class="ceramic-series">Tea Series · No.01</div>
<div class="ceramic-price">¥ 68,000</div>
</div>
<div class="ceramic-card">
<div class="ceramic-thumb" style="aspect-ratio:1"><div class="c-shape" style="border-radius:50%"></div></div>
<div class="ceramic-name">Ash Katakuchi</div>
<div class="ceramic-series">Vessel Series · No.04</div>
<div class="ceramic-price">¥ 42,000</div>
</div>
<div class="ceramic-card">
<div class="ceramic-thumb"><div class="c-shape" style="height:100px;border-radius:20% 20% 50% 50%"></div></div>
<div class="ceramic-name">Raku Guinomi</div>
<div class="ceramic-series">Sake Series · No.07</div>
<div class="ceramic-price">¥ 24,000</div>
</div>
<div class="ceramic-card">
<div class="ceramic-thumb"><div class="c-shape" style="width:80px;height:60px;border-radius:50%"></div></div>
<div class="ceramic-name">Oribe Hachi</div>
<div class="ceramic-series">Bowl Series · No.02</div>
<div class="ceramic-price">¥ 38,000</div>
</div>
</div>
</div>
<div class="page" id="p2">
<div class="p2-left">
<div class="p2-eyebrow">The Process</div>
<h2 class="p2-title">Made by<br>hand and<br>fire</h2>
<p class="p2-body">Nothing here is hurried. The kiln dictates the schedule, not the calendar. Each firing takes three days and reveals results that cannot be predicted or reproduced.</p>
<div class="process-list">
<div class="proc-item"><div class="proc-num">一</div><div class="proc-text">Clay reclaimed from mountain sources and aged for one year</div></div>
<div class="proc-item"><div class="proc-num">二</div><div class="proc-text">Hand-thrown or slab-built, each piece unique in form</div></div>
<div class="proc-item"><div class="proc-num">三</div><div class="proc-text">Wood-fired in anagama kiln at cone 12 over 60 hours</div></div>
<div class="proc-item"><div class="proc-num">四</div><div class="proc-text">Unboxed by hand and inspected. Most pieces are not kept.</div></div>
</div>
</div>
<div class="p2-right">
<div class="form-lg"></div>
<div class="form-sm"></div>
<div style="font-size:10px;letter-spacing:0.2em;color:var(--smoke);text-align:center;font-style:italic">木灰釉薬 · Wood-ash glaze</div>
</div>
</div>
<div class="page" id="p3">
<div class="p3-bg"></div>
<div class="p3-content">
<div class="p3-jp">白泥窯 · 一九八九年創業</div>
<h2 class="p3-title">Making is<br>a form of<br><em>listening</em></h2>
<blockquote class="p3-quote">The clay knows what it wants to become. Our work is only to be present for long enough to hear it — and skilled enough to help it arrive.</blockquote>
<div class="p3-meta">
<div class="p3-meta-item"><div class="p3-meta-num">36</div><div class="p3-meta-label">Years at the kiln</div></div>
<div class="p3-meta-item"><div class="p3-meta-num">4</div><div class="p3-meta-label">Annual firings</div></div>
<div class="p3-meta-item"><div class="p3-meta-num">~60</div><div class="p3-meta-label">Pieces kept per year</div></div>
</div>
</div>
</div>
</div>
<div class="dot-nav" id="dotNav">
<button class="dot active" data-i="0"></button>
<button class="dot" data-i="1"></button>
<button class="dot" data-i="2"></button>
<button class="dot" data-i="3"></button>
</div>
```
## CSS
```css
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--ash: #e8e2d8;
--pale: #f4f0ea;
--clay: #c4a882;
--smoke: #8a8070;
--ink: #1a1410;
--charcoal: #2a2420;
}
html,
body {
height: 100%;
overflow: hidden;
background: var(--pale);
font-family: 'Cormorant',serif;
color: var(--ink);
}
/* Ink canvas overlay */
#inkCanvas {
position: fixed;
inset: 0;
z-index: 800;
pointer-events: none;
}
/* Pages */
.pages {
position: fixed;
inset: 0;
}
.page {
position: absolute;
inset: 0;
visibility: hidden;
pointer-events: none;
}
.page.active {
visibility: visible;
pointer-events: auto;
}
/* PAGE 0 — STUDIO */
#p0 {
background: var(--pale);
display: grid;
grid-template-columns: 1fr 1fr;
}
.p0-left {
padding: 80px 70px;
display: flex;
flex-direction: column;
justify-content: space-between;
border-right: 1px solid rgba(26,20,16,0.08);
position: relative;
}
.studio-mark {
font-family: 'Noto Serif JP',serif;
font-size: 13px;
letter-spacing: 0.2em;
color: rgba(26,20,16,0.3);
font-weight: 200;
}
.p0-hero {
flex: 1;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding-bottom: 20px;
}
.kanji-bg {
position: absolute;
font-family: 'Noto Serif JP',serif;
font-size: 40vw;
font-weight: 200;
color: rgba(26,20,16,0.025);
line-height: 1;
top: 50%;
left: 50%;
transform: translate(-35%,-55%);
pointer-events: none;
user-select: none;
}
.p0-tagline {
font-size: 9px;
letter-spacing: 0.35em;
text-transform: uppercase;
color: var(--smoke);
margin-bottom: 20px;
}
.p0-title {
font-size: clamp(48px,7vw,88px);
font-weight: 300;
line-height: 0.95;
letter-spacing: -0.02em;
color: var(--ink);
margin-bottom: 20px;
position: relative;
}
.p0-title-jp {
font-family: 'Noto Serif JP',serif;
font-size: 11px;
letter-spacing: 0.2em;
color: var(--clay);
font-weight: 300;
margin-bottom: 32px;
}
.p0-body {
font-size: 15px;
line-height: 1.8;
font-weight: 300;
color: rgba(26,20,16,0.5);
max-width: 360px;
font-style: italic;
}
.p0-footer {
display: flex;
align-items: center;
justify-content: space-between;
}
.season-badge {
font-size: 9px;
letter-spacing: 0.25em;
text-transform: uppercase;
color: var(--clay);
border: 1px solid rgba(196,168,130,0.4);
padding: 6px 16px;
}
.p0-right {
display: flex;
flex-direction: column;
background: var(--ash);
position: relative;
overflow: hidden;
}
/* Sumi-e brush artwork area */
.brush-art {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.bowl-art {
position: relative;
width: 240px;
height: 240px;
}
.ceramic-ring {
position: absolute;
border-radius: 50%;
border: 1px solid rgba(26,20,16,0.12);
}
.ring-1 {
inset: 0;
background: rgba(196,168,130,0.15);
}
.ring-2 {
inset: 16px;
background: rgba(196,168,130,0.1);
border-color: rgba(26,20,16,0.08);
}
.ring-3 {
inset: 40px;
background: rgba(196,168,130,0.08);
border-color: rgba(26,20,16,0.05);
}
.ring-inner {
position: absolute;
inset: 80px;
border-radius: 50%;
background: var(--ink);
opacity: 0.06;
}
.stroke {
position: absolute;
background: rgba(26,20,16,0.12);
border-radius: 50% 20% 50% 20%;
transform-origin: center;
}
.stroke-1 {
width: 3px;
height: 80px;
top: -40px;
left: 50%;
transform: translateX(-50%) rotate(-10deg);
}
.stroke-2 {
width: 2px;
height: 60px;
top: 20px;
right: 20px;
transform: rotate(35deg);
opacity: 0.6;
}
.stroke-3 {
width: 2px;
height: 40px;
bottom: 20px;
left: 24px;
transform: rotate(-20deg);
opacity: 0.4;
}
.p0-caption {
padding: 32px 48px;
border-top: 1px solid rgba(26,20,16,0.08);
font-size: 11px;
letter-spacing: 0.12em;
color: rgba(26,20,16,0.35);
text-align: center;
font-style: italic;
}
/* PAGE 1 — COLLECTION */
#p1 {
background: var(--charcoal);
color: var(--ash);
display: flex;
flex-direction: column;
padding: 80px;
position: relative;
overflow: hidden;
}
.p1-bg {
position: absolute;
font-family: 'Noto Serif JP',serif;
font-size: 500px;
font-weight: 200;
color: rgba(255,255,255,0.015);
line-height: 1;
bottom: -100px;
right: -60px;
pointer-events: none;
}
.p1-eyebrow {
font-size: 9px;
letter-spacing: 0.35em;
text-transform: uppercase;
color: var(--clay);
margin-bottom: 40px;
position: relative;
z-index: 1;
display: flex;
align-items: center;
gap: 12px;
}
.p1-eyebrow::after {
content: '';
flex: 1;
height: 1px;
background: rgba(196,168,130,0.2);
}
.p1-title {
font-size: clamp(40px,6vw,72px);
font-weight: 300;
color: var(--ash);
letter-spacing: -0.02em;
line-height: 1.05;
margin-bottom: 60px;
position: relative;
z-index: 1;
}
.p1-grid {
display: grid;
grid-template-columns: repeat(4,1fr);
gap: 24px;
flex: 1;
align-content: start;
position: relative;
z-index: 1;
}
.ceramic-card {
display: flex;
flex-direction: column;
gap: 12px;
cursor: pointer;
transition: transform .3s;
}
.ceramic-card:hover {
transform: translateY(-4px);
}
.ceramic-thumb {
aspect-ratio: 3/4;
background: rgba(255,255,255,0.03);
border: 1px solid rgba(232,226,216,0.08);
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
transition: border-color .3s;
}
.ceramic-card:hover .ceramic-thumb {
border-color: rgba(196,168,130,0.3);
}
.c-shape {
width: 60px;
height: 80px;
background: linear-gradient(160deg,rgba(196,168,130,0.3),rgba(196,168,130,0.1));
border-radius: 50% 50% 40% 40%;
border: 1px solid rgba(196,168,130,0.2);
position: relative;
}
.c-shape::before {
content: '';
position: absolute;
top: -8px;
left: 50%;
transform: translateX(-50%);
width: 24px;
height: 12px;
background: rgba(196,168,130,0.25);
border-radius: 50% 50% 0 0;
border: 1px solid rgba(196,168,130,0.2);
border-bottom: none;
}
.ceramic-name {
font-size: 13px;
font-weight: 300;
color: var(--ash);
letter-spacing: 0.04em;
}
.ceramic-series {
font-size: 9px;
letter-spacing: 0.2em;
text-transform: uppercase;
color: rgba(232,226,216,0.3);
}
.ceramic-price {
font-size: 11px;
color: var(--clay);
letter-spacing: 0.08em;
}
/* PAGE 2 — PROCESS */
#p2 {
background: var(--pale);
display: grid;
grid-template-columns: 480px 1fr;
}
.p2-left {
padding: 80px 70px;
display: flex;
flex-direction: column;
justify-content: center;
border-right: 1px solid rgba(26,20,16,0.08);
}
.p2-eyebrow {
font-size: 9px;
letter-spacing: 0.35em;
text-transform: uppercase;
color: var(--clay);
margin-bottom: 28px;
}
.p2-title {
font-size: clamp(36px,5vw,64px);
font-weight: 300;
color: var(--ink);
letter-spacing: -0.02em;
line-height: 1.05;
margin-bottom: 24px;
}
.p2-body {
font-size: 15px;
line-height: 1.85;
font-weight: 300;
font-style: italic;
color: rgba(26,20,16,0.5);
margin-bottom: 48px;
}
.process-list {
display: flex;
flex-direction: column;
gap: 20px;
}
.proc-item {
display: flex;
align-items: flex-start;
gap: 20px;
padding-bottom: 20px;
border-bottom: 1px solid rgba(26,20,16,0.06);
}
.proc-num {
font-family: 'Noto Serif JP',serif;
font-size: 9px;
color: var(--clay);
width: 20px;
flex-shrink: 0;
padding-top: 2px;
font-weight: 300;
}
.proc-text {
font-size: 13px;
line-height: 1.7;
color: rgba(26,20,16,0.5);
letter-spacing: 0.03em;
}
.p2-right {
background: var(--ash);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px;
position: relative;
overflow: hidden;
gap: 48px;
}
.form-lg {
width: 160px;
height: 220px;
background: linear-gradient(160deg,rgba(196,168,130,0.4),rgba(26,20,16,0.06));
border-radius: 50% 50% 40% 40%;
border: 1px solid rgba(196,168,130,0.3);
box-shadow: 12px 24px 40px rgba(26,20,16,0.08);
}
.form-sm {
width: 100px;
height: 60px;
background: linear-gradient(160deg,rgba(196,168,130,0.3),rgba(26,20,16,0.04));
border-radius: 50%;
border: 1px solid rgba(196,168,130,0.2);
}
/* PAGE 3 — ABOUT */
#p3 {
background: var(--ink);
color: var(--ash);
display: flex;
align-items: center;
justify-content: center;
padding: 80px;
position: relative;
overflow: hidden;
}
.p3-bg {
position: absolute;
inset: 0;
background: radial-gradient(ellipse 60% 60% at 30% 50%,rgba(196,168,130,0.04),transparent);
}
.p3-content {
max-width: 640px;
position: relative;
}
.p3-jp {
font-family: 'Noto Serif JP',serif;
font-size: 11px;
letter-spacing: 0.3em;
font-weight: 200;
color: rgba(196,168,130,0.5);
margin-bottom: 32px;
}
.p3-title {
font-size: clamp(36px,6vw,72px);
font-weight: 300;
color: var(--ash);
line-height: 1.05;
letter-spacing: -0.02em;
margin-bottom: 32px;
}
.p3-title em {
font-style: italic;
color: var(--clay);
}
.p3-quote {
font-size: 17px;
line-height: 1.8;
font-style: italic;
font-weight: 300;
color: rgba(232,226,216,0.55);
border-left: 2px solid rgba(196,168,130,0.3);
padding-left: 24px;
margin-bottom: 40px;
}
.p3-meta {
display: flex;
gap: 48px;
padding-top: 32px;
border-top: 1px solid rgba(232,226,216,0.08);
}
.p3-meta-item {
display: flex;
flex-direction: column;
gap: 6px;
}
.p3-meta-num {
font-size: 32px;
font-weight: 300;
color: var(--clay);
}
.p3-meta-label {
font-size: 9px;
letter-spacing: 0.25em;
text-transform: uppercase;
color: rgba(232,226,216,0.25);
}
/* Nav */
.dot-nav {
position: fixed;
bottom: 40px;
left: 50%;
transform: translateX(-50%);
z-index: 900;
display: flex;
gap: 10px;
align-items: center;
}
.dot {
width: 5px;
height: 5px;
border-radius: 50%;
background: rgba(26,20,16,0.2);
border: none;
cursor: pointer;
transition: transform .35s,background .25s;
}
.dot.on-dark {
background: rgba(232,226,216,0.2);
}
.dot.active {
transform: scale(1.8);
background: var(--clay);
}
.dot.on-dark.active {
background: var(--clay);
}
@media (max-width: 768px) {
#p0,
#p2 {
grid-template-columns: 1fr;
}
.p0-right,
.p2-right {
display: none;
}
.p0-left,
.p2-left,
.p3-content {
padding: 48px 28px;
}
#p1 {
padding: 48px 28px;
}
.p1-grid {
grid-template-columns: 1fr 1fr;
}
.p3-meta {
gap: 24px;
}
}
```
## JavaScript
```js
const canvas=document.getElementById('inkCanvas');
const ctx=canvas.getContext('2d');
const pages=document.querySelectorAll('.page');
const dots=document.querySelectorAll('.dot');
const darkPages=[1,3];
let cur=0,busy=false;
function resize(){
canvas.width=window.innerWidth;
canvas.height=window.innerHeight;
}
resize();
window.addEventListener('resize',resize);
function inkTransition(x,y,color,midCallback,done){
const W=canvas.width,H=canvas.height;
const maxR=Math.sqrt(W*W+H*H)*0.6;
let r=0,phase=0;
const speed=28;
const roughnessSeeds=Array.from({length:16},(_,i)=>({
angle:i/16*Math.PI*2,
amp:Math.random()*0.15+0.05,
freq:Math.random()*3+2,
phase:Math.random()*Math.PI*2
}));
function draw(){
ctx.clearRect(0,0,W,H);
if(phase===0&&r>maxR*0.45&&!midCallback._called){
midCallback();midCallback._called=true;
}
if(phase===1&&r<=0){
ctx.clearRect(0,0,W,H);done&&done();return;
}
ctx.beginPath();
const steps=120;
for(let i=0;i<=steps;i++){
const a=i/steps*Math.PI*2;
let rad=r;
roughnessSeeds.forEach(s=>{
rad+=r*s.amp*Math.sin(a*s.freq+s.phase+(phase===0?r*0.01:-r*0.01));
});
rad=Math.max(0,rad);
const px=x+Math.cos(a)*rad;
const py=y+Math.sin(a)*rad;
i===0?ctx.moveTo(px,py):ctx.lineTo(px,py);
}
ctx.closePath();
const g=ctx.createRadialGradient(x,y,0,x,y,r*1.1);
g.addColorStop(0,color);
g.addColorStop(0.7,color);
g.addColorStop(1,'transparent');
ctx.fillStyle=g;
ctx.fill();
ctx.beginPath();
for(let i=0;i<=steps;i++){
const a=i/steps*Math.PI*2;
let rad=r;
roughnessSeeds.forEach(s=>{rad+=r*s.amp*Math.sin(a*s.freq+s.phase+(phase===0?r*0.01:-r*0.01));});
const edgeR=rad*0.92;
const px=x+Math.cos(a)*edgeR;
const py=y+Math.sin(a)*edgeR;
i===0?ctx.moveTo(px,py):ctx.lineTo(px,py);
}
ctx.closePath();
ctx.fillStyle='rgba(0,0,0,0.08)';
ctx.fill();
if(phase===0){
r+=speed;
if(r>=maxR){phase=1;}
}else{
r-=speed*1.4;
}
requestAnimationFrame(draw);
}
draw();
}
const inkColors=['#1a1410','#e8e2d8','#1a1410','#1a1410'];
function goTo(n,ex,ey){
if(busy||n===cur)return;
busy=true;
const prev=cur;cur=n;
const x=ex||window.innerWidth/2;
const y=ey||window.innerHeight/2;
const color=inkColors[cur];
const mid=()=>{
pages[prev].classList.remove('active');
pages[cur].classList.add('active');
dots.forEach((d,i)=>{
d.classList.toggle('active',i===cur);
d.classList.toggle('on-dark',darkPages.includes(cur));
});
};
mid._called=false;
inkTransition(x,y,color,mid,()=>{busy=false;});
}
dots.forEach(d=>d.addEventListener('click',e=>{
const r=e.currentTarget.getBoundingClientRect();
goTo(+d.dataset.i,r.left+r.width/2,r.top+r.height/2);
}));
let wt=0;
window.addEventListener('wheel',e=>{
const now=Date.now();if(now-wt<1000)return;wt=now;
goTo(Math.max(0,Math.min(3,cur+(e.deltaY>0?1:-1))),window.innerWidth/2,window.innerHeight/2);
},{passive:true});
document.addEventListener('keydown',e=>{
const c=window.innerWidth/2,m=window.innerHeight/2;
if(e.key==='ArrowRight')goTo(Math.min(3,cur+1),c,m);
if(e.key==='ArrowLeft')goTo(Math.max(0,cur-1),c,m);
});
```