DEV Community

Cover image for FEM-1 Product Preview Card Component
Kehinde_Daniels
Kehinde_Daniels

Posted on

FEM-1 Product Preview Card Component

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, 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

2.1 Basic Layout

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

    </article>
  </main>
</body>
Enter fullscreen mode Exit fullscreen mode

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

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  -->
    <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>
Enter fullscreen mode Exit fullscreen mode

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 elements match the conditions or if the browser doesn’t support the 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

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  -->
      <!-- 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><s>$169.99</s></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>

Enter fullscreen mode Exit fullscreen mode

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 */
/* 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;
  }
}

Enter fullscreen mode Exit fullscreen mode

This whole reset code is provided by Andy bell, you can check here 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 pseudo-class

: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 */
: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;
}
Enter fullscreen mode Exit fullscreen mode

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 */

body {
  display: grid;
  place-content: center

Enter fullscreen mode Exit fullscreen mode

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

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 */
.product {
    background-color: var(--clr-white);
    border-radius: .5rem;
    overflow: hidden;
}
Enter fullscreen mode Exit fullscreen mode

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 */

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

3.5 Styling the product content

Image description

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 */

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

Enter fullscreen mode Exit fullscreen mode

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

Image description

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 */
.product__category {
    font-family: var(--ff-secondary);
    letter-spacing: 6px;
    text-transform: uppercase;
}
Enter fullscreen mode Exit fullscreen mode

*3.5.2 Styling the product title and product description *

.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);
}

Enter fullscreen mode Exit fullscreen mode

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.

.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);
}
Enter fullscreen mode Exit fullscreen mode

:first-child is a pseudo-class selector in CSS used to select and style the first child element within its parent element.

*3.5.4 Styling the button *

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;
}

Enter fullscreen mode Exit fullscreen mode

Next is to set the hover and focused state

button:focus,
button:hover {
  background-color: var(--clr-primary-cyan-dark);
} 
Enter fullscreen mode Exit fullscreen mode

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

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

Image description

4.0 Responsiveness / Media Query

Image description

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:

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

Enter fullscreen mode Exit fullscreen mode

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.

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

Enter fullscreen mode Exit fullscreen mode

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 */


}
Enter fullscreen mode Exit fullscreen mode

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

6.4 Media Query for the images

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

      <!-- 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>

Enter fullscreen mode Exit fullscreen mode

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.

/* 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;
  }
}
Enter fullscreen mode Exit fullscreen mode

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?

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

Enter fullscreen mode Exit fullscreen mode

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,

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

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!

`

Top comments (0)