FEM-1 Product Preview Card Component
Tags: tutorial, css, webdev, responsivedesign
Published: 2023-12-28 01:40:25

## 1.0 Overview 
Ever felt overwhelmed starting a challenge? Don't worry; we've got you covered. This guide focuses on frontend mentor challenges, aiming to explain our decisions while building from scratch.

Oh, sorry, I lied. Before we begin, if you would like to jump directly to the final code and be lazy like me, click [here](https://github.com/KehindeDaniels/FEM-1-product-preview-card-component-main.git), but don't be lazy to give me a star yeah?😉

## 2.0 Writing the html
Mobile-first or Desktop-first: Where to Start?. 
Well, if you chose mobile-first design, you're not alone! 😄 However, despite its popularity for CSS styling, starting with the desktop-design view when constructing your web's skeleton (HTML) can be a game-changer.

Here's why: Mobile designs often follow a top-to-bottom layout, making it tricky to determine element groupings for the web layout.

So, 

CSS: Mobile-first design approach,

HTML: Desktop-design approach

So, let's begin!

First, I want to believe you have your environment set and have downloaded the starter file. If not, no worries, you can download it from [here](https://github.com/KehindeDaniels/product-preview-card-component-main.git)
 
**2.1 Basic Layout**

```html
<body>
  <main>
    <article class="product__card">

    </article>
  </main>
</body>
```
This simplified structure is designed specifically for styling the product card. It utilizes the `<article>` HTML element, which is ideal for presenting content as an individual and self-contained unit.

Now, before we start writing codes like hackers, its important we first analyze the design so we can know how to structure it well.



![Gabrielle Essence Eau De Parfum Bottle](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ui29novey0t40w1km8r9.png)

From the above image, we know we have to divide our products into two columns, which in our markup will be represented by two divisions.

The first division will have the images (mobile and desktop-sized), and the second division will have the card content or details.

**2.2 Product Image Markup (The product element)**
```html
 <!-- html  -->
    <article class="product">
       <!-- Product Image -->
       <picture>
        <source srcset="images/image-product-desktop.jpg" media="(min-width:600px)">
        <img src="./images/image-product-mobile.jpg" alt="Gabrielle Essence Eau De Parfum Bottle">
      </picture>

      <!-- Product Content  -->
      <div class="product__content">

      </div>
    </article>
```

Hi there, don't be blown away by the `<picture>` element we used. We learn everyday right. But let's talk about it briefly

**2.2.1 The `<picture>` element**
The `<picture>` element in HTML is used to provide different versions of an image based on factors like screen size or resolution. 

I feel this is better than wrapping two `<img>` elements in a div and then styling them separately to either display or hide based on the screen size. 

Here, the `<source>` element is used to specify different versions of the image based on certain conditions using the `srcset` attribute.

Inside each `<source>` element, you define the image source (srcset) and use the media attribute to set conditions (like screen width using media queries) for when that particular image should be displayed.

The `<img>` will serve as a fallback or default image source that will be displayed if none of the <source> elements match the conditions or if the browser doesn’t support the <picture> element. It's also used for accessibility purposes by providing the alt attribute to describe the image.

If you are still not clear, you can take a few seconds to look it up in [w3schools here](https://www.w3schools.com/html/html_images_picture.asp)

Now, lets fill up the mark-up for the product content, so we can move straight to CSS. I know that's where you heading for right?

**2.3 Product Content Mark-up**

```html
 <!-- html  -->
      <!-- Product Content  -->
      <div class="product__content">

        <p class="product__category">Perfume</p>

        <h1 class="product__title">Gabrielle Essence Eau De Parfum</h1>
  
        <p class="product__description">A floral, solar and voluptuous interpretation composed by Olivier Polge, 
        Perfumer-Creator for the House of CHANEL.</p>


          <!-- Product Price -->
        <div class="product__price">
          <p>$149.99</p>
          <p>
$169.99
</p>
        </div>
        

        <!-- Button -->

        <button aria-label="Add Gabrielle Essence Eau De Parfum to Cart">
          <img src="images/icon-cart.svg" alt="">
          Add to Cart
        </button>  
        
       <!-- <button aria-label="Add Gabrielle Essence Eau De Parfum to Cart" data-icon="shopping-cart">Add to Cart</button>  -->

      </div>

```

You would notice the commented-out button used, but fear not! I will explain that towards the end so spend too much ti
me drawing our skelenton.

A few things to note from the above mark-up

##3.0 Writing the CSS
Now, the interesting part!

**3.1 CSS Reset**
The first thing you want to do is have a reset. Trust me, you don't want to work without a reset, as some elements have default margin and spacing, which you may not want in your style.

CSS reset styles are used to standardize and neutralize default styles applied by different browsers to HTML elements. It ensures a consistent starting point for styling, helping you avoid inconsistencies and achieve more control over the appearance of elements across various browsers

```css
 /* CSS */
/* Box sizing rules */
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* Remove default margin */
body,
h1,
h2,
h3,
h4,
p,
figure,
blockquote,
dl,
dd {
  margin-block-end: 0;

}

/* Remove list styles on ul, ol elements with a list role, which suggests default styling will be removed */
ul[role='list'],
ol[role='list'] {
  list-style: none;
}

/* Set core root defaults */
html:focus-within {
  scroll-behavior: smooth;
}

/* Set core body defaults */
body {
  min-height: 100vh;
  text-rendering: optimizeSpeed;
  line-height: 1.5;
}

/* A elements that don't have a class get default styles */
a:not([class]) {
  text-decoration-skip-ink: auto;
}

/* Make images easier to work with */
img,
picture {
  max-width: 100%;
  display: block;
}

/* Inherit fonts for inputs and buttons */
input,
button,
textarea,
select {
  font: inherit;
}

/* Remove all animations, transitions and smooth scroll for people that prefer not to see them */
@media (prefers-reduced-motion: reduce) {
  html:focus-within {
   scroll-behavior: auto;
  }
  
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

```
This whole reset code is provided by Andy bell, you can check [here] (https://andy-bell.co.uk/a-modern-css-reset/) for the full documentation.

**3.2 Re-usable Variables**
The DRY (D-on't R-epeat yourself) principle is not just applied in JavaScript, I mean, even in life generally, who wants to do the same rigorous process when there is a better way to go about it? CSS is not an exemption. You don't want to 'hard-condignly' change a color through out your CSS file when you could simply change it once. 

Now, this is a small project, but we will still implement variables for best styling practice

_CSS variables are applied to the [`:root`](https://developer.mozilla.org/en-US/docs/Web/CSS/:root) [pseudo-class](https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes)_

`:root` in CSS refers to the highest-level parent element, usually the `<html>` element. It's commonly used to declare global CSS variables (custom properties) accessible throughout the document, ensuring consistent values and easy modifications across stylesheets.

```CSS
 /* CSS */
:root {
    --clr-primary-cyan: hsl(158, 36%, 40%);
    --clr-primary-cyan-dark: hsl(158, 36%, 30%);
    --clr-primary-cream: hsl(30, 38%, 92%);

    --clr-neutral-dark-blue: hsl(212, 21%, 14%);
    --clr-neutral-grayish-blue: hsl(228, 12%, 48%);
    --clr-white: hsl(0, 0%, 100%);

    --ff-body: 'Montserrat', sans-serif;
    --ff-secondary: 'Fraunces', sans-serif;

    --fw-regular: 500;
    --fw-bold: 700;
}
```
What we have done basically is define the colors `--clr`, font family `--ff` and font weight `--fw`.

It is important to note that for larger projects, the variables might be more than this.

**3.2 Little adjustment to the body**
We already have some reset code to the `<body>` element, but for the purpose of this small project we will add a few more

- Center the card on our page (grid/flex)

- and a default font-size: 0.875rem;

```CSS
 /* CSS */

body {
  display: grid;
  place-content: center

```
You could as well display flex, justify, and align items to center; well, that's an extra line of code. Who likes extra?

**3.3 Styling the product container**

![Gabrielle Essence Eau De Parfum Bottle](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mj18q7rca9egbyz5qfzt.png)

Nothing much here:

- give it a background color
- make the border radius little rounded and
- to prevent the elements from overflowing outside because of the shortened rounded border radius, we set to hidden. (try commenting out the overflow property and observe the difference)

```CSS
 /* CSS */
.product {
    background-color: var(--clr-white);
    border-radius: .5rem;
    overflow: hidden;
}
```
_var(--custom-css-property); is how we use our variables in case you are wondering what that means_

**3.4 Styling the product image**

Thanks to our reset, we already have enough to make our images and pictures responsive.

```CSS
 /* CSS */

 /* This is already declared in the reset, no need re-declaring */
img,
picture {
  max-width: 100%;
  display: block;
}
```

**3.5 Styling the product content**


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jix29y0m20f0171o1h0r.png)

Where we are going: Notice the space around (padding) the content; hence we need to set padding. 

Where we are: Notice the button does not take up the entire width of the container because they are `in-line` elements. We can solve this in various ways by setting its `width` to 100%, but in this scenario, we will utilize the grid method. when you set a container to grid, its children's elements take up the entire width of the container. 

```CSS
 /* CSS */

.product__content {
  padding: 1.5rem 2rem;
  display: grid;
  gap: .5rem;
}

```
You should see the difference now if you are following along.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qgk2nw4mtadv0yx49mlp.jpg)

**3.5.1 Styling the Product Category**

This is as straight-forward as:

we set the font-family, give it spacing as seen in the final design, and set the text to uppercase

```CSS
/* CSS */
.product__category {
    font-family: var(--ff-secondary);
    letter-spacing: 6px;
    text-transform: uppercase;
}
```

**3.5.2 Styling the product title and product description **

```CSS
.product__title {
    font-size: 2rem;
    line-height: 1.1;
    color: var(--clr-neutral-dark-blue);
    font-weight: var(--fw-bold);
}

.product__desscription {
  font-weight: var(--fw-regular);
}

```
Self-explanatory right? maybe just the line height, if you are wondering, which is just the vertical space between lines of text within an element. Try commenting out that property and observe the subtle difference

**3.5.3 Styling the product price**

Notice the layout; Flex is our go-to for this.

```CSS
.product__price{
  display: flex;
  align-items: center;
  gap: 1rem;
}

.product__price p:first-child {
  font-size: 2rem;
  font-weight: var(--fw-bold);
  color: var(--clr-primary-cyan);
}
```
`:first-child` is a [pseudo-class](https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes) selector in CSS used to select and style the first child element within its parent element.


**3.5.4 Styling the button **

```CSS
button {
  cursor: pointer;
  font-family: inherit;
  border: none;
  border-radius: .5rem;
  padding: 1rem 1.5rem;
  background-color: var(--clr-primary-cyan);
  color: var(--clr-white);
  font-weight: var(--fw-bold);

  /* flexing because of the icon */
  display: inline-flex;
  justify-content: center;
  align-items: center;
  gap: .5rem;
}

```
Next is to set the hover and focused state

```CSS
button:focus,
button:hover {
  background-color: var(--clr-primary-cyan-dark);
} 
```

For visual accessibility on tab-focus, it is best to set the focused state to a bit different style.

```CSS
/* Clearly visible focus outline for accessibility on tab focus */
button:focus {
  outline: 2px solid var(--clr-primary-cyan-dark);
  outline-offset: 2px;
}
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pwyzj7g1n7js73lfjjtb.jpg)

**4.0 Responsiveness / Media Query**


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rurmryyopn56jol2ihwx.png)

We have styled for mobile; to make it responsive, we need to style for desktop

**4.1 little adjustments**

go to the responsive mode of your browser. Right-click and choose inspect or the command key `ctrl/cmd` + `shift` + `i`. Try expanding the screen; you will notice how the elements are stretched beyond imagination😀.

what if we can tell our product that, "hey product card, don't go beyond this size in width" That would be like saying, your maximum width is this. 

Notice this is different from saying your width is this, else the product card will maintain only one width, but a max width is a saying: hey, you can increase or decrease as much as you want but don't go beyond this width

So, let's go back to our product container and apply this:

```CSS
.product {
   /* previous code */
    max-width: 38.125rem;
}

```
Now, try resizing in responsive mode again; you will notice that at a given width the product card stops growing and becomes intact.

Let's make another little adjustment. We said column, right? From the above image, we can either use flex or grid.

If we use flex, fine! but we will have to set `flex-direction` to column (another extra line we do not want). But if we use `grid` the layout remains the same, and we will only need to declare for column template. 

Moreso, Choosing Grid over Flex for equally-sized containers:

- Grid allows direct and simple definition of equal-width columns/rows.

- Flex might need additional lines of code (like flex-grow or percentage calculations) to achieve equal widths for multiple containers, making it less straightforward than Grid.

```CSS
.product {
   /* previous code */
    max-width: 38.125rem;
    display: grid;
}

```

**6.2 Determining the breaking point**
Notice that in the desktop view, we have two columns. Now switch back to the responsive mode and try resizing again; it will get to the point where the design looks off and is ready for a 2-column layout.

At around 600 pixels, the design started looking a bit off, so a good point to break the layout!


**6.3 Media query**
Having determined the breaking point, lets set a media query for that.

```
/* Media query for screens with a MINIMUM width of 600px */
@media screen and (min-width: 600px) {
  /* Styles for screens larger than 600px width */
  
  
}
```

**why use min-width?**

The logic is simple. We are styling for a screen size of a minimum of 600 pixels. That is the screen size starting from 600px upward.

For more info on media query, visit the MDN docs [here](https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Media_queries)

**6.4 Media Query for the images**

Aren't we lucky? recall our mark-up for the images 

```HTML
      <!-- Product Image -->
      <picture>
        <source srcset="images/image-product-desktop.jpg" media="(min-width:600px)">
        <img src="./images/image-product-mobile.jpg" alt="Gabrielle Essence Eau De Parfum Bottle">
      </picture>

```
we already set an alternate image to be displayed at screen size of 600 pixels. Else, if we wrapped two `img` elements in a div, we would have to toggle between hidden and display for the mobile and desktop image.

**6.5 Media query for the product container**

This is as simple as it gets. We just need to set it to two columns and that's all.

```CSS
/* Media query for screens with a MINIMUM width of 600px */
@media screen and (min-width: 600px) {
  /* Styles for screens larger than 600px width */
  
  .product {
    grid-template-columns: 1fr 1fr;
  }

  button {
    padding: .5rem 1.5rem;
  }
}
```
We can also make a little adjustment to the button.

And y'all, that's all😀.

Sorry, I lied; we are not done.

Recall this?

```HTML
        <button aria-label="Add Gabrielle Essence Eau De Parfum to Cart" data-icon="shopping-cart">Add to Cart</button>  

```

I said we would talk about it. 

Note that this will achieve the same result as what we already have.

This HTML code represents a button element designed to add a product to a shopping cart. Here's an explanation:

- `<button>`: This is an HTML button element used for user interaction, likely triggering an action such as adding an item to a shopping cart.

- `aria-label="Add Gabrielle Essence Eau De Parfum to Cart"`: The `aria-label` attribute provides an accessible label for the button, specifying the text to be announced by screen readers ("Add Gabrielle Essence Eau De Parfum to Cart"). This attribute enhances accessibility, ensuring users relying on assistive technologies understand the button's purpose.

- `data-icon="shopping-cart"`: This is a `data` attribute (`data-icon`) used to store custom data associated with the button. In this case, it contains the value "shopping-cart," which might be utilized by JavaScript or CSS to display a shopping cart icon associated with the button, improving its visual representation or functionality.

- "Add to Cart": This text serves as the visible label inside the button, informing users that clicking it will add the product to the cart.

Similar to the previous code, 

```HTML
<button aria-label="Add Gabrielle Essence Eau De Parfum to Cart">
          <img src="images/icon-cart.svg" alt="">
          Add to Cart
        </button> 
```


this HTML snippet creates a button for adding a specific product to a shopping cart, ensuring accessibility through the `aria-label` attribute and potentially utilizing the `data-icon` attribute to associate the button with a shopping cart icon for improved visual representation or interaction.

## Bonus Tip

**Difference between Padding and Margin**

Imagine your content—text, images, or anything inside a box in your web page—as a picture you're hanging on a wall.

- **Padding** is like the space between the picture frame and the actual picture. It's the room you give between the content and the border of the box that holds it. More padding means more space between the content and the edge of its container.

- **Margin**, on the other hand, is the space between the picture's frame and the next picture or wall's edge. It's the gap between the box that holds your content and the other elements nearby. More margin means more space between your content's box and the surrounding elements or boundaries.

In short:
- **Padding**: Space between the content and its container's border.
- **Margin**: Space between the container (or content's box) and nearby elements or boundaries.

##Conclusion
Building a product preview card involves structuring HTML, applying CSS styles, and ensuring responsiveness. We've covered the basics here, but remember, practice and experimentation are key to mastering frontend development!

And that's a wrap! Happy coding and creating awesome product cards!



`