<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">
                                        &ndash; Good afternoon, sir. Name?
                                        <br />&ndash; Uh, Fry.
                                        <br />&ndash; 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 />&ndash; 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">
                                                &ndash; As long as it's not about my eye.
                                                <br />&ndash; Uh...
                                                <br />&ndash; Is it about my eye?
                                                <br />&ndash; Sort of.
                                                <br />&ndash; Just ask the question.
                                                <br />&ndash; 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">
                                                        &ndash; My God! A million years!
                                                        <br />&ndash; I'm sure this must be very upsetting for you.
                                                        <br />&ndash; 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 />&ndash; 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);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.