Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Auto Save

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <nav>
  <ul>
    <li>Nalgene</li>
    <li>Montbell</li>
    <li>Arc'teryx</li>
    <li>Snow Peak</li>
    <li>Aquapac</li>
    <li>Colorful Standard</li>
    <li>Teva</li>
    <li>Nalgene</li>
    <li>Montbell</li>
    <li>Arc'teryx</li>
    <li>Snow Peak</li>
    <li>Aquapac</li>
    <li>Colorful Standard</li>
    <li>Teva</li>
    <li>Nalgene</li>
    <li>Montbell</li>
    <li>Arc'teryx</li>
    <li>Snow Peak</li>
    <li>Aquapac</li>
    <li>Colorful Standard</li>
  </ul>
</nav>
              
            
!

CSS

              
                @import url('https://fonts.googleapis.com/css2?family=Space+Mono&display=swap');

:root {
  --vw: 100vw;
  --blue: #9FF1FF;
  --black: #141414;
  --white: #F9F9F7;
}

html {
  position: fixed;
  overflow: hidden;
  font-size: calc(var(--vw) / 768 * 10);
  line-height: 1;
  
  @media (min-width: 768px) {
    font-size: calc(var(--vw) / 1200 * 10);
  }

  @media (min-width: 1200px) {
    font-size: calc(var(--vw) / 1512 * 10);
  }
}

body {
  background-color: var(--white);
  font-family: 'Space Mono', monospace;
  background: var(--blue);
  color: var(--black);
  position: fixed;
  letter-spacing: -0.03em;
  line-height: 1;
  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-text-size-adjust: 100%;
}

nav {
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  height: 100vh;
  width: 100vw;
}

ul {
  position: relative;
  height: 100%;
  width: 100%;
}

li {
  margin: 0 0 0 -0.3rem;
  font-size: 2.8rem;
  color: var(--black);
  text-align: left;
  transition: 180ms ease font-weight, 180ms ease color;
  display: block;
  position: absolute;
}
              
            
!

JS

              
                console.clear();

gsap.registerPlugin(Draggable);

const nav = document.querySelector('nav');
const wrapper = document.querySelector('ul');
const items = document.querySelectorAll('li');
const itemHeight = items[0].clientHeight;
const itemsHeight = items.length * itemHeight;
const itemsProgress = gsap.utils.wrap(0, 1);

// Set the height of each item so they stack
items.forEach((item, index) => {
  gsap.set(item, {
    y: index * itemHeight,
    top: -itemHeight,
  })
})

// Create animation... try unpausing it to see what's going on
const animation = gsap.to("li", {
  duration: 1,
  y: `+=${itemsHeight}`, // the total height of the items
  paused: true,
  ease: "linear", // disable easing, it f's with this
  repeat: -1, // repeat infinitely
  modifiers: {
    y: (y) => {
      // Set the Y value to the value of the remainder after dividing the current Y position by the total number of items. This is what sets the item position and calculates whether its position should be shifted back to the top of the page. If the itemsHeight is 600 and the Y is 600, then the remainder is 0 so it gets moved back to the top. If the itemsHeight is 600 and the Y is 700, the remainder is 100 so the item is set to 100px
      y = parseFloat(y) % itemsHeight;
      return `${y}px`;
    }
  }
}).progress(1 / items.length);

// Update progress
function updateProgress() {
  animation.progress(itemsProgress(props("y") / itemsHeight));
}

// Create a proxy to capture the dragging without actually affecting the element. In other words, we want to understand the users drag behaviour without actually dragging the thing, because we just use this drag data elsewhere
const proxy = document.createElement("div");
const props = gsap.getProperty(proxy);

Draggable.create(proxy, {
  trigger: nav,
  throwProps: true,
  onDrag: updateProgress,
  onThrowUpdate: updateProgress,
  snap: {
    y: gsap.utils.snap(itemHeight)
  }
})
              
            
!
999px

Console