Pen Settings

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

You're using npm packages, so we've auto-selected Babel for you here, which we require to process imports and make it all work. If you need to use a different JavaScript preprocessor, remove the packages in the npm tab.

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

Use npm Packages

We can make npm packages available for you to use in your JavaScript. We use webpack to prepare them and make them available to import. We'll also process your JavaScript with Babel.

⚠️ This feature can only be used by logged in users.

Code Indentation

     

Save Automatically?

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.

HTML Settings

Here you can Sed posuere consectetur est at lobortis. Donec ullamcorper nulla non metus auctor fringilla. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec id elit non mi porta gravida at eget metus. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.

            
              <div class="demo-spacer">Scroll Down &hellip;</div>

<section class="image-column-grid viewport-height">
  <div class="max-wrap"><div class="column reverse">
      <a class="item">
        <div class="text"><h3>Banners</h3></div>
      </a>
      <a class="item vertical-aspect">
        <div class="text"><h3>Pens</h3></div>
      </a>
      <a class="item">
        <div class="text"><h3>Promotional Products</h3></div>
      </a>
      <a class="item">
        <div class="text"><h3>Banners</h3></div>
      </a>
      <a class="item vertical-aspect">
        <div class="text"><h3>Pens</h3></div>
      </a>
      <a class="item">
        <div class="text"><h3>Promotional Products</h3></div>
      </a>
    </div><div class="column">
      <a class="item">
        <div class="text"><h3>Hats</h3></div>
      </a>
      <a class="item">
        <div class="text"><h3>Polo Shirts</h3></div>
      </a>
      <a class="item vertical-aspect">
        <div class="text"><h3>Custom T-Shirts</h3></div>
      </a>
      <a class="item">
        <div class="text"><h3>Hats</h3></div>
      </a>
      <a class="item">
        <div class="text"><h3>Polo Shirts</h3></div>
      </a>
      <a class="item vertical-aspect">
        <div class="text"><h3>Custom T-Shirts</h3></div>
      </a>
    </div><div class="column reverse">
      <a class="item">
        <div class="text"><h3>Envelopes</h3></div>
      </a>
      <a class="item vertical-aspect">
        <div class="text"><h3>Business Cards</h3></div>
      </a>
      <a class="item">
        <div class="text"><h3>Letterheads</h3></div>
      </a>
      <a class="item">
        <div class="text"><h3>Envelopes</h3></div>
      </a>
      <a class="item vertical-aspect">
        <div class="text"><h3>Business Cards</h3></div>
      </a>
      <a class="item">
        <div class="text"><h3>Letterheads</h3></div>
      </a>
    </div></div>
</section>

<div class="demo-spacer"></div>
<div class="demo-spacer"></div>
<div class="demo-spacer"></div>
            
          
!
            
              /* Reset */
html { box-sizing: border-box; }
*, *:before, *:after { box-sizing: inherit; }

/* Mixin for vertical alignment */
@mixin vertical-align($position: absolute) {
  position: $position;
  top: 50%;
  -webkit-transform: translateY(-50%);
  -ms-transform: translateY(-50%);
  transform: translateY(-50%);
}

.max-wrap { 
  max-width:1200px; 
  margin-left:auto; 
  margin-right:auto; 
}

// Uneven image grid items in columns. With middle
// column scrolling in reverse, vertically, w/ JS.
.image-column-grid {
  position:relative;
  background:#e7e2e7;
  min-height:400px;
  overflow:hidden;
  margin:0 auto;
  
  // Clearfix
  &:after { content:""; display:table; clear:both; }
  
  .column {
    width:33.33%;
    display:inline-block;
    position:relative;
    vertical-align:top;
    min-height:100%;
    padding:0 1px 0 1px; /* Gutters */
    
    &.reverse { /* Margin calculated by JS */ }
    &.first-not-in-view .item .text { opacity:0; }
  }
  
  .item {
    background:#004c68;
    transition:all 0.3s ease;
    position:relative;
    display:block;
    margin-bottom:2px;
    min-height:100px;
    vertical-align:top;
    
    &:last-child { margin-bottom:0; }
    
    // Keep aspect ratio
    &:after {
      display: block; content: ""; width: 100%;
      padding-top: (334 / 465) * 100%;
    }
    // Grid items w/ different vertical aspect ratio
    &.vertical-aspect:after {
      padding-top: (490 / 465) * 100%;
    }

    .text {
      @include vertical-align(absolute);
      width:100%;
      color:#ffffff;
      padding:18px;
      opacity:1.0;
      transition:opacity 0.35s ease;
    }
    
    h3 { 
      margin:0; 
      font-size:32px;
      color:#ffffff;
      font-family:sans-serif;
      text-align:center;
      font-weight:normal;
    }
    
    &:hover, &:focus {
      background:#111111;
    }
  }
}

// DEMO DISPLAY ONLY
.demo-spacer { 
  display:block; 
  height:100px; 
  text-align:center; 
  text-transform:uppercase; 
  font-family:sans-serif; 
  font-size:12px; 
  padding:32px; 
}
.image-column-grid a { cursor:pointer; }




            
          
!
            
              (function($) {


  // Make elements equal to the viewport height. 
  // Or at least as tall as the viewport height, if second parameter is true. Sets the 'height' or 'min-height' property.
	function setToViewportHeight( selector, forceViewportMax )
  {
		var viewportHeight = $(window).innerHeight();

		$(selector).each(function(){
			var height = $(this).height();

      if ( forceViewportMax == true ){
        // Set height to same as viewport height.
        if ( height < viewportHeight ){
          $(this).height(viewportHeight);
        }  
      } 
      else {
        // Min height of element that is the same as the viewport
        $(this).css('min-height', viewportHeight + "px");
      }

		});
	}
  
  // Reverse scroll of the middle column in the Image Column Grid.
  //   (Organized within the "module pattern": self-executes an 
  //    anonymous function that returns an object) 
  var reverseScroll = (function(){
    
    var selector = '.image-column-grid .reverse';
    var selectorNotReverse = '.image-column-grid .column:not(.reverse)';
    var wrapSelector = '.image-column-grid';
    // Number of column pixels always visible, until scrolling begins
    var fixedOffsetEdge = 180; 
    
    var setupBindings = function(){
      
      // Regular column element(s)
  $(selectorNotReverse).each(function(index, el){
        var startOffset = window.innerHeight - fixedOffsetEdge;
        $(el).css('margin-top', startOffset+"px");
      });
      
      // Reversed column element(s)
      $(selector).each(function(index, el){
          // Get container
          var container = $(el).closest(wrapSelector);
          if ( $(container).length == 0){ return; }
        
          // Get heights
          var wrapHeight = $(container).outerHeight();
          var elHeight = $(el).outerHeight();
          var firstChildHeight = $('*:first-child', el).outerHeight();
          var lastChildHeight = $('*:last-child', el).outerHeight();
        
          // Total amount that margin changes (top to bottom of scroll) 
          // And don't scroll past last item (first child)
          var scrollTotal = (wrapHeight + elHeight) -firstChildHeight - fixedOffsetEdge;

          // Set a negative top value to move it out of sight.
          $(el).css('top', (-elHeight + fixedOffsetEdge) + "px");

          // When scrolling, change margin proportionately.
          $(window).on('scroll', function(){
            // Any part of element enters viewport
            if ( isScrolledIntoView( $(container)[0], false) && wrapHeight > 0 )
            {
              // Start scrolling when this point is reached
              var scrollStart = window.innerHeight;
              
              // Where element is offset from top of the page. 
              // If it's visible on load, the starting scroll point 
              // starts there, rather than when element comes into view.
              var containerOffset = $(container).offset().top;
              //console.log(containerOffset);
              if (containerOffset < window.innerHeight){
                scrollStart = window.innerHeight - containerOffset;
              }
              //console.log("scrollStart:"+scrollStart);
              
              // Where top of container is scrolled to in the window
              var elemTop = $(container)[0].getBoundingClientRect().top;

              // Percent scaled ratio (0 to 1.0)
              var minToStartScroll = fixedOffsetEdge;
              if (scrollStart > minToStartScroll){
                minToStartScroll = scrollStart;
              }
              var percentScrolled = (window.innerHeight - elemTop - minToStartScroll) / (wrapHeight - window.innerHeight + lastChildHeight + fixedOffsetEdge);
              //console.log("min to start scroll:"+minToStartScroll);
              console.log("percentScrolled: "+percentScrolled);
              if (percentScrolled < 0){ percentScrolled = 0; }
              if (percentScrolled > 1){ percentScrolled = 1.0; }

              // Scale total margin change by percent scrolled.
              var newMargin = (scrollTotal * percentScrolled);
              // Set new margin
              $(el).css('transform', 'translateY(' + newMargin + 'px)');
            }
          });

        });
      
    } // end setupBindings
    
    var init = function(){
      setupBindings();
    }
    
    // Public API
    return {
        init: init
    };
  })();
  

  // Check if element is scrolled into view.
  // Pass true to "fullyInView" if the whole element must be visible
  function isScrolledIntoView(el, fullyInView) {
    var elemTop = el.getBoundingClientRect().top;
    var elemBottom = el.getBoundingClientRect().bottom;
    var isVisible = false;

    if (fullyInView == true){
      // element must be fully within viewport
      isVisible = (elemTop >= 0) && (elemBottom <= window.innerHeight);
    } else {
      // any part of element is visible in the viewport
      isVisible = elemTop <= window.innerHeight;
    }
    return isVisible;
  }
  
  // INIT
  setToViewportHeight('.viewport-height', false);
  reverseScroll.init();
  
  // Window Resize
  // To-Do/Should Do: debounce
  $(window).resize(function() {
    setToViewportHeight('.viewport-height', false);
    reverseScroll.init();
    //console.log('resize');
  });
  
})(jQuery);
            
          
!
999px
🕑 One or more of the npm packages you are using needs to be built. You're the first person to ever need it! We're building it right now and your preview will start updating again when it's ready.

Console