<div class="invitation">
<div class="frame" data-step="0">
<div class="paper">
<div class="fold fold-00">
<div class="inner">
<div class="side exterior dark-background"><div class="copy-wrapper">
Welcome to the world of Tomorrow!
</div></div>
<div class="side interior"><div class="img-wrapper"><img src="https://via.placeholder.com/660x510/ff6f00/fff" /></div></div>
</div>
<div class="next-fold">
<div class="fold fold-01">
<div class="inner">
<div class="side exterior dark-background envelope-back"><div class="copy-wrapper">
Cool! Just like in Star Trek!<br /><strong>Ow!</strong>
</div></div>
<div class="side interior"><div class="img-wrapper"><img src="https://via.placeholder.com/660x510/c71000/fff" /></div></div>
</div>
<div class="next-fold">
<div class="fold fold-02">
<div class="inner">
<div class="side exterior dark-background"><div class="copy-wrapper">
– Good afternoon, sir. Name?
<br />– Uh, Fry.
<br />– I'm Leela. Now, it's New Year's Eve so I'd like to decide your fate quickly and get out of here.
<br />– Can I ask you a question?
</div></div>
<div class="side interior"><div class="img-wrapper"><img src="https://via.placeholder.com/660x510/8a4198/fff" /></div></div>
</div>
<div class="next-fold">
<div class="fold fold-03">
<div class="inner">
<div class="side exterior dark-background"><div class="copy-wrapper">
– As long as it's not about my eye.
<br />– Uh...
<br />– Is it about my eye?
<br />– Sort of.
<br />– Just ask the question.
<br />– What's with the eye?
</div></div>
<div class="side interior"><div class="img-wrapper"><img src="https://via.placeholder.com/660x510/5a9599/fff" /></div></div>
</div>
<div class="next-fold">
<div class="fold fold-04">
<div class="inner">
<div class="side exterior dark-background"><div class="copy-wrapper">
– My God! A million years!
<br />– I'm sure this must be very upsetting for you.
<br />– Y'know, I guess it should be but, actually, I'm glad. I had nothing to live for in my old life. I was broke, I had a humiliating job and I was beginning to suspect my girlfriend might be cheating on me.
<br />– Well, at least here you'll be treated with dignity. Now strip naked and get on the probulator.
</div></div>
<div class="side interior"><div class="img-wrapper"><img src="https://via.placeholder.com/660x510/ff95a8/fff" /></div></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<nav>
<div class="position-indicator">
<span>1</span>
<span>/</span>
<span>5</span>
</div>
<label class="scroll-indicator">Scroll to Learn More</label>
</nav>
</div>
/* =============================================================================
* Invitation Container
* =============================================================================
* These Styles have been modified after uploading them to squarespace to
* account for multiple issues introduced by their cascading styles. Such as:
* - calc() not properly working. I think the CSS gets mangled.
* - margin-top being overwritten. They have a rule that zeroes the mt, you need
* the following format to overrule it .gparent .parent element:first-child()
*
*/
.invitation {
--width: 33vw; // ratio: 22:17 // 0.7727
--height: calc(0.7727 * var(--width)); // 27.0445em = calc(0.7727 * var(--width))
--paper-angle: 35deg; // 180 + 25: We're flipping and tilting the paper a bit
// paper adjustments
// 45deg => 1.4142 => 0.7071
// 35deg => 1.2208 => 0.8191
// 25deg => 1.1034 => 0.9063
--paper-adjustment: 0.78; // opted for a custom that is more visually appealing
position: relative;
margin: auto;
background: #1d1d28;
font-family: sans-serif;
overflow: hidden; // conceals extra width from .frame scroll bar
img { display: block; }
// Letter Design and Layout
// =============================================================================
.frame {
height: 100vh;
margin-right:-1em;
overflow-y: scroll;
// Stage floor light
&::before {
content: '';
display: block;
width: 100%;
height: 16em;
border-radius: 100%;
box-shadow: 0 36em 20em #cbe9ff3d;
background: transparent;
position: fixed;
left: 0; bottom: 32em;
pointer-events: none;
}
}
.paper {
margin: 33vh auto; // 5.4089em = calc(0.2 * var(--height))
}
.paper,
.fold,
.next-fold,
.inner {
width: var(--width); height: var(--height);
transform-style: preserve-3d;
}
.fold {
& > .inner {
position: relative;
backface-visibility: hidden;
transform: translateZ(-1px);
}
.exterior, .interior {
position: absolute;
backface-visibility: hidden;
}
.exterior {
transform: rotateX(180deg);
position: absolute; left: 0; top: 0; right: 0; bottom: 0;
padding: 2em;
display: flex;
justify-content: center;
align-items: center;
}
.side.dark-background {
background-color: #375dd3;
color: #e0f3ff
}
img {
width: 100%;
height: 100%;
}
.copy-wrapper {
font-weight: 300;
font-size: 1.4vw;
}
}
// Unfolding Engine
// =============================================================================
// - All folds start preloaded, so they unfold as we scroll down.
// - First fold starts flipped on its head and seems to unfold up.
// - Subsequent folds simply unfold down.
// - Further inner content is not yet available as it overlaps the visible content.
//
// where first fold starts
.fold-00 {
transform-origin: bottom center;
transform: rotateX(-180deg) translateY(100%);
}
// where subsequent folds start
.next-fold > .fold {
transform: rotateX(180deg);
transform-origin: top center;
& > .next-fold { opacity: 0; }
}
// animation speeds
.paper {
transition: transform .4s ease-in-out;
}
.fold,
.inner { transition: transform 1s ease; }
// where animations end
[data-step="1"],
[data-step="2"],
[data-step="3"],
[data-step="4"],
[data-step="5"] {
.paper {
transform: rotateY(var(--paper-angle)) scaleY(var(--paper-adjustment));
}
.fold.fold-00 {
transform: rotateX(2deg) translateY(0%);
& > .next-fold > .fold > .next-fold { opacity: 1; }
}
.fold.fold-01 {
transform: rotateX(-4deg);
& > .next-fold > .fold > .next-fold { opacity: 1; }
}
}
[data-step="2"],
[data-step="3"],
[data-step="4"],
[data-step="5"] {
.fold.fold-02 {
transform: rotateX(4deg);
& > .next-fold > .fold > .next-fold { opacity: 1; }
}
}
[data-step="3"],
[data-step="4"],
[data-step="5"] {
.fold.fold-03 {
transform: rotateX(-4deg);
& > .next-fold > .fold > .next-fold { opacity: 1; }
}
}
[data-step="4"],
[data-step="5"] {
.fold.fold-04 {
transform: rotateX(4deg);
& > .next-fold > .fold > .next-fold { opacity: 1; }
}
}
// // seen upon first unfolding
// // delay opacity on closing
// .next-fold .next-fold {
// transition:
// transform 1.4s ease,
// opacity .05s 1.35s ease;
// // play opacity immediately and quickly upon opening
// .paper > .trigger:checked ~ .fold > & {
// transition:
// transform 1.4s ease,
// opacity .05s ease;
// }
// }
// // seen upon subsequent unfoldings
// // delay helps match the opacity to when the fold turns
// .paper > .trigger:checked ~ .fold > .next-fold .next-fold .next-fold {
// transition:
// transform 1.4s ease,
// opacity .25s .20s ease;
// }
//
// Back of Letter
// =============================================================================
// overwrite and add proper direction
.fold > .inner .exterior.envelope-back { transform: rotateX(180deg) rotateZ(180deg); }
//
// Lightning Effects
// =============================================================================
.side.interior::after {
content: '';
display: block;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
background: linear-gradient(15deg, rgba(0, 0, 0, 0.2) 0%, transparent 50%), linear-gradient(15deg, transparent 70%, rgba(255, 255, 255, 0.1) 100%);
box-shadow: inset 0 -2px 4px rgb(0 0 0 / 5%), inset 0 2px 4px rgb(255 255 255 / 5%);
transition: 0s box-shadow ease;
}
[data-step="0"] .fold-00 .side.interior::after,
[data-step="1"] .fold-01 .side.interior::after,
[data-step="2"] .fold-02 .side.interior::after,
[data-step="3"] .fold-03 .side.interior::after {
transition: 1.6s box-shadow ease;
box-shadow: inset 3px -2px 2px #121a21;
}
//
// Controls & User Interface
// =============================================================================
nav {
// HUD text styles
font-size: .625em;
font-weight: 500;
color: rgba(255,255,255,.8);
text-shadow: 0 2px 2px rgba(0,0,0,.4);
text-transform: uppercase;
letter-spacing: 1px;
text-align: center;
.position-indicator {
position: absolute;
height: 5em;
margin: auto;
top: 0; bottom: 0;
right: 2em;
// override squarespace rule
&:first-child { margin-top: auto; }
// stack indicator
span { display: block; }
// style a prettier slash
span:nth-child(2) {
color: transparent;
text-shadow: initial;
position: relative;
}
span:nth-child(2)::after {
content: '';
width: 0; height: 3em;
position: absolute;
margin: auto; left: 0; right: 0; top: 0; bottom: 0;
border-right: 1px solid rgba(255,255,255,.6);
transform: rotateZ(45deg);
}
// tuck numbers closer to slash
span:first-child { margin-left: -.75em; }
span:last-child { margin-right: -1.125em; }
// increase scale
// modifies font size without altering overall size or positioning
span:first-child,
span:last-child {
transform: scale(1.25);
}
}
.scroll-indicator {
position: absolute;
width: 8em;
margin: auto;
left: 0; right: 0;
bottom: 2em;
transition: .1s .6s opacity ease, .4s .4s bottom ease;
&::after {
content: '';
display: block;
width: 2em; height: 2em;
position: absolute;
border: 1px solid rgba(255,255,255,.4);
border-width: 0 1px 1px 0;
transform: rotateZ(45deg);
top: -4.5em;
left: 0; right: 0; margin: auto;
animation: 2s .25s scrollMe ease-in-out alternate infinite;
box-shadow: 4px 4px 4px rgba(0,0,0,.2);
}
&.is-invisible {
opacity: 0;
bottom: 0;
pointer-events: none;
transition: .3s opacity ease, .4s bottom ease;
}
}
}
@keyframes scrollMe {
0% { transform: rotateZ(45deg); }
100% { transform: rotateZ(45deg) translateX(1em) translateY(1em); }
}
.color-picker { position: fixed; }
}
View Compiled
function invitationInitialize(context) {
const frame = context.querySelector('.frame');
const paper = context.querySelector('.paper');
const foldHeight = paper.offsetHeight * .78;
let animationOffset = foldHeight * 0.25; // first scroll happens 1/4 fold earlier.
// set total paper height based on number of folds + offset
paper.style = `height: ${(foldHeight * 5) + animationOffset}px;`;
function unfold() {
const scrollDistance = frame.scrollTop;
const folds = context.querySelectorAll('.fold');
let highestLevel = 1;
for (let i = 0; i < folds.length; i++) {
const stepThreshold = ((i * foldHeight) - animationOffset);
if ( scrollDistance > stepThreshold ) {
frame.dataset.step = i;
highestLevel = i + 1;
}
}
// Set position indicator
const positionIndicator = context.querySelector('.position-indicator span:first-child');
positionIndicator.innerHTML = highestLevel;
// Determine when to hide the scroll indicator
const scrollIndicator = context.querySelector('.scroll-indicator');
if (scrollDistance > animationOffset) {
scrollIndicator.classList.add('is-invisible');
} else {
scrollIndicator.classList.remove('is-invisible');
}
}
frame.addEventListener("scroll", unfold);
}
const invitation = document.querySelector('.invitation');
if (invitation) invitationInitialize(invitation);
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.