From our sponsor: Leverage AI for dynamic, custom website builds with ease.
This Blueprint is a responsive product grid layout with comparison functionality. A maximum of three items can be selected for the product comparison. The comparison view shows flexbox-powered columns or rows (depending on the viewport size) that appear with a slide-in effect. There are a couple of example media queries for smaller viewports.
Browser Support:- ChromeSupported
- FirefoxSupported
- Internet ExplorerSupported from version 10+
- SafariSupported
- OperaSupported
The HTML
<!-- Compare basket --> <div class="compare-basket"> <!-- comparison item miniatures come here --> <button class="action action--button action--compare"><i class="fa fa-check"></i><span class="action__text">Compare</span></button> </div> <!-- Main view --> <div class="view"> <!-- Blueprint header --> <header class="bp-header cf"><!-- ... --></header> <!-- Product grid --> <section class="grid"> <!-- Products --> <div class="product"> <div class="product__info"> <img class="product__image" src="images/1.png" alt="Product 1" /> <h3 class="product__title">Chryseia</h3> <span class="product__year extra highlight">2011</span> <span class="product__region extra highlight">Douro</span> <span class="product__varietal extra highlight">Touriga Nacional</span> <span class="product__alcohol extra highlight">13%</span> <span class="product__price highlight">$55.90</span> <button class="action action--button action--buy"> <i class="fa fa-shopping-cart"></i> <span class="action__text">Add to cart</span> </button> </div> <label class="action action--compare-add"> <input class="check-hidden" type="checkbox" /> <i class="fa fa-plus"></i> <i class="fa fa-check"></i> <span class="action__text action__text--invisible">Add to compare</span> </label> </div> <div class="product"> <!-- ... --> </div> <div class="product"> <!-- ... --> </div> <!-- ... --> </section> </div><!-- /view --> <!-- product compare wrapper --> <section class="compare"> <!-- comparison items come here --> <button class="action action--close"><i class="fa fa-remove"></i><span class="action__text action__text--invisible">Close comparison overlay</span></button> </section> <script src="js/classie.js"></script> <script src="js/main.js"></script>
The CSS
.view { -webkit-transition: -webkit-transform 0.4s ease-in-out; transition: transform 0.4s ease-in-out; } .view--compare { -webkit-transform: scale3d(0.9,0.9,1); transform: scale3d(0.9,0.9,1); } /* product grid */ .grid { margin: 0 auto; padding: 4em 1em; max-width: 1200px; text-align: center; overflow: hidden; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } /* if flexbox is supported, let's use it to lay out the products */ .flexbox .grid { display: -ms-flexbox; display: -webkit-flex; display: flex; -webkit-flex-direction: row; -ms-flex-direction: row; flex-direction: row; -webkit-flex-wrap: wrap; -ms-flex-wrap: wrap; flex-wrap: wrap; -webkit-justify-content: center; -ms-flex-pack: center; justify-content: center; -webkit-align-content: stretch; -ms-flex-line-pack: stretch; align-content: stretch; -webkit-align-items: flex-start; -ms-flex-align: start; align-items: flex-start; } /* product */ .product { position: relative; display: inline-block; vertical-align: top; min-width: 16em; margin: 0 1em 2.5em; padding: 1.5em 1.5em 2em; background: #24252A; border-radius: 5px; } .product--selected { box-shadow: 0 0 0 2px #5C5EDC; } .flexbox .product { display: block; -webkit-flex: 0 0 16em; -ms-flex: 0 0 16em; flex: 0 0 16em; } /* product info */ .product__info > span { display: block; padding: 1em 0; } /* since we'll be using the product info inside of the comparison, we'll hide the extra info for the grid view */ .grid .extra { display: none; } .product__image { display: block; margin: 0 auto; max-width: 100%; -webkit-transform-origin: 50% 100%; transform-origin: 50% 100%; } .product:hover .product__image { -webkit-animation: swing 0.6s forwards; animation: swing 0.6s forwards; } /* https://daneden.github.io/animate.css/ */ @-webkit-keyframes swing { 25% { -webkit-transform: rotate3d(0, 0, 1, 6deg); transform: rotate3d(0, 0, 1, 6deg); } 50% { -webkit-transform: rotate3d(0, 0, 1, -4deg); transform: rotate3d(0, 0, 1, -4deg); } 75% { -webkit-transform: rotate3d(0, 0, 1, 2deg); transform: rotate3d(0, 0, 1, 2deg); } 100% { -webkit-transform: rotate3d(0, 0, 1, 0deg); transform: rotate3d(0, 0, 1, 0deg); } } @keyframes swing { 25% { -webkit-transform: rotate3d(0, 0, 1, 6deg); transform: rotate3d(0, 0, 1, 6deg); } 50% { -webkit-transform: rotate3d(0, 0, 1, -4deg); transform: rotate3d(0, 0, 1, -4deg); } 75% { -webkit-transform: rotate3d(0, 0, 1, 2deg); transform: rotate3d(0, 0, 1, 2deg); } 100% { -webkit-transform: rotate3d(0, 0, 1, 0deg); transform: rotate3d(0, 0, 1, 0deg); } } .product__title { font-size: 150%; margin: 1em 0 0; min-height: 3em; } .product__price { font-weight: bold; color: #797BED; } .action { display: inline-block; font-size: 1em; white-space: nowrap; padding: 0.85em 1.25em; cursor: pointer; border: none; background: transparent; text-align: center; } .action:focus { outline: none; } .action--button { background: #2C2D34; color: #fff; border-radius: 2px; -webkit-transition: background 0.2s; transition: background 0.2s; } .action--button:hover { background: #5C5EDC; } .action__text { font-family: 'Raleway', 'Helvetica Neue', Helvetica, Arial, sans-serif; font-weight: bold; letter-spacing: 1px; font-size: .813em; vertical-align: middle; display: inline-block; } .action__text--invisible { position: absolute; top: 100%; opacity: 0; pointer-events: none; } .action--button i + span { margin-left: 1em; } .flexbox .action--buy { -webkit-align-self: center; -ms-flex-item-align: center; align-self: center; margin-top: 1em; -webkit-flex: none; -ms-flex: none; flex: none; } .action--close { position: absolute; overflow: hidden; top: 0; right: 0; font-size: 1.5em; color: #ddd; pointer-events: none; opacity: 0; -webkit-transition: opacity 0.3s, background 0.2s; transition: opacity 0.3s, background 0.2s; } .view--compare + .compare .action--close { pointer-events: auto; opacity: 1; -webkit-transition-delay: 0.4s, 0s; transition-delay: 0.4s, 0s; } .action--close:hover, .action--close:focus { color: #797BED; } .action--compare { margin: 0 0 0 4px; opacity: 0; pointer-events: none; cursor: default; background-color: #34363D; color: #565B6C; -webkit-transition: opacity 0.3s; transition: opacity 0.3s; } .compare-basket--active .action--compare { opacity: 1; } .compare-basket--active .action--compare:nth-child(3), .compare-basket--active .action--compare:nth-child(4) { background-color: #494BC7; color: #fff; pointer-events: auto; cursor: pointer; } .action--remove { position: absolute; overflow: hidden; color: #ddd; top: 0px; right: 2px; padding: 0; font-size: 0.65em; } .action--compare-add { color: #ddd; position: absolute; top: 10px; right: 5px; } .action--compare-add:hover .action__text--invisible { opacity: 1; top: 35px; right: 10px; color: #ddd; font-size: 75%; letter-spacing: 0; background: #2F3035; border-radius: 2px; padding: 3px 5px; } .action--remove:hover, .action--compare-add:hover { color: #5C5EDC; } .action--compare-add .fa-check, .action--compare-add input[type=checkbox]:checked ~ .fa-plus { display: none; } .action--compare-add input[type=checkbox]:checked ~ .fa-check { display: block; color: #5C5EDC; } .check-hidden { position: absolute; opacity: 0; } .compare-basket { width: 100%; padding: 0.75em; text-align: right; position: fixed; top: 0; left: 0; background: #212227; z-index: 1000; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; -webkit-transform: translate3d(0,-100%,0); transform: translate3d(0,-100%,0); -webkit-transition: -webkit-transform 0.3s cubic-bezier(0.2,1,0.3,1); transition: transform 0.3s cubic-bezier(0.2,1,0.3,1); } .compare-basket--active { -webkit-transform: translate3d(0,0,0); transform: translate3d(0,0,0); } .flexbox .compare-basket { display: -webkit-flex; display: -ms-flex; display: flex; -webkit-justify-content: flex-end; -ms-flex-pack: end; justify-content: flex-end; } .product-icon { display: inline-block; vertical-align: middle; background: #42444F; width: 50px; height: 50px; padding: 5px; margin: 0 3px; border-radius: 2px; position: relative; } .product-icon::after { content: ''; position: absolute; width: 100%; height: 100%; top: 0; left: 0; border-radius: 4px; z-index: -1; box-shadow: -56px 0 #2C2D34; } .compare-basket--full .product-icon::after { display: none; } .flexbox .product-icon { display: block; } /* comparison overlay */ .compare { position: fixed; z-index: 100; width: 100%; height: 0; overflow: hidden; top: 0; left: 0; z-index: 1001; -webkit-transition: height 0s 0.4s; transition: height 0s 0.4s; } .flexbox .compare { display: -webkit-flex; display: -ms-flex; display: flex; } .view--compare + .compare { pointer-events: auto; height: 100%; -webkit-transition: none; transition: none; } .compare::before { content: ''; position: absolute; width: 100%; height: 100%; top: 0; left: 0; background: rgba(0,0,0,0.5); opacity: 0; -webkit-transition: opacity 0.4s; transition: opacity 0.4s; } .view--compare + .compare::before { opacity: 1; } .compare__item { height: 100%; width: 50%; background: #24252A; text-align: center; cursor: default; padding: 2em 0; -webkit-transition: -webkit-transform 0.4s ease-in-out; transition: transform 0.4s ease-in-out; } .no-flexbox .compare__item { display: inline-block; width: 50%; } .compare__item:nth-of-type(2) { background: #212227; } .compare__item .product__title { margin: 1em 0; min-height: 0; } .compare__item .product__price { color: #CECECE; } .compare__item span[class^="product__"] { display: block; padding: 0.85em 0; -webkit-transition: background-color 0.3s; transition: background-color 0.3s; } .compare__item span[class^="product__"].hover { background: #000; } /* three items */ .compare__item:first-of-type:nth-last-of-type(3), .compare__item:first-of-type:nth-last-of-type(3) ~ .compare__item { width: 33.3333%; } .flexbox .compare__item { -webkit-flex: auto; -ms-flex: auto; flex: auto; } .compare__item:nth-child(odd) { -webkit-transform: translate3d(0,-100vh,0); transform: translate3d(0,-100vh,0); } .compare__item:nth-child(even) { -webkit-transform: translate3d(0,100vh,0); transform: translate3d(0,100vh,0); } .view--compare + .compare .compare__item:nth-child(odd), .view--compare + .compare .compare__item:nth-child(even) { -webkit-transform: translate3d(0,0,0); transform: translate3d(0,0,0); } .compare__effect { width: 100%; height: 100%; opacity: 0; -webkit-transition: -webkit-transform 1s cubic-bezier(0.2, 1, 0.3, 1), opacity 1s cubic-bezier(0.2, 1, 0.3, 1); transition: transform 1s cubic-bezier(0.2, 1, 0.3, 1), opacity 1s cubic-bezier(0.2, 1, 0.3, 1); } .compare__item:nth-child(odd) .compare__effect { -webkit-transform: translate3d(0,-250px,0); transform: translate3d(0,-250px,0); } .compare__item:nth-child(even) .compare__effect { -webkit-transform: translate3d(0,250px,0); transform: translate3d(0,250px,0); } .view--compare + .compare .compare__item:nth-child(odd) .compare__effect, .view--compare + .compare .compare__item:nth-child(even) .compare__effect { opacity: 1; -webkit-transform: translate3d(0,0,0); transform: translate3d(0,0,0); -webkit-transition-delay: 0.3s; transition-delay: 0.3s; } @media screen and (min-width: 59.688em) { .flexbox .compare__effect { display: -webkit-flex; display: -ms-flex; display: flex; -webkit-flex-direction: column; -ms-flex-direction: column; flex-direction: column; -webkit-justify-content: space-between; -ms-flex-pack: justify; justify-content: space-between; } .flexbox .compare__item .product__image { -webkit-align-self: center; -ms-flex-align: center; align-self: center; -webkit-flex: none; -ms-flex: none; flex: none; } } @media screen and (max-width: 59.688em) { .grid { padding: 2em 0.5em; font-size: 65%; } .product { margin: 0 0.5em 1em; min-width: 13em; } .product__title { font-size: 115%; } .flexbox .product { -webkit-flex: 0 0 13em; -ms-flex: 0 0 13em; flex: 0 0 13em; } .flexbox .compare { -webkit-flex-direction: column; -ms-flex-direction: column; flex-direction: column; } .no-flexbox .compare__item, .compare__item, .compare__item:first-of-type:nth-last-of-type(3), .compare__item:first-of-type:nth-last-of-type(3) ~ .compare__item { width: 100%; } .compare__item { text-align: left; padding: 1.5em; font-size: 90%; } .compare__item .product__image { height: 40px; float: left; } .compare__item .product__title { margin: 0 40px 0 43px; font-size: 0.85em; display: block; } .compare__item .product__year { border-bottom: 2px solid #2E294E; } .compare__item .product__region { border-bottom: 2px solid #6D6FD2; } .compare__item .product__varietal { border-bottom: 2px solid #4B5267; } .compare__item .product__alcohol { border-bottom: 2px solid #3C3474; } .action--close { padding: 0.5em 0.75em; } .compare__item .action--buy { margin: 0; display: block; } .compare__item span[class^="product__"] { display: inline-block; padding: 0.25em; margin: 0 0 0.5em 0; font-size: 0.85em; } .compare__item:nth-child(odd) { -webkit-transform: translate3d(-100%,0,0); transform: translate3d(-100%,0,0); } .compare__item:nth-child(even) { -webkit-transform: translate3d(100%,0,0); transform: translate3d(100%,0,0); } .compare__item:nth-child(odd) .compare__effect { -webkit-transform: translate3d(-250px,0,0); transform: translate3d(-250px,0,0); } .compare__item:nth-child(even) .compare__effect { -webkit-transform: translate3d(250px,0,0); transform: translate3d(250px,0,0); } }
The JavaScript
/** * main.js * http://www.codrops.com * * Licensed under the MIT license. * http://www.opensource.org/licenses/mit-license.php * * Copyright 2015, Codrops * http://www.codrops.com */ (function() { var viewEl = document.querySelector('.view'), gridEl = viewEl.querySelector('.grid'), items = [].slice.call(gridEl.querySelectorAll('.product')), basket; // the compare basket function CompareBasket() { this.el = document.querySelector('.compare-basket'); this.compareCtrl = this.el.querySelector('.action--compare'); this.compareWrapper = document.querySelector('.compare'), this.closeCompareCtrl = this.compareWrapper.querySelector('.action--close') this.itemsAllowed = 3; this.totalItems = 0; this.items = []; // compares items in the compare basket: opens the compare products wrapper this.compareCtrl.addEventListener('click', this._compareItems.bind(this)); // close the compare products wrapper var self = this; this.closeCompareCtrl.addEventListener('click', function() { // toggle compare basket classie.add(self.el, 'compare-basket--active'); // animate.. classie.remove(viewEl, 'view--compare'); }); } CompareBasket.prototype.add = function(item) { // check limit if( this.isFull() ) { return false; } classie.add(item, 'product--selected'); // create item preview element var preview = this._createItemPreview(item); // prepend it to the basket this.el.insertBefore(preview, this.el.childNodes[0]); // insert item this.items.push(preview); this.totalItems++; if( this.isFull() ) { classie.add(this.el, 'compare-basket--full'); } classie.add(this.el, 'compare-basket--active'); }; CompareBasket.prototype._createItemPreview = function(item) { var self = this; var preview = document.createElement('div'); preview.className = 'product-icon'; preview.setAttribute('data-idx', items.indexOf(item)); var removeCtrl = document.createElement('button'); removeCtrl.className = 'action action--remove'; removeCtrl.innerHTML = '<i class="fa fa-remove"></i><span class="action__text action__text--invisible">Remove product</span>'; removeCtrl.addEventListener('click', function() { self.remove(item); }); var productImageEl = item.querySelector('img.product__image').cloneNode(true); preview.appendChild(productImageEl); preview.appendChild(removeCtrl); var productInfo = item.querySelector('.product__info').innerHTML; preview.setAttribute('data-info', productInfo); return preview; }; CompareBasket.prototype.remove = function(item) { classie.remove(this.el, 'compare-basket--full'); classie.remove(item, 'product--selected'); var preview = this.el.querySelector('[data-idx = "' + items.indexOf(item) + '"]'); this.el.removeChild(preview); this.totalItems--; var indexRemove = this.items.indexOf(preview); this.items.splice(indexRemove, 1); if( this.totalItems === 0 ) { classie.remove(this.el, 'compare-basket--active'); } // checkbox var checkbox = item.querySelector('.action--compare-add > input[type = "checkbox"]'); if( checkbox.checked ) { checkbox.checked = false; } }; CompareBasket.prototype._compareItems = function() { var self = this; // remove all previous items inside the compareWrapper element [].slice.call(this.compareWrapper.querySelectorAll('div.compare__item')).forEach(function(item) { self.compareWrapper.removeChild(item); }); for(var i = 0; i < this.totalItems; ++i) { var compareItemWrapper = document.createElement('div'); compareItemWrapper.className = 'compare__item'; var compareItemEffectEl = document.createElement('div'); compareItemEffectEl.className = 'compare__effect'; compareItemEffectEl.innerHTML = this.items[i].getAttribute('data-info'); compareItemWrapper.appendChild(compareItemEffectEl); this.compareWrapper.insertBefore(compareItemWrapper, this.compareWrapper.childNodes[0]); } setTimeout(function() { // toggle compare basket classie.remove(self.el, 'compare-basket--active'); // animate.. classie.add(viewEl, 'view--compare'); }, 25); }; CompareBasket.prototype.isFull = function() { return this.totalItems === this.itemsAllowed; }; function init() { // initialize an empty basket basket = new CompareBasket(); initEvents(); } function initEvents() { items.forEach(function(item) { var checkbox = item.querySelector('.action--compare-add > input[type = "checkbox"]'); checkbox.checked = false; // ctrl to add to the "compare basket" checkbox.addEventListener('click', function(ev) { if( ev.target.checked ) { if( basket.isFull() ) { ev.preventDefault(); return false; } basket.add(item); } else { basket.remove(item); } }); }); } init(); })();