From our sponsor: Ready to show your plugin skills? Enter the Penpot Plugins Contest (Nov 15-Dec 15) to win cash prizes!
This Blueprint is a responsive grid gallery based on the gallery by Google for the Chromebook Getting Started guide. In this Blueprint we use Masonry for the grid and 3D transforms for navigating the items. For smaller screens we have some example media queries that adjust the grid layout and also the gallery view.
In the gallery view, the arrow keys can be used to navigate and the view can be closed using the “Esc” key.
Please note that we are using CSS 3D Transforms which are only supported in modern browsers.
The images used in the demo were created using the grafitti shapes by Luke Roberts.
The HTML
<div id="grid-gallery" class="grid-gallery"> <section class="grid-wrap"> <ul class="grid"> <li class="grid-sizer"></li><!-- for Masonry column width --> <li> <figure> <img src="img/thumb/1.png" alt="img01"/> <figcaption><h3>Letterpress asymmetrical</h3><p>Chillwave hoodie ea gentrify aute sriracha consequat.</p></figcaption> </figure> </li> <li> <figure> <img src="img/thumb/2.png" alt="img02"/> <figcaption><h3>Vice velit chia</h3><p>Laborum tattooed iPhone, Schlitz irure nulla Tonx retro 90's chia cardigan quis asymmetrical paleo. </p></figcaption> </figure> </li> <li> <figure> <img src="img/thumb/3.png" alt="img03"/> <figcaption><h3>Brunch semiotics</h3><p>Ex disrupt cray yr, butcher pour-over magna umami kitsch before they sold out commodo.</p></figcaption> </figure> </li> <li> <figure> <img src="img/thumb/4.png" alt="img04"/> <figcaption><h3>Chillwave nihil occupy</h3><p>In post-ironic gluten-free deserunt, PBR&B non pork belly cupidatat polaroid. </p></figcaption> </figure> </li> <li> <figure> <img src="img/thumb/5.png" alt="img05"/> <figcaption><h3>Kale chips lomo biodiesel</h3><p>Pariatur food truck street art consequat sustainable, et kogi beard qui paleo. </p></figcaption> </figure> </li> <li> <figure> <img src="img/thumb/6.png" alt="img06"/> <figcaption><h3>Exercitation occaecat</h3><p>Street chillwave hoodie ea gentrify.</p></figcaption> </figure> </li> </ul> </section><!-- // grid-wrap --> <section class="slideshow"> <ul> <li> <figure> <figcaption> <h3>Letterpress asymmetrical</h3> <p>Kale chips lomo biodiesel stumptown Godard Tumblr, mustache sriracha tattooed cray aute slow-carb placeat delectus. Letterpress asymmetrical fanny pack art party est pour-over skateboard anim quis, ullamco craft beer.</p> </figcaption> <img src="img/large/1.png" alt="img01"/> </figure> </li> <li> <figure> <figcaption> <h3>Vice velit chia</h3> <p>Chillwave Echo Park Etsy organic Cosby sweater seitan authentic pour-over. Occupy wolf selvage bespoke tattooed, cred sustainable Odd Future hashtag butcher.</p> </figcaption> <img src="img/large/2.png" alt="img02"/> </figure> </li> <li> <figure> <figcaption> <h3>Brunch semiotics</h3> <p>IPhone PBR polaroid before they sold out meh you probably haven't heard of them leggings tattooed tote bag, butcher paleo next level single-origin coffee photo booth.</p> </figcaption> <img src="img/large/3.png" alt="img03"/> </figure> </li> <li> <figure> <figcaption> <h3>Chillwave nihil occupy</h3> <p>Vice cliche locavore mumblecore vegan wayfarers asymmetrical letterpress hoodie mustache. Shabby chic lomo polaroid, scenester 8-bit Portland Pitchfork VHS tote bag.</p> </figcaption> <img src="img/large/4.png" alt="img04"/> </figure> </li> <li> <figure> <figcaption> <h3>Kale chips lomo biodiesel</h3> <p>Chambray Schlitz pug YOLO, PBR Tumblr semiotics. Flexitarian YOLO ennui Blue Bottle, forage dreamcatcher chillwave put a bird on it craft beer Etsy.</p> </figcaption> <img src="img/large/5.png" alt="img05"/> </figure> </li> <li> <figure> <figcaption> <h3>Exercitation occaecat</h3> <p>Cosby sweater hella lomo Thundercats VHS occupy High Life. Synth pop-up readymade single-origin coffee, fanny pack tousled retro. Fingerstache mlkshk ugh hashtag, church-key ethnic street art pug yr.</p> </figcaption> <img src="img/large/6.png" alt="img06"/> </figure> </li> </ul> <nav> <span class="icon nav-prev"></span> <span class="icon nav-next"></span> <span class="icon nav-close"></span> </nav> <div class="info-keys icon">Navigate with arrow keys</div> </section><!-- // slideshow --> </div><!-- // grid-gallery --> <script src="js/imagesloaded.pkgd.min.js"></script> <script src="js/masonry.pkgd.min.js"></script> <script src="js/classie.js"></script> <script src="js/cbpGridGallery.js"></script> <script> new CBPGridGallery( document.getElementById( 'grid-gallery' ) ); </script>
The CSS
@font-face { font-family: 'fontawesome'; src:url('../fonts/fontawesome/fontawesome.eot?-e43dk9'); src:url('../fonts/fontawesome/fontawesome.eot?#iefix-e43dk9') format('embedded-opentype'), url('../fonts/fontawesome/fontawesome.woff?-e43dk9') format('woff'), url('../fonts/fontawesome/fontawesome.ttf?-e43dk9') format('truetype'), url('../fonts/fontawesome/fontawesome.svg?-e43dk9#fontawesome') format('svg'); font-weight: normal; font-style: normal; } /* Made with http://icomoon.io/app */ /* General style */ .grid-gallery ul { list-style: none; margin: 0; padding: 0; } .grid-gallery figure { margin: 0; } .grid-gallery figure img { display: block; width: 100%; } .grid-gallery figcaption h3 { margin: 0; padding: 0 0 0.5em; } .grid-gallery figcaption p { margin: 0; } /* Grid style */ .grid-wrap { max-width: 69em; margin: 0 auto; padding: 0 1em 1.875em; } .grid { margin: 0 auto; } .grid li { width: 25%; float: left; cursor: pointer; } .grid figure { padding: 15px; -webkit-transition: opacity 0.2s; transition: opacity 0.2s; } .grid li:hover figure { opacity: 0.7; } .grid figcaption { background: #e4e4e4; padding: 25px; } /* Slideshow style */ .slideshow { position: fixed; background: rgba(0,0,0,0.6); width: 100%; height: 100%; top: 0; left: 0; z-index: 500; opacity: 0; visibility: hidden; overflow: hidden; -webkit-perspective: 1000px; perspective: 1000px; -webkit-transition: opacity 0.5s, visibility 0s 0.5s; transition: opacity 0.5s, visibility 0s 0.5s; } .slideshow-open .slideshow { opacity: 1; visibility: visible; -webkit-transition: opacity 0.5s; transition: opacity 0.5s; } .slideshow ul { width: 100%; height: 100%; -webkit-transform-style: preserve-3d; transform-style: preserve-3d; -webkit-transform: translate3d(0,0,150px); transform: translate3d(0,0,150px); -webkit-transition: -webkit-transform 0.5s; transition: transform 0.5s; } .slideshow ul.animatable li { -webkit-transition: -webkit-transform 0.5s; transition: transform 0.5s; } .slideshow-open .slideshow ul { -webkit-transform: translate3d(0,0,0); transform: translate3d(0,0,0); } .slideshow li { width: 660px; height: 560px; position: absolute; top: 50%; left: 50%; margin: -280px 0 0 -330px; visibility: hidden; } .slideshow li.show { visibility: visible; } .slideshow li:after { content: ''; position: absolute; width: 100%; height: 100%; top: 0; left: 0; background: rgba(255,255,255,0.8); -webkit-transition: opacity 0.3s; transition: opacity 0.3s; } .slideshow li.current:after { visibility: hidden; opacity: 0; -webkit-transition: opacity 0.3s, visibility 0s 0.3s; transition: opacity 0.3s, visibility 0s 0.3s; } .slideshow figure { width: 100%; height: 100%; background: #fff; border: 50px solid #fff; overflow: hidden; } .slideshow figcaption { padding-bottom: 20px; } .slideshow figcaption h3 { font-weight: 300; font-size: 200%; } /* Navigation */ .slideshow nav span { position: fixed; z-index: 1000; color: #59656c; text-align: center; padding: 3%; cursor: pointer; font-size: 2.2em; } .slideshow nav span.nav-prev, .slideshow nav span.nav-next { top: 50%; -webkit-transform: translateY(-50%); transform: translateY(-50%); } .slideshow nav span.nav-next { right: 0; } .slideshow nav span.nav-close { top: 0; right: 0; padding: 0.5em 1em; color: #31373a; } .icon:before, .icon:after { font-family: 'fontawesome'; speak: none; font-style: normal; font-weight: normal; font-variant: normal; text-transform: none; line-height: 1; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } span.nav-prev:before { content: "e601"; } span.nav-next:before { content: "e600"; } span.nav-close:before { content: "e602"; } /* Info on arrow key navigation */ .info-keys { position: fixed; top: 10px; left: 10px; width: 60px; font-size: 8px; padding-top: 20px; text-transform: uppercase; color: #fff; letter-spacing: 1px; text-align: center; } .info-keys:before, .info-keys:after { position: absolute; top: 0; width: 16px; height: 16px; border: 1px solid #fff; text-align: center; line-height: 14px; font-size: 12px; } .info-keys:before { left: 10px; content: "e603"; } .info-keys:after { right: 10px; content: "e604"; } /* Example media queries (reduce number of columns and change slideshow layout) */ @media screen and (max-width: 60em) { /* responsive columns; see "Element sizing" on http://masonry.desandro.com/options.html */ .grid li { width: 33.3%; } .slideshow li { width: 100%; height: 100%; top: 0; left: 0; margin: 0; } .slideshow li figure img { width: auto; margin: 0 auto; max-width: 100%; } .slideshow nav span, .slideshow nav span.nav-close { font-size: 1.8em; padding: 0.3em; } .info-keys { display: none; } } @media screen and (max-width: 35em) { .grid li { width: 50%; } } @media screen and (max-width: 24em) { .grid li { width: 100%; } }
Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.
The JavaScript
/** * cbpGridGallery.js v1.0.0 * http://www.codrops.com * * Licensed under the MIT license. * http://www.opensource.org/licenses/mit-license.php * * Copyright 2014, Codrops * http://www.codrops.com */ ;( function( window ) { 'use strict'; var docElem = window.document.documentElement, transEndEventNames = { 'WebkitTransition': 'webkitTransitionEnd', 'MozTransition': 'transitionend', 'OTransition': 'oTransitionEnd', 'msTransition': 'MSTransitionEnd', 'transition': 'transitionend' }, transEndEventName = transEndEventNames[ Modernizr.prefixed( 'transition' ) ], support = { transitions : Modernizr.csstransitions, support3d : Modernizr.csstransforms3d }; function setTransform( el, transformStr ) { el.style.WebkitTransform = transformStr; el.style.msTransform = transformStr; el.style.transform = transformStr; } // from http://responsejs.com/labs/dimensions/ function getViewportW() { var client = docElem['clientWidth'], inner = window['innerWidth']; if( client < inner ) return inner; else return client; } function extend( a, b ) { for( var key in b ) { if( b.hasOwnProperty( key ) ) { a[key] = b[key]; } } return a; } function CBPGridGallery( el, options ) { this.el = el; this.options = extend( {}, this.options ); extend( this.options, options ); this._init(); } CBPGridGallery.prototype.options = { }; CBPGridGallery.prototype._init = function() { // main grid this.grid = this.el.querySelector( 'section.grid-wrap > ul.grid' ); // main grid items this.gridItems = [].slice.call( this.grid.querySelectorAll( 'li:not(.grid-sizer)' ) ); // items total this.itemsCount = this.gridItems.length; // slideshow grid this.slideshow = this.el.querySelector( 'section.slideshow > ul' ); // slideshow grid items this.slideshowItems = [].slice.call( this.slideshow.children ); // index of current slideshow item this.current = -1; // slideshow control buttons this.ctrlPrev = this.el.querySelector( 'section.slideshow > nav > span.nav-prev' ); this.ctrlNext = this.el.querySelector( 'section.slideshow > nav > span.nav-next' ); this.ctrlClose = this.el.querySelector( 'section.slideshow > nav > span.nav-close' ); // init masonry grid this._initMasonry(); // init events this._initEvents(); }; CBPGridGallery.prototype._initMasonry = function() { var grid = this.grid; imagesLoaded( grid, function() { new Masonry( grid, { itemSelector: 'li', columnWidth: grid.querySelector( '.grid-sizer' ) }); }); }; CBPGridGallery.prototype._initEvents = function() { var self = this; // open the slideshow when clicking on the main grid items this.gridItems.forEach( function( item, idx ) { item.addEventListener( 'click', function() { self._openSlideshow( idx ); } ); } ); // slideshow controls this.ctrlPrev.addEventListener( 'click', function() { self._navigate( 'prev' ); } ); this.ctrlNext.addEventListener( 'click', function() { self._navigate( 'next' ); } ); this.ctrlClose.addEventListener( 'click', function() { self._closeSlideshow(); } ); // window resize window.addEventListener( 'resize', function() { self._resizeHandler(); } ); // keyboard navigation events document.addEventListener( 'keydown', function( ev ) { if ( self.isSlideshowVisible ) { var keyCode = ev.keyCode || ev.which; switch (keyCode) { case 37: self._navigate( 'prev' ); break; case 39: self._navigate( 'next' ); break; case 27: self._closeSlideshow(); break; } } } ); // trick to prevent scrolling when slideshow is visible window.addEventListener( 'scroll', function() { if ( self.isSlideshowVisible ) { window.scrollTo( self.scrollPosition ? self.scrollPosition.x : 0, self.scrollPosition ? self.scrollPosition.y : 0 ); } else { self.scrollPosition = { x : window.pageXOffset || docElem.scrollLeft, y : window.pageYOffset || docElem.scrollTop }; } }); }; CBPGridGallery.prototype._openSlideshow = function( pos ) { this.isSlideshowVisible = true; this.current = pos; classie.addClass( this.el, 'slideshow-open' ); /* position slideshow items */ // set viewport items (current, next and previous) this._setViewportItems(); // add class "current" and "show" to currentItem classie.addClass( this.currentItem, 'current' ); classie.addClass( this.currentItem, 'show' ); // add class show to next and previous items // position previous item on the left side and the next item on the right side if( this.prevItem ) { classie.addClass( this.prevItem, 'show' ); var translateVal = Number( -1 * ( getViewportW() / 2 + this.prevItem.offsetWidth / 2 ) ); setTransform( this.prevItem, support.support3d ? 'translate3d(' + translateVal + 'px, 0, -150px)' : 'translate(' + translateVal + 'px)' ); } if( this.nextItem ) { classie.addClass( this.nextItem, 'show' ); var translateVal = Number( getViewportW() / 2 + this.nextItem.offsetWidth / 2 ); setTransform( this.nextItem, support.support3d ? 'translate3d(' + translateVal + 'px, 0, -150px)' : 'translate(' + translateVal + 'px)' ); } }; CBPGridGallery.prototype._navigate = function( dir ) { if( this.isAnimating ) return; if( dir === 'next' && this.current === this.itemsCount - 1 || dir === 'prev' && this.current === 0 ) { this._closeSlideshow(); return; } this.isAnimating = true; // reset viewport items this._setViewportItems(); var self = this, itemWidth = this.currentItem.offsetWidth, // positions for the centered/current item, both the side items and the incoming ones transformLeftStr = support.support3d ? 'translate3d(-' + Number( getViewportW() / 2 + itemWidth / 2 ) + 'px, 0, -150px)' : 'translate(-' + Number( getViewportW() / 2 + itemWidth / 2 ) + 'px)', transformRightStr = support.support3d ? 'translate3d(' + Number( getViewportW() / 2 + itemWidth / 2 ) + 'px, 0, -150px)' : 'translate(' + Number( getViewportW() / 2 + itemWidth / 2 ) + 'px)', transformCenterStr = '', transformOutStr, transformIncomingStr, // incoming item incomingItem; if( dir === 'next' ) { transformOutStr = support.support3d ? 'translate3d( -' + Number( (getViewportW() * 2) / 2 + itemWidth / 2 ) + 'px, 0, -150px )' : 'translate(-' + Number( (getViewportW() * 2) / 2 + itemWidth / 2 ) + 'px)'; transformIncomingStr = support.support3d ? 'translate3d( ' + Number( (getViewportW() * 2) / 2 + itemWidth / 2 ) + 'px, 0, -150px )' : 'translate(' + Number( (getViewportW() * 2) / 2 + itemWidth / 2 ) + 'px)'; } else { transformOutStr = support.support3d ? 'translate3d( ' + Number( (getViewportW() * 2) / 2 + itemWidth / 2 ) + 'px, 0, -150px )' : 'translate(' + Number( (getViewportW() * 2) / 2 + itemWidth / 2 ) + 'px)'; transformIncomingStr = support.support3d ? 'translate3d( -' + Number( (getViewportW() * 2) / 2 + itemWidth / 2 ) + 'px, 0, -150px )' : 'translate(-' + Number( (getViewportW() * 2) / 2 + itemWidth / 2 ) + 'px)'; } // remove class animatable from the slideshow grid (if it has already) classie.removeClass( self.slideshow, 'animatable' ); if( dir === 'next' && this.current < this.itemsCount - 2 || dir === 'prev' && this.current > 1 ) { // we have an incoming item! incomingItem = this.slideshowItems[ dir === 'next' ? this.current + 2 : this.current - 2 ]; setTransform( incomingItem, transformIncomingStr ); classie.addClass( incomingItem, 'show' ); } var slide = function() { // add class animatable to the slideshow grid classie.addClass( self.slideshow, 'animatable' ); // overlays: classie.removeClass( self.currentItem, 'current' ); var nextCurrent = dir === 'next' ? self.nextItem : self.prevItem; classie.addClass( nextCurrent, 'current' ); setTransform( self.currentItem, dir === 'next' ? transformLeftStr : transformRightStr ); if( self.nextItem ) { setTransform( self.nextItem, dir === 'next' ? transformCenterStr : transformOutStr ); } if( self.prevItem ) { setTransform( self.prevItem, dir === 'next' ? transformOutStr : transformCenterStr ); } if( incomingItem ) { setTransform( incomingItem, dir === 'next' ? transformRightStr : transformLeftStr ); } var onEndTransitionFn = function( ev ) { if( support.transitions ) { if( ev.propertyName.indexOf( 'transform' ) === -1 ) return false; this.removeEventListener( transEndEventName, onEndTransitionFn ); } if( self.prevItem && dir === 'next' ) { classie.removeClass( self.prevItem, 'show' ); } else if( self.nextItem && dir === 'prev' ) { classie.removeClass( self.nextItem, 'show' ); } if( dir === 'next' ) { self.prevItem = self.currentItem; self.currentItem = self.nextItem; if( incomingItem ) { self.nextItem = incomingItem; } } else { self.nextItem = self.currentItem; self.currentItem = self.prevItem; if( incomingItem ) { self.prevItem = incomingItem; } } self.current = dir === 'next' ? self.current + 1 : self.current - 1; self.isAnimating = false; }; if( support.transitions ) { self.currentItem.addEventListener( transEndEventName, onEndTransitionFn ); } else { onEndTransitionFn(); } }; setTimeout( slide, 25 ); } CBPGridGallery.prototype._closeSlideshow = function( pos ) { // remove class slideshow-open from the grid gallery elem classie.removeClass( this.el, 'slideshow-open' ); // remove class animatable from the slideshow grid classie.removeClass( this.slideshow, 'animatable' ); var self = this, onEndTransitionFn = function( ev ) { if( support.transitions ) { if( ev.target.tagName.toLowerCase() !== 'ul' ) return; this.removeEventListener( transEndEventName, onEndTransitionFn ); } // remove classes show and current from the slideshow items classie.removeClass( self.currentItem, 'current' ); classie.removeClass( self.currentItem, 'show' ); if( self.prevItem ) { classie.removeClass( self.prevItem, 'show' ); } if( self.nextItem ) { classie.removeClass( self.nextItem, 'show' ); } // also reset any transforms for all the items self.slideshowItems.forEach( function( item ) { setTransform( item, '' ); } ); self.isSlideshowVisible = false; }; if( support.transitions ) { this.el.addEventListener( transEndEventName, onEndTransitionFn ); } else { onEndTransitionFn(); } }; CBPGridGallery.prototype._setViewportItems = function() { this.currentItem = null; this.prevItem = null; this.nextItem = null; if( this.current > 0 ) { this.prevItem = this.slideshowItems[ this.current - 1 ]; } if( this.current < this.itemsCount - 1 ) { this.nextItem = this.slideshowItems[ this.current + 1 ]; } this.currentItem = this.slideshowItems[ this.current ]; } // taken from https://github.com/desandro/vanilla-masonry/blob/master/masonry.js by David DeSandro // original debounce by John Hann // http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/ CBPGridGallery.prototype._resizeHandler = function() { var self = this; function delayed() { self._resize(); self._resizeTimeout = null; } if ( this._resizeTimeout ) { clearTimeout( this._resizeTimeout ); } this._resizeTimeout = setTimeout( delayed, 50 ); } CBPGridGallery.prototype._resize = function() { if ( this.isSlideshowVisible ) { // update width value if( this.prevItem ) { var translateVal = Number( -1 * ( getViewportW() / 2 + this.prevItem.offsetWidth / 2 ) ); setTransform( this.prevItem, support.support3d ? 'translate3d(' + translateVal + 'px, 0, -150px)' : 'translate(' + translateVal + 'px)' ); } if( this.nextItem ) { var translateVal = Number( getViewportW() / 2 + this.nextItem.offsetWidth / 2 ); setTransform( this.nextItem, support.support3d ? 'translate3d(' + translateVal + 'px, 0, -150px)' : 'translate(' + translateVal + 'px)' ); } } } // add to global namespace window.CBPGridGallery = CBPGridGallery; })( window );
Hello friend… I am having some problems with this plugin, I’m using and tags in the text inside my grids, but it seems that the plugin use them like another grid and adds them properties that makes them behave strangely
I hope that someone cans help me with this.. (sorry for my english, is not my native language)
Have a nice day
Hi,
I’m very keen in this style but all I know is just copy and paste the codes.
Do I copy all of them into the WordPress page?
Pls help,
Adrian
Yes.
I wanted to add text link below the sentence of text that appears under the thumbnails on the Masonry layout so that the text link is clickable to another page on the website. I got it to work but when I click on it, it opens the pop-up slideshow AND THEN goes to the link I specified. If I assign a CSS class to that link is there a way to write an exception for it in the .js so that the link is clickable without activating the pop-up slideshow? Everything else can be clickable on the main page – just not that link. I’m not so great at .js – so would appreciate any help – thank you.
PS – I meant to add that the link I want to exclude from popping up the slideshow is right under tags.
Hi!
I’m using this grid for a personal website. I have updated it a bit, slightly review the design and fix minor bugs!
You can check the code (and fork it if you wish) here:
https://github.com/kevindelord/picturemyworld.github.io
The live demo is right there and is online using Github Pages and Jekyll.
http://picturemy.world/
Best,