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 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); } }
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
/** * 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(); })();
amazing
Nice tricks! Thanks. So awesome
The comparison bar doesn’t show up in Opera/Chrome. I’m guessing it has to do with this error that pops up in the console:
Uncaught SyntaxError: Failed to execute 'querySelector' on 'Element': '.action--compare-add > input[type = "checkbox"]' is not a valid selector.
In Firefox, it works fine and looks beautiful!
Works fine in my Chrome Browser
Works fine in Chrome 45 (Canary) but not in Chrome 43 (latest stable).
Same error as Sander mentions.
Looks fantastic in Chrome Canary though, nice job.
PRO!
Awesome as usual
Blueprints just got a wonderful makeover
Only issue I see here is that I can scroll while being in the comparison view. Some overflow: hidden would be great here.
Very good!!!!
Impressive !
Good work !
Really great demo, love some of the examples you put up on this site. Looking forward to implementing it in a new design I’m working on and seeing if it fits in.
You might want to stop background scrolling when the compare view comes up.
Mary Lou… I don’t know who you are. I don’t know what you want… But I will look for you, I will find you, and I will marry you. <3
I don’t know a lot of HTML/CSS/JAVASCRIPT so, how I can put more than three items for the product comparison?
* In main.js, replace
this.itemsAllowed = 3;
with
this.itemsAllowed = 4;
* In component.css, replace
.compare-basket-active .btn-compare:nth-child(3),
.compare-basket-active .btn-compare:nth-child(4) {
background-color: #494BC7;
color: #fff;
pointer-events: auto;
cursor: pointer;
}
with
.compare-basket-active .btn-compare:nth-child(3),
.compare-basket-active .btn-compare:nth-child(4),
.compare-basket-active .btn-compare:nth-child(5) {
background-color: red;
color: #fff;
pointer-events: auto;
cursor: pointer;
}
So amazingly awesome, great UI. Love the content too, as a former Sommelier it’s quite funny. Got a couple 2000 Branaire-Ducru in the cellar right now 🙂
good Morning! Anyone know how to do to compare two specific items without having to add 1-1?
Example: they have become the first click of the comparison to inves I go and pick 1-1
Hello, am newb, but how compare for example 10 items?
this.itemsAllowed = 3;
with
this.itemsAllowed = 10;
very very good.
How can I make the comparison bar inactive when clicking somewhere on the page?
Currently it blocks the menu when 1 product is added to the comparison.
Good Tutorial.
really amazing,
thanks for your effort 🙂
How to make compare item save in localstorage or session?
Great Job. Keep up the good work. Thanks rascojet for your suggestion on adding more items in the comparison grid.
How to add more than 5 Product Comparison
The plus and check buttons are not showing up! Neither is the remove item form compare basket! I downloaded the theme a few months ago and everything was right then! What went wrong?
Hi Shreyas, thanks a lot for the heads-up! We’ve recently switched to HTTPS and some resources still needed their link fixed (Font Awesome). It should all work now, please clean your cache and reload; let us know if it works for you now. Thanks, cheers, ML
@Mary Lou. Yes! It works now. Thanks:)
This is great! I would love to figure out a lightbox effect on the product images.
How can we add pagination to the sorted items
I’m having trouble adding multiple product comparisons on the same page. The first works, but all others do nothing.
Nevermind. I figured it out. 🙂