Taking advantage of bigger screens shouldn’t be limited to the use of bigger images/elements. We can put those extra pixels to work by enhancing the user experience with additional features.
The standard UX for a product gallery is: user clicks on a product he likes, then navigates to the single product page. From here he’s supposed to select some options, and finally add the product to the cart. Nothing is broken in this process. However, a returning customer, who’s familiar with the products line, may find it handy to select and buy a product directly from the gallery!
By showing customers a “quick add to cart” panel while they interact with a product preview, we can reduce the number of steps that separate a user from purchasing an item, and, potentially, increase our conversion rate.
Icons from our Nucleo library.
Creating the structure
The main HTML structure is an unordered list. Each list item contains an unordered list (product image slider), a div.cd-customization
(with the Add To Cart button and the product customization options) and a div.cd-item-info
(with product title and price).
<ul class="cd-gallery">
<li>
<div class="cd-single-item">
<a href="#0">
<ul class="cd-slider-wrapper">
<li class="selected"><img src="img/thumb-1.jpg" alt="Preview image"></li>
<li><img src="img/thumb-2.jpg" alt="Preview image"></li>
<!-- other product images here -->
</ul>
</a>
<div class="cd-customization">
<div class="color" data-type="select">
<ul>
<li class="color-1 active">color-1</li>
<li class="color-2">color-2</li>
<!-- other product colors here -->
</ul>
</div>
<div class="size" data-type="select">
<ul>
<li class="small active">Small</li>
<li class="medium">Medium</li>
<!-- other product sizes here -->
</ul>
</div>
<button class="add-to-cart">
<em>Add to Cart</em>
<svg x="0px" y="0px" width="32px" height="32px" viewBox="0 0 32 32">
<path stroke-dasharray="19.79 19.79" stroke-dashoffset="19.79" fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="square" stroke-miterlimit="10" d="M9,17l3.9,3.9c0.1,0.1,0.2,0.1,0.3,0L23,11"/>
</svg>
</button>
</div> <!-- .cd-customization -->
<button class="cd-customization-trigger">Customize</button>
</div> <!-- .cd-single-item -->
<div class="cd-item-info">
<b><a href="#0">Product Name</a></b>
<em>$9.99</em>
</div> <!-- cd-item-info -->
</li>
<!-- other list items here -->
</ul> <!-- cd-gallery -->
Adding style
Let's start from the product slider. By default, the list items are in absolute position and translated to the right (outside their .cd-gallery
parent) so they are not visible. We then used 2 classes to properly style them: .selected
(added to the first list item – visible product image) and .move-left
( product image on the left - not visible).
.cd-slider-wrapper {
position: relative;
overflow: hidden;
}
.cd-slider-wrapper li {
position: absolute;
top: 0;
left: 0;
visibility: hidden;
/* by default, move the product image to the right*/
transform: translateX(100%);
transition: transform 0.3s 0s, visibility 0s 0.3s;
}
.cd-slider-wrapper li.selected {
/* this is the visible product image */
position: relative;
visibility: visible;
z-index: 1;
transform: translateX(0);
transition: transform 0.3s 0s, visibility 0s 0s;
}
.cd-slider-wrapper li.move-left {
/* move the product image to the left */
transform: translateX(-100%);
}
About the product customization options: the .cd-customization
element is shown when a user hovers over the product; it's in absolute position and placed at the bottom of its .cd-single-item
parent element.
To create the customization options (color and size), we used two different <ul>
elements, both wrapped in a div[data-type="select"]
(div.size and div.color). The <ul>
element is in absolute position and centered relative to its parent while the div[data-type="select"]
has a fixed height (34px) and an overflow: hidden. Each list item inside the unordered list has a height equal to the div[data-type="select"]
so that, by default, only the selected option is visible.
When a user clicks one of the two customization options, the overflow property of the div[data-type="select"]
is set to visible so that the entire <ul>
element is shown.
.cd-customization {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
visibility: hidden;
opacity: 0;
}
.no-touch .cd-single-item:hover .cd-customization {
/* product customization visible */
pointer-events: auto;
visibility: visible;
opacity: 1;
}
.cd-customization .color, .cd-customization .size {
height: 34px;
position: relative;
overflow: hidden;
}
.cd-customization .color ul, .cd-customization .size ul {
display: inline-block;
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%);
width: 100%;
}
.cd-customization .color.is-open, .cd-customization .size.is-open {
/* color/size list open - make ul element visible */
overflow: visible;
}
To make sure that the selected <li>
item is always visible, we had to rearrange the list items inside their <ul>
parent according to the selected option. To do that, we created the .selected-n
class (where n is the item selected). For example, the .selected-3
class is added to the div[data-type="select"]
when the third list item is selected:
.cd-customization .color.selected-3 ul li:first-of-type,
.cd-customization .size.selected-3 ul li:first-of-type {
/* third option selected in the ul.color/ul.size list */
transform: translateY(0);
}
.cd-customization .color.selected-3 ul li:nth-of-type(2),
.cd-customization .size.selected-3 ul li:nth-of-type(2) {
transform: translateY(100%);
}
.cd-customization .color.selected-3 ul li:nth-of-type(3),
.cd-customization .size.selected-3 ul li:nth-of-type(3) {
transform: translateY(-100%);
}
About the Add To Cart animation: the .add-to-cart
button is composed by an <em>
(button text message) and a svg (check icon). By default, the svg is not visible (it's moved to the right, outside the button).
When the product is added to the cart, the .is-added
class is added to the .add-to-cart
button: the <em>
(text) is hidden (moved to the left), while the svg is moved back inside the button, and the drawing animation starts:
.cd-customization .add-to-cart em {
/* this is the button text message */
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.cd-customization .add-to-cart svg {
/* this is the check icon */
position: absolute;
left: 50%;
top: 50%;
width: 100%;
/* move the icon on the right - outside the button */
transform: translateX(50%) translateY(-50%);
}
.cd-customization .add-to-cart.is-added em {
/* product added to the cart - hide text message on the left with no transition*/
color: transparent;
transform: translateX(-100%);
}
.cd-customization .add-to-cart.is-added svg {
/* product added to the cart - move the svg back inside the button */
transform: translateX(-50%) translateY(-50%);
}
About the svg drawing animation, we used the two svg attributes stroke-dasharray
and stroke-dashoffset
. The first one lets you specify dash length and gaps, while the second one lets you change where the dasharray starts. In our case, we set stroke-dasharray="19.79 19.79" and stroke-dashoffset="19.79" (where 19.76 is the path length):
<svg x="0px" y="0px" width="32px" height="32px" viewBox="0 0 32 32">
<path stroke-dasharray="19.79 19.79" stroke-dashoffset="19.79" fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="square" stroke-miterlimit="10" d="M9,17l3.9,3.9c0.1,0.1,0.2,0.1,0.3,0L23,11"/>
</svg>
and then animate the dashoffset value to 0 to create the drawing animation.
If you're interested in more details about animating svg icons, you can give a look at what we did in the article Animated SVG Icon.
Note: to animate the dashoffset value, we could have used css transitions (rather then using the animate() jQuery method), but unfortunately IE11 seems to have a bug with transitions on some svg properties.
Last note for touch devices: we used Modernizer to target touch devices and add a .cd-customization-trigger
(settings icon) to trigger the.cd-customization
element visibility.
Events handling
When a user interacts with one of the product customization options (e.g, the colors option), the .hover
class is added to the corresponding .cd-single-item
element. This class sets the .cd-customization element
to visible, so that it doesn't disappear if the cursor leaves the product (we assumed that the user is interested in the product since he has interacted with it, and doesn't want to lose the options if he accidentally leaves the product). For the same reason, the .hover
class is removed from all the other products (so that user can focus only on the product he is interacting with). The resetCustomization()
function has been defined to do that:
function resetCustomization(selectOptions) {
//add .hover class to the item user is interacting with
//close ul.color/ul.size if they were left open and user is not interacting with them anymore
//remove the .hover class from items if user is interacting with a different one
selectOptions.siblings('[data-type="select"]').removeClass('is-open').end().parents('.cd-single-item').addClass('hover').parent('li').siblings('li').find('.cd-single-item').removeClass('hover').end().find('[data-type="select"]').removeClass('is-open');
}
Besides, we used jQuery to implement the product slider (different color images) and the updateCart()
function (to update the number of products added to the cart).