🎉🎉 E-commerce product page in HTML, CSS and JavaScript - Frontend Mentor Challenge
Tags: css, javascript, webdev, beginners
Published: 2024-04-17 13:26:00

In this post we will take an existing design from [Frontend Mentor](https://www.frontendmentor.io/), and convert the design into a `HTML`, `CSS`and `JavaScript` webpage.

![Image slide show](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o5n9hu5pk23kmrt4g8ub.jpg)

The webpage will have some cool features like:

- Image gallery
- Image slide show
- Adding product to cart
- Removing product from cart
- Cart counter
- Mobile responsiveness
- and more...

##Interested in a video tutorial guide? I got you sorted. The following is the video:

_Subscribe to my channel for more._

{% youtube 044RFNi1aHI %}

##Starter files

To get the designs and starter files, visit the following link which will take you to the front-end mentor challenge: [E-commerce product page](https://www.frontendmentor.io/challenges/ecommerce-product-page-UPsZ9MJp6)

After downloading the starter file, just unzip the zipped folder. Open the unzipped folder with your favorite code editor. I will be using vs-code. You should now have the following starter files.

![Folder Structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ck79swkm4n1man83g9qq.png)

- design folder - this folder have all the designs you need to complete the challenge
- images - these are the different images and icons you will need when working on the web page
- index.html - A html template with content to get started with.
- README - please read the README to get more details about the challenge and what is expected
- style-guide - a guide on fonts and colors to be used

##Head

Open the index.html file. At our head tag we will link the different css and javascript files that we will need. Remove the already existing style tag, we will use an external css file for styles called `main.css`. Add script tags for `main.js`, `slider.js`, and `cart.js`.

```html
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link
      rel="icon"
      type="image/png"
      sizes="32x32"
      href="./images/favicon-32x32.png"
    />

    <title>Frontend Mentor | E-commerce product page</title>
    <link rel="stylesheet" type="text/css" media="screen" href="main.css" />
    <script src="main.js" defer></script>
    <script src="slider.js" defer></script>
    <script src="cart.js" defer></script>
  </head>
```
Note how we have the defer `attributes` at our scripts. When you include a script with the defer attribute, the browser will download the script in the background while continuing to parse and render the HTML. Once the HTML parsing is complete, the deferred scripts will be executed in the order they appear in the document.

Make sure to also create these files at the root. Note how the folder structure now have additional files. That is `main.css`, `main.js`, `slider.js`, and `cart.js`.

![Folder structure 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z20arvhwaaifmlvm009q.png)

##Nav
At `index.html` file we will start by working on our navbar. Below will be our html markup for the navbar. We will make sure to place our nav inside the div `.container` which will set a maximum width for all our content.

```html
<body>
    <div class="container">
      <header>
        <nav class="navbar">
          <section class="nav-first">
            <svg
              class="menu-icon"
              width="16"
              height="15"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path
                d="M16 12v3H0v-3h16Zm0-6v3H0V6h16Zm0-6v3H0V0h16Z"
                fill="#69707D"
                fill-rule="evenodd"
              />
            </svg>
            <div class="logo">
              <img src="images/logo.svg" alt="sneakers logo" />
            </div>
            <div class="backdrop"></div>
            <div class="nav-links">
              <svg
                class="close-icon"
                width="14"
                height="15"
                xmlns="http://www.w3.org/2000/svg"
              >
                <path
                  d="m11.596.782 2.122 2.122L9.12 7.499l4.597 4.597-2.122 2.122L7 9.62l-4.595 4.597-2.122-2.122L4.878 7.5.282 2.904 2.404.782l4.595 4.596L11.596.782Z"
                  fill="#69707D"
                  fill-rule="evenodd"
                />
              </svg>
              <a href="#">Collections</a>
              <a href="#">Men</a>
              <a href="#">Women</a>
              <a href="#">About</a>
              <a href="#">Contact</a>
            </div>
          </section>
          <section class="nav-second">
            <div class="cart">
              <img
                class="cart-icon"
                src="images/icon-cart.svg"
                alt="cart icon"
              />
              <div class="cart-container">
                <div class="cart-title">Cart</div>
                <div class="cart-items empty">
                  <p class="cart-empty">Your cart is empty</p>
                </div>
                <button class="checkout empty">Checkout</button>
              </div>
              <div class="cart-count">
                <span class="qty">0</span>
              </div>
            </div>
            <div class="avatar">
              <img src="images/image-avatar.png" alt="avatar" />
            </div>
          </section>
        </nav>
      </header>
    </div>
  </body>
```
##Initial CSS
At `main.css` we will start by importing `Kumbh Sans` font from google fonts. We then remove the default `margin` and `padding` from all the elements and set `box-sizing` to `border-box`. This will tell css to include the padding and border of the elements to their height and width. This way we will not have unexpected sizes of our elements.

We also target `:root` to setup css variables for our colors from the style guide file. The following will be our initial css.

```css
@import url("https://fonts.googleapis.com/css2?family=Kumbh+Sans:wght@400;700&display=swap");

*,
*::before,
*::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

:root {
  --orange: hsl(26, 100%, 55%);
  --pale-orange: hsl(25, 100%, 94%);
  --very-dark-blue: hsl(220, 13%, 13%);
  --dark-grayish-blue: hsl(219, 9%, 45%);
  --grayish-blue: hsl(220, 14%, 75%);
  --light-grayish-blue: hsl(223, 64%, 98%);
  --white: hsl(0, 0%, 100%);
  --black: hsl(0, 0%, 0%);
  --black-with-opacity: hsla(0, 0%, 0%, 0.75);
}

html {
  font-family: "Kumbh Sans", sans-serif;
}

a {
  text-decoration: none;
  color: var(--dark-grayish-blue);
}

body {
  min-height: 100vh;
  min-width: 100vw;
  position: relative;
}

.container {
  max-width: 1120px;
  min-height: 100vh;
  padding: 0 5px;
  margin: auto;
}
```
##Nav CSS
To layout our navbar we will just use css flex box. Notice how we had divided the navbar into two sections. `.nav-first` and `.nav-second`. On the first section we style the logo and links while on the second section we will style our cart and profile. Since the cart styles are more detailed, we will have a separate section for it below.

```css
/* Navbar */
.navbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-top: 26px;
  border-bottom: 1px solid var(--grayish-blue);
  margin-bottom: 85px;
  position: relative;
}

.nav-first {
  display: flex;
  align-items: center;
  gap: 50px;
  padding-bottom: 30px;
}

.nav-first .menu-icon {
  display: none;
}

.nav-first .backdrop {
  display: none;
}

.nav-links .close-icon {
  display: none;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 30px;
}

.nav-links a {
  position: relative;
}

.nav-links a:hover {
  color: var(--black);
}

.nav-links a:hover::after {
  content: "";
  position: absolute;
  background-color: var(--orange);
  width: 100%;
  height: 3px;
  left: 0;
  bottom: -47px;
}

.nav-second {
  display: flex;
  align-items: center;
  gap: 45px;
  padding-bottom: 30px;
}

.logo img {
  height: 22px;
}

.avatar img {
  height: 50px;
  width: 50px;
}
```
##Cart CSS
`.cart-container` will hold our cart items. `.empty` is a dynamic class which we will add or remove it depending on whether we have items in cart or not by making use of javascript. By default the cart will be empty. Note how `.cart-container` has a display of none. We will add an active class with javascript whenever we click the cart icon in order to open the cart. If you check the html that we currently have, we don't have a `.cart-item`. We will create the cart-item at our javascript file and make it a child of `.cart-items`.

```css
/* Cart */
.cart {
  position: relative;
}

.cart-icon {
  cursor: pointer;
}

.cart-container {
  right: -95px;
  top: 50px;
  z-index: 9;
  position: absolute;
  width: 360px;
  min-height: 260px;
  background: white;
  box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;
  display: none;
}

.cart-container.active {
  display: flex;
  flex-direction: column;
}

.cart-title {
  padding: 25px 20px;
  font-weight: 700;
  border-bottom: 1px solid var(--grayish-blue);
}

.cart .cart-items {
  padding: 25px 20px;
  display: flex;
  flex-direction: column;
  gap: 25px;
}

.cart .cart-items.empty {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 185px;
  font-weight: 700;
}

.cart .cart-items.empty .cart-empty {
  color: var(--grayish-blue);
  display: inline-block;
}

.cart .cart-items .cart-empty {
  display: none;
}

.cart-item {
  display: flex;
  align-items: center;
  gap: 20px;
}

.cart-item img {
  height: 50px;
  border-radius: 5px;
}

.cart-item {
  color: var(--dark-grayish-blue);
}

.cart-item .total-price {
  color: var(--black);
  font-weight: 700;
}

.checkout.empty {
  display: none;
}

.checkout {
  height: 56px;
  margin: 27px 23px;
  border: none;
  color: var(--white);
  background-color: var(--orange);
  border-radius: 10px;
  font-weight: 700;
}

.checkout:hover {
  cursor: pointer;
}

.cart-count {
  cursor: pointer;
  position: absolute;
  top: -8px;
  right: -10px;
  background-color: var(--orange);
  color: var(--white);
  min-width: 25px;
  min-height: 17px;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 700;
}

.delete-item {
  border: none;
  background: none;
  cursor: pointer;
}
```
##Side Nav
To style the side navbar, add the following at the bottom of our css file. We have created a breakpoint at which the side nav will become active.

```css
/* Mobile */

@media (max-width: 755px) {
  .navbar {
    margin-bottom: 0;
    border-bottom: none;
  }

  .nav-first,
  .nav-second {
    gap: 30px;
    padding-bottom: 10px;
  }

  .nav-first .menu-icon {
    cursor: pointer;
    display: inline-block;
  }

  .nav-links {
    display: none;
  }

  .nav-links.active {
    display: flex;
    flex-direction: column;
    position: absolute;
    top: 0;
    left: -5px;
    max-width: 220px;
    width: 100%;
    height: 100vh;
    background: var(--white);
    align-items: start;
    z-index: 15;
    padding: 25px 30px;
  }

  .nav-first .backdrop.active {
    background: var(--black-with-opacity);
    width: 100vw;
    height: 100vh;
    display: block;
    position: absolute;
    top: 0;
    left: -5px;
    z-index: 11;
  }

  .nav-links.active .close-icon {
    display: inline-block;
    margin-bottom: 30px;
    cursor: pointer;
  }

  .nav-links a {
    font-weight: 700;
    color: black;
  }

  .nav-links.active a:hover::after {
    bottom: -5px;
  }
}
```
To open and close the navbar, let's add some javascript. At `main.js`, add the following code:
```javascript
const menuIcon = document.querySelector(".menu-icon");
const backdrop = document.querySelector(".backdrop");
const navLinks = document.querySelector(".nav-links");
const closeIcon = document.querySelector(".close-icon");

menuIcon.addEventListener("click", () => {
  backdrop.classList.add("active");
  navLinks.classList.add("active");
});

closeIcon.addEventListener("click", () => {
  backdrop.classList.remove("active");
  navLinks.classList.remove("active");
});

backdrop.addEventListener("click", () => {
  backdrop.classList.remove("active");
  navLinks.classList.remove("active");
});

```
##Image Gallery, Lightbox and Product Details
At our `index.html` just right after our header, we will add a main section with the html markup for our `gallery`, `lightbox` and `product description`. 

```html
<section class="main">
        <div class="default gallery">
          <div class="main-img">
            <img
              class="active"
              src="images/image-product-1.jpg"
              alt="product-img"
            />
            <img src="images/image-product-2.jpg" alt="product-img" />
            <img src="images/image-product-3.jpg" alt="product-img" />
            <img src="images/image-product-4.jpg" alt="product-img" />
          </div>
          <div class="thumb-list">
            <div class="active">
              <img
                src="images/image-product-1-thumbnail.jpg"
                alt="product-img"
              />
            </div>
            <div>
              <img
                src="images/image-product-2-thumbnail.jpg"
                alt="product-img"
              />
            </div>
            <div>
              <img
                src="images/image-product-3-thumbnail.jpg"
                alt="product-img"
              />
            </div>
            <div>
              <img
                src="images/image-product-4-thumbnail.jpg"
                alt="product-img"
              />
            </div>
          </div>
        </div>

        <div class="lightbox">
          <div class="gallery">
            <div class="main-img">
              <!-- icons -->
              <span class="icon-close">
                <svg width="14" height="15" xmlns="http://www.w3.org/2000/svg">
                  <path
                    d="m11.596.782 2.122 2.122L9.12 7.499l4.597 4.597-2.122 2.122L7 9.62l-4.595 4.597-2.122-2.122L4.878 7.5.282 2.904 2.404.782l4.595 4.596L11.596.782Z"
                    fill="#69707D"
                    fill-rule="evenodd"
                  />
                </svg>
              </span>
              <span class="icon-prev">
                <svg width="12" height="18" xmlns="http://www.w3.org/2000/svg">
                  <path
                    d="M11 1 3 9l8 8"
                    stroke="#1D2026"
                    stroke-width="3"
                    fill="none"
                    fill-rule="evenodd"
                  />
                </svg>
              </span>
              <span class="icon-next">
                <svg width="13" height="18" xmlns="http://www.w3.org/2000/svg">
                  <path
                    d="m2 1 8 8-8 8"
                    stroke="#1D2026"
                    stroke-width="3"
                    fill="none"
                    fill-rule="evenodd"
                  />
                </svg>
              </span>

              <!-- main images -->
              <img
                class="active"
                src="images/image-product-1.jpg"
                alt="product-img"
              />
              <img src="images/image-product-2.jpg" alt="product-img" />
              <img src="images/image-product-3.jpg" alt="product-img" />
              <img src="images/image-product-4.jpg" alt="product-img" />
            </div>
            <div class="thumb-list">
              <div class="active">
                <img
                  src="images/image-product-1-thumbnail.jpg"
                  alt="product-img"
                />
              </div>
              <div>
                <img
                  src="images/image-product-2-thumbnail.jpg"
                  alt="product-img"
                />
              </div>
              <div>
                <img
                  src="images/image-product-3-thumbnail.jpg"
                  alt="product-img"
                />
              </div>
              <div>
                <img
                  src="images/image-product-4-thumbnail.jpg"
                  alt="product-img"
                />
              </div>
            </div>
          </div>
        </div>

        <div class="content">
          <h3>SNEAKER COMPANY</h3>
          <h2 class="product-name">Fall Limited Edition Sneakers</h2>
          <p class="product-desc">
            These low-profile sneakers are your perfect casual wear companion.
            Featuring a durable rubber outer sole, they’ll withstand everything
            the weather can offer.
          </p>
          <div class="price-info">
            <div class="price">
              <span class="current-price">$125.00</span>
              <span class="discount">50%</span>
            </div>
            <div class="prev-price">$250.00</div>
          </div>
          <div class="add-to-cart-container">
            <div class="counter">
              <button class="minus">
                <svg
                  width="12"
                  height="4"
                  xmlns="http://www.w3.org/2000/svg"
                  xmlns:xlink="http://www.w3.org/1999/xlink"
                >
                  <defs>
                    <path
                      d="M11.357 3.332A.641.641 0 0 0 12 2.69V.643A.641.641 0 0 0 11.357 0H.643A.641.641 0 0 0 0 .643v2.046c0 .357.287.643.643.643h10.714Z"
                      id="a"
                    />
                  </defs>
                  <use fill="#FF7E1B" fill-rule="nonzero" xlink:href="#a" />
                </svg>
              </button>
              <span class="count">0</span>
              <button class="plus">
                <svg
                  width="12"
                  height="12"
                  xmlns="http://www.w3.org/2000/svg"
                  xmlns:xlink="http://www.w3.org/1999/xlink"
                >
                  <defs>
                    <path
                      d="M12 7.023V4.977a.641.641 0 0 0-.643-.643h-3.69V.643A.641.641 0 0 0 7.022 0H4.977a.641.641 0 0 0-.643.643v3.69H.643A.641.641 0 0 0 0 4.978v2.046c0 .356.287.643.643.643h3.69v3.691c0 .356.288.643.644.643h2.046a.641.641 0 0 0 .643-.643v-3.69h3.691A.641.641 0 0 0 12 7.022Z"
                      id="b"
                    />
                  </defs>
                  <use fill="#FF7E1B" fill-rule="nonzero" xlink:href="#b" />
                </svg>
              </button>
            </div>
            <button class="add-to-cart">
              <span>
                <svg width="22" height="20" xmlns="http://www.w3.org/2000/svg">
                  <path
                    d="M20.925 3.641H3.863L3.61.816A.896.896 0 0 0 2.717 0H.897a.896.896 0 1 0 0 1.792h1l1.031 11.483c.073.828.52 1.726 1.291 2.336C2.83 17.385 4.099 20 6.359 20c1.875 0 3.197-1.87 2.554-3.642h4.905c-.642 1.77.677 3.642 2.555 3.642a2.72 2.72 0 0 0 2.717-2.717 2.72 2.72 0 0 0-2.717-2.717H6.365c-.681 0-1.274-.41-1.53-1.009l14.321-.842a.896.896 0 0 0 .817-.677l1.821-7.283a.897.897 0 0 0-.87-1.114ZM6.358 18.208a.926.926 0 0 1 0-1.85.926.926 0 0 1 0 1.85Zm10.015 0a.926.926 0 0 1 0-1.85.926.926 0 0 1 0 1.85Zm2.021-7.243-13.8.81-.57-6.341h15.753l-1.383 5.53Z"
                    fill="#69707D"
                    fill-rule="nonzero"
                  />
                </svg>
              </span>
              <span>Add to cart</span>
            </button>
          </div>
        </div>
      </section>
```

Before the mobile responsiveness, we will style the gallery and the product details. By default the lightbox(image slideshow) will be hidden, and we will add the active to it upon clicking the `.main-img`.

```css
/* Main */
.main {
  display: flex;
  gap: 125px;
  min-height: 570px;
  align-items: center;
  padding: 0 50px;
}

/* Image gallery */
.gallery {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 30px;
}

.gallery .main-img img {
  display: none;
}

.gallery .main-img img.active {
  display: inline-block;
  max-width: 445px;
  max-height: 445px;
  width: 100%;
  height: 100%;
  border-radius: 20px;
  cursor: pointer;
}

.gallery .thumb-list {
  display: flex;
  justify-content: space-between;
  max-width: 445px;
  width: 100%;
}

.gallery .thumb-list div {
  max-width: 90px;
  max-height: 90px;
  margin: 0 2px;
}

.gallery .thumb-list img {
  width: 100%;
  height: 100%;
  border-radius: 10px;
  cursor: pointer;
}

.gallery .thumb-list img:hover {
  opacity: 50%;
}

.gallery .thumb-list .active img {
  opacity: 30%;
}

.gallery .thumb-list .active {
  border: 2px solid var(--orange);
  border-radius: 13px;
  margin: 0;
}

/* lightbox */
.lightbox {
  display: none;
  position: absolute;
  top: 0;
  left: 0;
  height: 100vh;
  width: 100vw;
  z-index: 10;
  background: var(--black-with-opacity);
  align-items: center;
  justify-content: center;
}

.lightbox.active {
  display: flex;
}

.lightbox.active .gallery {
  max-width: 445px;
}

.lightbox .main-img {
  position: relative;
}

.lightbox .icon-prev,
.lightbox .icon-next {
  position: absolute;
  height: 60px;
  width: 60px;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: var(--white);
  border-radius: 50%;
}

.icon-prev:hover,
.icon-next:hover {
  cursor: pointer;
}

/* .icon-prev svg path {
  fill: var(--orange);
} */

.icon-prev {
  top: 50%;
  transform: translate(-50%, -50%);
}

.icon-next {
  top: 50%;
  right: 0;
  transform: translate(50%, -50%);
}

.icon-close svg path {
  fill: var(--white);
}

.icon-close svg path:hover {
  cursor: pointer;
  fill: var(--orange);
}

.icon-close {
  position: absolute;
  right: 0;
  top: -40px;
}

/* Content */

.content {
  flex: 1;
}
.content h3 {
  font-size: 16px;
  color: var(--orange);
}

.content h2 {
  font-size: 37px;
  margin: 20px 0 40px 0;
}

.content p {
  font-size: 16px;
  color: var(--dark-grayish-blue);
  margin-bottom: 30px;
}

.price {
  display: flex;
  align-items: center;
  gap: 15px;
}

.current-price {
  font-weight: 700;
  font-size: 25px;
}

.discount {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 6px;
  border-radius: 10%;
  height: 25px;
  background-color: var(--pale-orange);
  font-weight: 700;
  color: var(--orange);
}

.prev-price {
  margin: 10px 0 35px 0;
  font-size: 18px;
  color: var(--grayish-blue);
  font-weight: 700;
  text-decoration: line-through;
}

.add-to-cart-container {
  display: flex;
  align-items: center;
  gap: 15px;
}

.counter {
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-radius: 15px;
  width: 150px;
  height: 55px;
  background: var(--light-grayish-blue);
}

.counter button {
  width: 50px;
  height: 100%;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
}

.counter .count {
  font-weight: 700;
}

.add-to-cart {
  color: var(--white);
  background-color: var(--orange);
  border: 0px;
  height: 55px;
  width: 100%;
  border-radius: 10px;
  font-weight: 700;
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 15px;
  cursor: pointer;
  padding: 0 5px;
}

.add-to-cart svg path {
  fill: var(--white);
}
```
##Gallery and Slider Logic
Now let's add logic for our gallery and lightbox. At `slider.js` add the following code. In the following code we have selected all the `mainImages` and all the `thumbnails`, both for gallery and lightbox. Their logic will be similar apart from the slider. The goal is to show a certain image upon clicking it's thumbnail.

```javascript
const mainImages = document.querySelectorAll(".default .main-img img");
const thumbnails = document.querySelectorAll(".default .thumb-list div");
const lightboxMainImages = document.querySelectorAll(".lightbox .main-img img");
const lightboxThumbnails = document.querySelectorAll(
  ".lightbox .thumb-list div"
);
const lightbox = document.querySelector(".lightbox");
const iconClose = document.querySelector(".icon-close");
const iconPrev = document.querySelector(".icon-prev");
const iconNext = document.querySelector(".icon-next");

let currentImageIndex = 0;

const changeImage = (index, mainImages, thumbnails) => {
  mainImages.forEach((img) => {
    img.classList.remove("active");
  });
  thumbnails.forEach((thumb) => {
    thumb.classList.remove("active");
  });

  mainImages[index].classList.add("active");
  thumbnails[index].classList.add("active");
  currentImageIndex = index;
};

thumbnails.forEach((thumb, index) => {
  thumb.addEventListener("click", () => {
    changeImage(index, mainImages, thumbnails);
  });
});

lightboxThumbnails.forEach((thumb, index) => {
  thumb.addEventListener("click", () => {
    changeImage(index, lightboxMainImages, lightboxThumbnails);
  });
});

mainImages.forEach((img, index) => {
  img.addEventListener("click", () => {
    lightbox.classList.add("active");
    changeImage(index, lightboxMainImages, lightboxThumbnails);
  });
});

iconPrev.addEventListener("click", () => {
  if (currentImageIndex <= 0) {
    changeImage(mainImages.length - 1, lightboxMainImages, lightboxThumbnails);
  } else {
    changeImage(currentImageIndex - 1, lightboxMainImages, lightboxThumbnails);
  }
});

iconNext.addEventListener("click", () => {
  if (currentImageIndex >= mainImages.length - 1) {
    changeImage(0, lightboxMainImages, lightboxThumbnails);
  } else {
    changeImage(currentImageIndex + 1, lightboxMainImages, lightboxThumbnails);
  }
});

iconClose.addEventListener("click", () => {
  lightbox.classList.remove("active");
});
```
Let's break the above code down. We select the thumbnails and loop over each of them. This allows us to add click event listener to each of it, pass a callback function and then call `changeImage` function.

```javascript
thumbnails.forEach((thumb, index) => {
  thumb.addEventListener("click", () => {
    changeImage(index, mainImages, thumbnails);
  });
});
```

The changeImage function uses the thumbnail index to change the active image. First we remove the `.active` class from all mainImages and thumbnails and then we use the passed index to add the `.active` class to only specific mainImage and it's thumbnail.
This logic will be similar for our lighbox when we change images by clicking on thumbnails.

```javascript
const changeImage = (index, mainImages, thumbnails) => {
  mainImages.forEach((img) => {
    img.classList.remove("active");
  });
  thumbnails.forEach((thumb) => {
    thumb.classList.remove("active");
  });

  mainImages[index].classList.add("active");
  thumbnails[index].classList.add("active");
  currentImageIndex = index;
};
```
##Open Lightbox
To open the lightbox, we looped over the mainImages and added the click event to each of them. Upon clicking the main Image, we add an `active` class to the lightbox. This will open the lightbox. Now to make sure that the clicked image is the one that show on the lightbox, we also call our `changeImage` function and pass the index of the clicked image. This will update the lightbox to the current image.

```javascript
mainImages.forEach((img, index) => {
  img.addEventListener("click", () => {
    lightbox.classList.add("active");
    changeImage(index, lightboxMainImages, lightboxThumbnails);
  });
});
```
##Next and Prev Images
Here we reused our `changeImage` function. The main thing to note is the correct calculation of the index. To simply explain it, for `next` we simply add `1` to the index. But if we get to the end, we move the count of the index back to `0`. And for previous we decrease the index by `1` and if we get to the first image, we move the count of the index to the last image. We make use of the `.length` property to know the last image.

```javascript
iconPrev.addEventListener("click", () => {
  if (currentImageIndex <= 0) {
    changeImage(mainImages.length - 1, lightboxMainImages, lightboxThumbnails);
  } else {
    changeImage(currentImageIndex - 1, lightboxMainImages, lightboxThumbnails);
  }
});

iconNext.addEventListener("click", () => {
  if (currentImageIndex >= mainImages.length - 1) {
    changeImage(0, lightboxMainImages, lightboxThumbnails);
  } else {
    changeImage(currentImageIndex + 1, lightboxMainImages, lightboxThumbnails);
  }
});
```
##Cart Logic
Now add the following code at `cart.js`. At `addToCartBtn` we will add a `click` event listener. upon clicking the button we will extract the product details from the DOM including the name, price and image. The product quantity will already be available at our `count` variable. We will then call `addItemToCart` with the product details as parameters.

At `addItemToCart` we will create a cart item, notice how the information is dynamic depending on the details we got at `addItemToCart` as arguments, e.g the name, price etc

We also create a custom quantity attribute which will be helpful when calculating the total cart quantity at `updateTotalCartQty` function. 

`cartItem.dataset.quantity = count;`

After creating the `carItem`, we then append it as a child of `cartItems`. This is how the item will simply be added to cart.

```javascript
const countEl = document.querySelector(".count");
const minus = document.querySelector(".minus");
const plus = document.querySelector(".plus");
const cartIcon = document.querySelector(".cart-icon");
const cartContainer = document.querySelector(".cart-container");
const addToCartBtn = document.querySelector(".add-to-cart");
const cartItems = document.querySelector(".cart-items");
const checkout = document.querySelector(".checkout");
const cartCount = document.querySelector(".cart-count");

let count = 0;
let totalCartQty = 0;

const updateCount = (newCount) => {
  count = newCount;
  countEl.textContent = count;
};

minus.addEventListener("click", () => {
  if (count > 0) {
    updateCount(count - 1);
  }
});

plus.addEventListener("click", () => {
  updateCount(count + 1);
});

cartCount.addEventListener("click", () => {
  cartContainer.classList.toggle("active");
});

const updateTotalCartQty = () => {
  const cartItemsList = document.querySelectorAll(".cart-item");
  totalCartQty = 0;
  cartItemsList.forEach((item) => {
    totalCartQty += parseInt(item.dataset.quantity);
  });

  cartCount.innerHTML = `<span class="qty">${totalCartQty}</span>`;
};

// add item to cart

const addItemToCart = (name, price, imageSrc) => {
  const totalPrice = count * price;

  const cartItem = document.createElement("div");
  cartItem.classList.add("cart-item");
  cartItem.dataset.quantity = count;
  cartItem.innerHTML = `
      <img src="${imageSrc}" alt="${name}" />
      <div class="item-details">
        <div>${name}</div>
        <div>
            <p>
                $${price.toFixed(2)} x ${count} 
                <span class='total-price'>$${totalPrice.toFixed(2)}</span>
            </p>
        </div>
        </div>
        <button class="delete-item"> 
            <svg width="14" height="16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path d="M0 2.625V1.75C0 1.334.334 1 .75 1h3.5l.294-.584A.741.741 0 0 1 5.213 0h3.571a.75.75 0 0 1 .672.416L9.75 1h3.5c.416 0 .75.334.75.75v.875a.376.376 0 0 1-.375.375H.375A.376.376 0 0 1 0 2.625Zm13 1.75V14.5a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 1 14.5V4.375C1 4.169 1.169 4 1.375 4h11.25c.206 0 .375.169.375.375ZM4.5 6.5c0-.275-.225-.5-.5-.5s-.5.225-.5.5v7c0 .275.225.5.5.5s.5-.225.5-.5v-7Zm3 0c0-.275-.225-.5-.5-.5s-.5.225-.5.5v7c0 .275.225.5.5.5s.5-.225.5-.5v-7Zm3 0c0-.275-.225-.5-.5-.5s-.5.225-.5.5v7c0 .275.225.5.5.5s.5-.225.5-.5v-7Z" id="a"/></defs><use fill="#C3CAD9" fill-rule="nonzero" xlink:href="#a"/></svg>
        </button>
    `;

  cartItems.appendChild(cartItem);

  updateTotalCartQty();

  if (cartItems.classList.contains("empty")) {
    cartItems.classList.remove("empty");
    checkout.classList.remove("empty");
  }

  // attach an event listener to the delete button

  const deleteButton = cartItem.querySelector(".delete-item");
  deleteButton.addEventListener("click", (event) => {
    const cartItem = event.target.closest(".cart-item");
    removeItemFromCart(cartItem);
  });
};

addToCartBtn.addEventListener("click", () => {
  if (count === 0) return;
  const productName = document.querySelector(".main .product-name").textContent;
  const productPriceEl = document.querySelector(".main .current-price");
  const productPrice = parseFloat(productPriceEl.textContent.replace("$", ""));
  const productImg = document
    .querySelector(".default.gallery .main-img img")
    .getAttribute("src");

  addItemToCart(productName, productPrice, productImg);
  cartContainer.classList.add("active");

  updateCount(0);
});

// remove item from cart

const removeItemFromCart = (cartItem) => {
  cartItem.remove();
  updateTotalCartQty();

  if (cartItems.children.length === 1) {
    cartItems.classList.add("empty");
    checkout.classList.add("empty");
  }
};

```

##Mobile responsiveness
Let's make the rest of the page responsive. At our breakpoint `@media (max-width: 755px)` after `.nav-links.active`, add the following.

```css

/* main */
  .main {
    flex-direction: column;
    gap: 20px;
    padding: 0;
  }

  .main .default {
    display: none;
  }

  .lightbox {
    display: flex;
    position: relative;
    height: auto;
    width: auto;
    background: none;
  }

  .main .thumb-list {
    display: none;
  }

  .main .icon-prev {
    left: 50px;
    height: 45px;
    width: 45px;
  }

  .main .icon-next {
    right: 50px;
    height: 45px;
    width: 45px;
  }

  .gallery .main-img img.active {
    max-width: none;
    max-height: none;
    width: 100vw;
    height: auto;
    border-radius: 0;
  }

  .content {
    padding: 0 20px;
  }

  .content h2 {
    margin: 10px 0;
    font-size: 30px;
  }

  .price-info {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 30px;
  }

  .prev-price {
    margin: 0;
  }

  .add-to-cart-container {
    flex-direction: column;
  }

  .counter {
    width: 100%;
  }

  .counter button {
    width: 40%;
  }

  .cart-container {
    z-index: 20;
    right: -85px;
    top: 40px;
  }
```

That's it devs, incase of any confusion you can watch the video tutorial I pinned above and the source code is also available for free on my github: https://github.com/chaoocharles/ecommerce-product-page-html-css-javascript

_Do me a favor now and subscribe to my channel_ 😊: https://www.youtube.com/c/chaoocharles