Product Comparison Layout & Effect

A basic responsive product grid layout with comparison functionality and a slide-in effect.

BlueprintProductComparison

View demo Download source

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();

})();

View demo Download source

Previous:
Next:

Tagged with:

ML is a freelance web designer and developer with a passion for interaction design. She studied Cognitive Science and Computational Logic and has a weakness for the smell of freshly ground peppercorns.

View all contributions by

Website: http://www.codrops.com

Related Articles

Receive our bi-weekly Collective or blog updates right in your inbox.

Which newsletter would you like to receive?

CSS Reference

Learn about all important CSS properties from the basics with our extensive and easy-to-read CSS Reference.

It doesn't matter if you are a beginner or intermediate, start learning CSS now.