JSFiddle - React, Tailwind, and code Playground

by lollero

HTML

<script src="https://fonts.googleapis.com/css?family=Libre+Franklin:200,400,600,700|Nunito:300,400"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.4.1/jquery.easing.min.js"></script>
<!-- Used libraries/services -->
<!-- http://lesscss.org/ -->
<!-- https://fonts.google.com/ -->
<!-- http://gsgd.co.uk/sandbox/jquery/easing/ -->
<!-- https://material.io/icons/ -->

<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

<img 
  class="caption" 
  data-heading-one="Nam at accumsan urna" 
  data-heading-two="Curabitur pulvinar." 
  data-caption="Consectetur adipisicing elit. Autem, consequuntur 1." 
  src="http://joonas.me/1/dribbble-images/dribbble-1.png" alt=""
>

<img 
  class="caption"
  data-color="#333, #fff"
  data-heading-one="Maecenas faucibus convallis" 
  data-heading-two="Nam vitae." 
  data-caption="Aliquam volutpat odio vitae lectus feugiat venenatis. Curabitur id consectetur felis, ut ultricies orci. Nunc viverra quis nisl a consectetur. Ut pellentesque at ante ac porta. Mauris condimentum consequat congue. Phasellus sit amet tristique lacus. Donec commodo tristique sem." 
  src="http://joonas.me/1/dribbble-images/dribbble-2.png" alt=""
>

<img 
  class="caption" 
  data-overlay="true"
  data-heading-one="Mauris vulputate dignissim odio" 
  data-heading-two="Pellentesque pharetra." 
  data-caption="Consectetur adipisicing elit. Autem, consequuntur 3." 
  src="http://joonas.me/1/dribbble-images/dribbble-3.png" alt=""
>

<img 
  class="caption"
  data-heading-one="Sed pellentesque ligula" 
  data-heading-two="Cras nam dapibus feugiat." 
  data-caption="Aliquam posuere, ex sed fringilla fermentum, diam diam tempor enim, egestas laoreet nisl ante sit amet odio. Praesent urna enim, lobortis sed tristique quis, cursus non erat. In tempus, libero feugiat gravida rutrum, nunc mauris facilisis erat, eget blandit risus nulla at dolor. <br><br> Donec a dolor vel felis maximus gravida a eu enim. Donec...

SCSS

.caption-wrapper {
  position: relative;
  float: left;
  width: 430px; // With this we don't need to wait for the images to load before initiating the javascript.
  height: 450px;
  margin: 20px;
  box-shadow: 0 1px 5px rgba(0,0,0, .1);
  overflow: hidden;
  background-clip: padding-box;
  border-radius: 2px;
  cursor: pointer;

  img {
    position: relative;
    z-index: 0;
    display: block;
  }
  
  .caption-overlay {
    position: absolute;
    z-index: 1;
    top: 0px;
    right: 0;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: #fff;
    opacity: .65;
    display: none;
  }
  
  .caption {
    position: absolute;
    z-index: 2;
    left: 0;
    right: 0;
    bottom: 0px;
    width: 100%;
    background: #fff;
    color: #000;

    > div {
      padding: 35px 25px;
    }
    
    .material-icons {
      vertical-align: sub;
      float: right;
      opacity: .5;
    }
      
    &:hover .material-icons {
      opacity: 1;
    }
    
    .caption-h1 {
      font-family: 'Libre Franklin', sans-serif;
      font-weight: 100;
      font-size: 12px;
      opacity: .5;
    }
    
    .caption-h2 {
      font-family: 'Libre Franklin', sans-serif;
      font-weight: 300;
      font-size: 18px;
      opacity: .95;
    }
    
    .caption-text {
      width: 100%;
      overflow: hidden;
      font-family: 'nunito', sans-serif;
      font-weight: 300;
      font-size: 13px;
      opacity: .6;
      
      .divider {
        margin-top: 20px;
        height: 1px;
        background: #f1f1f1;
        margin-bottom: 20px;
      }
    }
    
  }
  
}

JavaScript

$('.caption').each(function() {

	var _this = $(this),
  		_thisD = _this.data();
  
  _this.wrap('<div class="caption-wrapper"></div>');
	_this.removeClass('caption');
	
	var caption = $('<div class="caption"><div></div></div>').insertAfter( _this ).find('div');

	$('\
		<div class="caption-heading">\
			<div class="caption-h1">'+ _thisD.headingOne +'</div>\
      <div class="caption-h2">\
				<span>'+ _thisD.headingTwo +'</span>\
				<i class="material-icons">&#xE5C8;</i>\
			</div>\
		</div>\
	').appendTo( caption );
  
	$('\
  	<div class="caption-text">\
    	<div class="divider"></div>\
    	<span>'+ _thisD.caption +'</span>\
		</div>\
	').appendTo( caption );
  
  var wrapper = _this.parent();

  if ( _thisD.overlay ) {
  	$('<div class="caption-overlay"></div>').appendTo( wrapper );
  }
  
  var overlay = wrapper.find('.caption-overlay');
  
  if ( _thisD.overlay ) {
  	overlay.css({
    	background: _thisD.overlay
    });
  }
  
  if ( _thisD.color ) {
		var color = _thisD.color.split(', ');
    caption.css({ 
      background: color[0],
      color: color[1]
    });
  }
  
  
	// Clear data. 
	_this
		.attr( 'data-heading-one', '' )
		.attr( 'data-heading-two', '' )
		.attr( 'data-caption', '' );
  
  var captionT = wrapper.find('.caption-text'),
      captionT_height = captionT.height();

	captionT.height(0);
  
  wrapper.on("mouseenter mouseleave", function( e ) {
  
    if ( e.type === "mouseenter" ) {
    	
      captionT.animate({
      	height: captionT_height
      }, 180, "easeOutBack");	
      
      overlay.stop().fadeIn( 600 );
      
    }
    else {
    
    	captionT.animate({
      	height: 0
      }, 300, "easeOutBack");
      
    	overlay.stop().fadeOut( 300 );
      
    }

  
  });

});