This JavaScript project helps you to create simple product image cards with like and dislike buttons. It loads another card if the user dislikes a card. It can be used to show products or portfolio items. Product cards allow the users to select their preferred interior design style. It includes a header with a title and description and three articles with images and likes/dislikes buttons. The like/dislike buttons suggest that the user’s selections will be recorded and used to inform the recommendations that are provided to them.
How to Create Simple Product Cards
Create the HTML structure for cards as follows:
<main class="main"> <header> <h1>What's your kind of interior design?</h1> <p>This will help us suit our suggestions to your taste</p> </header> <div id="cards"> <article class="card card_hidden" id="card-0"> <div class="card__image overflowFixForSafari"><img src="" alt="A nice furniture"/></div> <div class="card__choice"> <button data-action="dislike"><svg viewBox="0 0 512 512" width="100" title="heart-broken"><path d="M473.7 73.8l-2.4-2.5c-46-47-118-51.7-169.6-14.8L336 159.9l-96 64 48 128-144-144 96-64-28.6-86.5C159.7 19.6 87 24 40.7 71.4l-2.4 2.4C-10.4 123.6-12.5 202.9 31 256l212.1 218.6c7.1 7.3 18.6 7.3 25.7 0L481 255.9c43.5-53 41.4-132.3-7.3-182.1z" /></svg></button> <button data-action="like"><svg viewBox="0 0 512 512" width="100" title="heart"><path d="M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z" /></svg></button> </div> </article> <article class="card card_hidden" id="card-1"> <div class="card__image overflowFixForSafari"><img src="" alt="A nice furniture"/></div> <div class="card__choice"> <button data-action="dislike"><svg viewBox="0 0 512 512" width="100" title="heart-broken"><path d="M473.7 73.8l-2.4-2.5c-46-47-118-51.7-169.6-14.8L336 159.9l-96 64 48 128-144-144 96-64-28.6-86.5C159.7 19.6 87 24 40.7 71.4l-2.4 2.4C-10.4 123.6-12.5 202.9 31 256l212.1 218.6c7.1 7.3 18.6 7.3 25.7 0L481 255.9c43.5-53 41.4-132.3-7.3-182.1z" /></svg></button> <button data-action="like"><svg viewBox="0 0 512 512" width="100" title="heart"><path d="M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z" /></svg></button> </div> </article> <article class="card card_hidden" id="card-2"> <div class="card__image overflowFixForSafari"><img src="" alt="A nice furniture"/></div> <div class="card__choice"> <button data-action="dislike"><svg viewBox="0 0 512 512" width="100" title="heart-broken"><path d="M473.7 73.8l-2.4-2.5c-46-47-118-51.7-169.6-14.8L336 159.9l-96 64 48 128-144-144 96-64-28.6-86.5C159.7 19.6 87 24 40.7 71.4l-2.4 2.4C-10.4 123.6-12.5 202.9 31 256l212.1 218.6c7.1 7.3 18.6 7.3 25.7 0L481 255.9c43.5-53 41.4-132.3-7.3-182.1z" /></svg></button> <button data-action="like"><svg viewBox="0 0 512 512" width="100" title="heart"><path d="M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z" /></svg></button> </div> </article> <article class="card card_hidden" id="card-3"> <div class="card__image overflowFixForSafari"><img src="" alt="A nice furniture"/></div> <div class="card__choice"> <button data-action="dislike"><svg viewBox="0 0 512 512" width="100" title="heart-broken"><path d="M473.7 73.8l-2.4-2.5c-46-47-118-51.7-169.6-14.8L336 159.9l-96 64 48 128-144-144 96-64-28.6-86.5C159.7 19.6 87 24 40.7 71.4l-2.4 2.4C-10.4 123.6-12.5 202.9 31 256l212.1 218.6c7.1 7.3 18.6 7.3 25.7 0L481 255.9c43.5-53 41.4-132.3-7.3-182.1z" /></svg></button> <button data-action="like"><svg viewBox="0 0 512 512" width="100" title="heart"><path d="M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z" /></svg></button> </div> </article> <article class="card card_hidden" id="card-4"> <div class="card__image overflowFixForSafari"><img src="" alt="A nice furniture"/></div> <div class="card__choice"> <button data-action="dislike"><svg viewBox="0 0 512 512" width="100" title="heart-broken"><path d="M473.7 73.8l-2.4-2.5c-46-47-118-51.7-169.6-14.8L336 159.9l-96 64 48 128-144-144 96-64-28.6-86.5C159.7 19.6 87 24 40.7 71.4l-2.4 2.4C-10.4 123.6-12.5 202.9 31 256l212.1 218.6c7.1 7.3 18.6 7.3 25.7 0L481 255.9c43.5-53 41.4-132.3-7.3-182.1z" /></svg></button> <button data-action="like"><svg viewBox="0 0 512 512" width="100" title="heart"><path d="M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z" /></svg></button> </div> </article> <article class="card card_hidden" id="card-5"> <div class="card__image overflowFixForSafari"><img src="" alt="A nice furniture"/></div> <div class="card__choice"> <button data-action="dislike"><svg viewBox="0 0 512 512" width="100" title="heart-broken"><path d="M473.7 73.8l-2.4-2.5c-46-47-118-51.7-169.6-14.8L336 159.9l-96 64 48 128-144-144 96-64-28.6-86.5C159.7 19.6 87 24 40.7 71.4l-2.4 2.4C-10.4 123.6-12.5 202.9 31 256l212.1 218.6c7.1 7.3 18.6 7.3 25.7 0L481 255.9c43.5-53 41.4-132.3-7.3-182.1z" /></svg></button> <button data-action="like"><svg viewBox="0 0 512 512" width="100" title="heart"><path d="M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z" /></svg></button> </div> </article> <article class="card card_hidden" id="card-6"> <div class="card__image overflowFixForSafari"><img src="" alt="A nice furniture"/></div> <div class="card__choice"> <button data-action="dislike"><svg viewBox="0 0 512 512" width="100" title="heart-broken"><path d="M473.7 73.8l-2.4-2.5c-46-47-118-51.7-169.6-14.8L336 159.9l-96 64 48 128-144-144 96-64-28.6-86.5C159.7 19.6 87 24 40.7 71.4l-2.4 2.4C-10.4 123.6-12.5 202.9 31 256l212.1 218.6c7.1 7.3 18.6 7.3 25.7 0L481 255.9c43.5-53 41.4-132.3-7.3-182.1z" /></svg></button> <button data-action="like"><svg viewBox="0 0 512 512" width="100" title="heart"><path d="M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z" /></svg></button> </div> </article> <article class="card card_hidden" id="card-7"> <div class="card__image overflowFixForSafari"><img src="" alt="A nice furniture"/></div> <div class="card__choice"> <button data-action="dislike"><svg viewBox="0 0 512 512" width="100" title="heart-broken"><path d="M473.7 73.8l-2.4-2.5c-46-47-118-51.7-169.6-14.8L336 159.9l-96 64 48 128-144-144 96-64-28.6-86.5C159.7 19.6 87 24 40.7 71.4l-2.4 2.4C-10.4 123.6-12.5 202.9 31 256l212.1 218.6c7.1 7.3 18.6 7.3 25.7 0L481 255.9c43.5-53 41.4-132.3-7.3-182.1z" /></svg></button> <button data-action="like"><svg viewBox="0 0 512 512" width="100" title="heart"><path d="M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z" /></svg></button> </div> </article> </div> </main>
Style the cards using the following CSS styles:
@import url("https://fonts.googleapis.com/css2?family=Dancing+Script&family=Playfair+Display&family=Montserrat&display=swap"); * { box-sizing: border-box; } html, body { margin: 0; padding: 0; height: 100%; width: 100%; font-family: Montserrat, sans-serif; } @media screen and (max-width: 1200px) { html, body { font-size: 0.8em; } } h1 { font-size: 3em; margin-bottom: 0.25em; } .main { float: right; padding: 1.25em 2.5em; width: calc(100% - 18em); min-height: 100%; background-color: #d9ccc1; } #cards { max-width: 75em; } .card { display: inline-block; position: relative; overflow: hidden; will-change: transform; height: 15em; width: 15em; background-color: #c5b3a3; border-radius: 25px; margin: 2em 2em 0 0; transition: opacity 150ms ease-in-out, transform 150ms ease-in-out; } .card::after { content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: #f0f0f2; transition: opacity 500ms ease-in-out; opacity: 0; z-index: -1; } .card_favorited { transform-origin: 0.5em 0.5em; } .cd__main{ display: block !important; } .card_favorited::after { z-index: 1; opacity: 1; } .card_hidden { opacity: 0; visibility: hidden; transform: scale(0.5); } .card__image { width: 15em; height: 75%; border-radius: 25px 25px 0 0; } .card__image > img { position: relative; opacity: 0; transition: opacity 1000ms ease-in-out; width: 100%; height: 100%; } .card__image_loaded > img { opacity: 1; } .card__choice { display: flex; align-items: center; justify-content: center; height: 25%; } .card__choice button { font-size: 0.9em; background-color: #59524f; width: 3em; height: 3em; border: none; border-radius: calc(3em / 2); padding: 0.3em 0 0 0; margin: 0 1em; cursor: pointer; } .card__choice button svg { width: 1.3em; height: 1.3em; fill: #d9ccc1; stroke: #d9ccc1; } .card__choice button[data-action=dislike]:hover, .card__choice button[data-action=dislike]:focus { background-color: #b71c1c; } .card__choice button[data-action=like]:hover, .card__choice button[data-action=like]:focus { background-color: #8c7a70; } .overflowFixForSafari { -webkit-backface-visibility: hidden; -moz-backface-visibility: hidden; -webkit-transform: translate3d(0, 0, 0); -moz-transform: translate3d(0, 0, 0); }
Add the following JavaScript function (optional) to load random images from Unsplash:
const unsplashIds = [ "FV3GConVSss", "JaXs8Tk5Iww", "UV81E0oXXWQ", "MNz7IGrcEl0", "tleCJiDOri0", "JdcJn85xD2k", "nhWgZNV85LQ", "UXFJ-6Zj27M", "98WE9hWWjiQ", "3mlg5BRUifM", "_B9J6abAHPA", "VOS6akpHo1k", "f7h2nTvEknM", "RBoGC_OJvWs", "GliaHAJ3_5A" ]; const cards = document.querySelectorAll(".card"); const cardsContainer = document.querySelector("#cards"); const favoriteIcon = document.getElementById("favMenuItem"); function getCard(element) { if (element.classList.contains("card")) { return element; } else if (element !== document.documentElement) { return getCard(element.parentElement); } } function getButton(element) { if (element.tagName === "BUTTON") { return element; } else if (element !== document.documentElement) return getButton(element.parentElement); } function getImg(card) { if (!(card instanceof Element && card.classList.contains("card"))) return; return card.querySelector("img"); } function appear(card) { const id = Math.floor(Math.random() * unsplashIds.length); const cardImg = getImg(card); const src = `https://source.unsplash.com/${unsplashIds[id]}/240x180`; card.classList.remove("card_hidden"); cardImg.setAttribute("src", src); } function reset(card) { const cardImg = getImg(card); cardImg.setAttribute("src", ""); cardImg.parentElement.classList.remove("card__image_loaded"); card.classList.add("card_hidden"); card.classList.remove("card_favorited"); setTimeout(() => { appear(card); }, 400); } function like(card) { const animationDuration = 1000; const distance = getDistance(card, favoriteIcon); card.classList.add("card_favorited"); favoriteIcon.animate([{ transform: "translateZ(700px)", offset: 0.5 }], { duration: animationDuration, easing: "ease-in-out" }); const animation = card.animate( [ { transform: `translate(${distance.x}px, ${distance.y}px) scale(0.2) rotate(-40deg)` } ], { duration: animationDuration / 2, easing: "ease-in" } ); animation.onfinish = () => { reset(card); }; } function dislike(card) { const animation = card.animate( [ { transform: "scale(0.1) rotate(-130deg)", opacity: 0 } ], { duration: 400, easing: "ease-in-out" } ); animation.onfinish = () => { reset(card); }; } function buttonHandler(event) { const card = getCard(event.target); const button = getButton(event.target); if (!card || !button || !button.dataset) return; if (button.dataset.action === "like") like(card); if (button.dataset.action === "dislike") dislike(card); } function getDistance(elt1, elt2) { if (!(elt1 instanceof Element && elt2 instanceof Element)) throw Error("Illegal use of the function"); const elt1Bbox = elt1.getBoundingClientRect(); const elt2Bbox = elt2.getBoundingClientRect(); return { x: elt2Bbox.x - elt1Bbox.x, y: elt2Bbox.y - elt1Bbox.y }; } function init() { cardsContainer.addEventListener("click", buttonHandler); [...cards].forEach((card, index) => { const cardImg = getImg(card); cardImg.addEventListener("load", () => { cardImg.parentElement.classList.add("card__image_loaded"); }); setTimeout(() => { appear(card); }, 200 + index * 50); }); } window.addEventListener("DOMContentLoaded", init);
That’s all! hopefully, you have successfully created a simple product card. If you have any questions or suggestions, feel free to comment below.