Search code examples
csssassresponsive-designmedia-queriesweb-frontend

Responsive Design - making container element act differently on different resolutions


As a part of a home interview assignment, I was asked to build a responsive e-commerce mini website site based on a Figma design I got.

Here's the mockup:

mobile tablet desktop

My solution process:

Create a CSS file with media queries, and for the card container give a display of grid. On each resolution, change the elements' columns and raws they span, to copy the desired layout.

The problem:

On a tablet, they wish to show 3 products on each line and to make the product card look like the mobile single card. See image below

The problem is I ordered the card to change based on the resolution, so I kind of need to find a way to tell the card to look like it looks on mobile.

I saw it is possible doing it with an experimental technology which is called "container query", meaning that the size of the container will decide the element CSS and not the screen size.

Another possibility from what I've read online is that SASS allows you to create a class and tell it to behave differently on different resolutions (create the media queries inside the class essentially).

My questions:

  1. Which CSS library/pre-processor should I learn to use? SASS? Tailwind? Styled Components? (I'm not using React so I guess Styled Components isn't even relevant, isn't it?)
  2. I know I can solve this with pure CSS, but I'm not sure how. How would you approach this task?
  3. Can anyone please attach a fully responsive website example with up-to-date code which I can study from?

Here's the demo:

@import url("https://rsms.me/inter/inter.css");

/* ------------------- Variables ------------------------ */

:root {
  font-family: "Inter", sans-serif;

  /* btn background colors */
  --bg-dark-blue: #0b76e0;
  --bg-blue: #55a0ea;
  --bg-light-blue: #d5eaff;
  --bg-baby-blue: #88bdf1;

  --bg-orange: #ff5c00;
  --bg-dark-orange: #b04000;

  /* text colors */
  --text-semi-gray: #7b8997;
  --text-medium-gray: #9fa9b3;
  --text-bold-gray: #626e79;
  --text-heavy-gray: #2c384a;
  --text-xtra-bold-gray: #55626e;

  --text-red: #df0923;
  --text-blue: #0b76e0;
  --text-white: #ffffff;
}

* > * {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
}
ul {
  margin-left: 11px;
}
a {
  text-decoration: none;
}

body {
  background-color: #f2f4f7;
}

main {
  margin: 40px auto;
  max-width: 1024px;
  background-color: #eeeff1;
  padding: 25px;
}

h2 {
  font-size: 20px;
  color: var(--text-xtra-bold-gray);
  line-height: 24px;
}

/* ------------------- /Variables ------------------------ */

/* ------------------- Global Classes ------------------------ */

.btn {
  border: none;
  background-color: transparent;
  cursor: pointer;
  border-radius: 12px;
}

.bg-dark-blue {
  background-color: var(--bg-dark-blue);
}
.bg-blue {
  background-color: var(--bg-blue);
}
.bg-light-blue {
  background-color: var(--bg-light-blue);
}
.text-gray {
  color: var(--text-xtra-bold-gray);
}
.text-semi-gray {
  color: var(--text-semi-gray);
}
.text-heavy-gray {
  color: var(--text-heavy-gray);
}
.text-medium-gray {
  color: var(--text-medium-gray);
}
.text-red {
  color: var(--text-red);
}
.text-blue {
  color: var(--text-blue);
}
.text-white {
  color: var(--text-white);
}

/* ------------------- /Global Classes ------------------------ */

/* ------------------- Product Ticket ------------------------ */
.card {
  width: 100%;
  line-height: 140%;
  background: #ffffff;
  box-shadow: 0px 51px 114px rgba(199, 199, 199, 0.15),
    0px 15.375px 34.3677px rgba(199, 199, 199, 0.0895633),
    0px 6.38599px 14.2746px rgba(199, 199, 199, 0.0605927),
    0px 2.30969px 5.16283px rgba(199, 199, 199, 0.0352376);
  border-radius: 12px;
  position: relative;
  gap: 5px;
  letter-spacing: 0.2px;
  margin: 45px auto;
}

.ribbon-underlay {
  box-shadow: 4px 4px 15px rgb(26 35 126 / 20%);
  position: absolute;
  width: 7px;
  height: 6.5px;
  top: 10px;
  left: -5px;
  transform: rotate(45deg);
  z-index: -1;
}

.ribbon-underlay.best-choice-underlay {
  background-color: var(--bg-dark-orange);
}

.ribbon-underlay.best-value-underlay {
  background-color: var(--bg-baby-blue);
}

.ribbon-overlay {
  color: var(--text-white);
  content: "";
  width: 140px;
  height: 32px;
  position: absolute;
  top: -19px;
  left: -7px;
  box-shadow: 4px 4px 15px rgb(26 35 126 / 20%);
  z-index: 1;
  border-radius: 0px 8px 8px 0px;
}

.ribbon-overlay.best-choice-overlay {
  background-color: var(--bg-orange);
}

.ribbon-overlay.best-value-overlay {
  background-color: var(--bg-light-blue);
  color: var(--text-blue);
}

.ribbon-overlay .content {
  display: flex;
  justify-content: center;
  height: 100%;
  gap: 8px;
}

.ribbon-overlay .content > * {
  align-self: center;
}

.product .product-image img {
  max-width: 100%;
}

.product .product-rating {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-self: center;
  max-width: 49px;
}

.product .product-rating .numeric-rating {
  text-align: center;
  line-height: 33.6px;
}

.product .product-rating .stars-rating {
  display: flex;
  flex: 1;
  justify-content: center;
}

.product .product-rating .info {
  color: var(--text-medium-gray);
  position: absolute;
  right: 0;
}

.product .product-rating .numeric-rating {
  color: var(--text-xtra-bold-gray);
  font-weight: 700;
}

.product .product-rating .stars-rating {
  color: var(--bg-dark-blue);
}

.product .product-title {
  color: var(--text-xtra-bold-gray);
}

.product .product-discount {
  background-color: var(--bg-blue);
  color: white;
  border-radius: 4px;
  padding: 2px 4px;
  max-width: max-content;
}
.product .product-company {
  color: var(--text-bold-gray);
}
.product .product-view-deal {
  background-color: var(--bg-dark-blue);
  color: var(--text-white);
}
.product .product-vendor {
  color: var(--text-xtra-bold-gray);
}
.product .product-show-more {
  color: var(--text-xtra-bold-gray);
  cursor: pointer;
  display: flex;
}
.hidden {
  display: none !important;
}
.rotate {
  transform: rotate(180deg);
}
.product .product-info .highlights .title {
  color: var(--text-xtra-bold-gray);
  margin: 10px 0 8px 0;
}

.product .product-info .highlights ul li {
  color: var(--text-semi-gray);
  text-decoration: dotted;
}
.product .product-info .product-price {
}
.product .product-info .product-price .current {
  color: var(--text-heavy-gray);
}
.product .product-info .product-price .original {
  color: var(--text-medium-gray);
  text-decoration: line-through;
}
.product .product-info .product-price .discount {
  color: var(--text-red);
}

.product .product-info .vendor-container {
  /*   display: flex;
  justify-content: space-around; */
}

.product .product-info .vendor-container > * {
  /* flex: 1 1 100%; */
}

.product .product-info .vendor-container img {
  width: 100%;
}

.product .product-info .vendor-container .view-btn {
  border: 1px solid var(--bg-dark-blue);
  color: var(--bg-dark-blue);
}
/* ------------------- /Product Ticket ------------------------ */
/* ------------------- Media Query ------------------------ */

/* Media Query for Mobile Devices */
@media (max-width: 480px) {
  .card {
    display: grid;
    padding: 20px;
    grid-template-columns: repeat(4, minmax(10px, 1fr));
    grid-template-rows: repeat(7, minmax(min-content, max-content));
  }

  .product .product-image {
    grid-column: 2 / 4;
    grid-row: 1;
    -ms-grid-column-span: 3;
    justify-content: center;
    margin-top: 22px;
  }

  .product .product-image img {
    width: 160px;
  }

  .product .product-rating {
    grid-column: 4 / 5;
    grid-row: 1;
    align-self: end;
    bottom: 10px;
  }

  .product .product-rating .info > * {
    font-size: 8.96px;
  }

  .product .product-rating .numeric-rating {
    font-size: 24px;
  }

  .product .product-rating .stars-rating > * {
    font-size: 8.1px;
  }

  .product .product-discount {
    font-size: 12px;
    font-weight: 600;
  }

  .product .product-title {
    font-size: 14px;
    font-weight: 600;
    line-height: 19.6px;
    grid-row: 4;
    grid-column: 1/-1;
  }

  .product .product-discount {
    font-size: 12px;
    font-weight: 500;
    grid-column: 1 / -1;
  }

  .product .product-company {
    font-size: 14px;
    font-weight: 400;
    grid-column: 1/5;
    grid-row: 2;
    text-align: center;
  }

  .product .product-view-deal {
    font-size: 16px;
    font-weight: 500;
    padding: 4px 40px;
    width: 100%;
    height: 42px;
    margin: 15px 0 10px;
    grid-row: 5;
    grid-column: 1/-1;
  }

  .product .product-vendor {
    font-size: 14px;
    font-weight: 400;
    text-align: left;
    grid-row: 6;
    grid-column: 1/3;
    text-align: center;
  }
  .product .product-show-more {
    justify-content: right;
    font-size: 14px;
    font-weight: 400;
    text-align: right;
    grid-row: 6;
    grid-column: 3/5;
  }
  .product .product-info {
    display: grid;
    grid-column: 1/-1;
    grid-template-columns: repeat(4, 1fr);
    grid-template-rows: repeat(3, minmax(min-content, max-content));
    gap: 10px;
  }
  .product .product-info .highlights {
    font-weight: 700;
    font-size: 18px;
    grid-column: 1/-1;
  }
  .product .product-info .highlights .title {
    font-size: 18px;
    font-weight: 700;
    margin: 10px 0 8px 0;
  }
  .product .product-info .highlights ul {
    line-height: 16.8px;
  }
  .product .product-info .highlights ul li {
    font-weight: 400;
    font-size: 12px;
  }

  .product .product-info .product-price {
    grid-column: 1/-1;
    display: flex;
    gap: 10px;
    justify-content: center;
  }

  .product .product-info .product-price .current {
    font-weight: 600;
    font-size: 14px;
  }
  .product .product-info .product-price .original {
    font-weight: 400;
    font-size: 14px;
  }
  .product .product-info .product-price .discount {
    font-weight: 400;
    font-size: 10px;
  }
  .product .product-info .vendor-container {
    grid-column: 1/-1;
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    justify-items: center;
  }
  .product .product-info .vendor-container img {
    align-self: center;
    width: 65.83px;
    grid-column: 1/3;
  }
  .product .product-info .vendor-container .view-btn {
    width: 152px;
    height: 32px;
    grid-column: 3/5;
  }
}

/* Media Query for low resolution  Tablets, Ipads */
@media (min-width: 481px) and (max-width: 768px) {
  .card {
    display: grid;
    padding: 20px 10px 10px;
    column-gap: 15px;
    grid-template-columns: 1fr 2fr max-content max-content;
    grid-template-rows: repeat(5, minmax(min-content, max-content));
  }

  .hide-discount {
    visibility: hidden;
  }

  .ribbon-overlay {
    font-size: 16px;
    font-weight: 600px;
  }

  .product .product-image {
    grid-column: 1 / 2;
    grid-row: 1/5;
  }

  .product .product-rating {
    grid-column: 3/4;
    grid-row: 1/5;
  }

  .product .product-rating .info > * {
    font-size: 9.05px;
  }

  .product .product-rating .numeric-rating {
    font-size: 20px;
  }

  .product .product-rating .stars-rating > * {
    font-size: 8.19px;
  }

  .product .product-title {
    font-size: 14px;
    font-weight: 500;
    line-height: 19.6px;
    grid-column: 2/3;
    grid-row: 1/2;
    height: 40px;
    overflow: hidden;
  }

  .product .product-discount {
    font-size: 12px;
    font-weight: 600;
    grid-column: 2/3;
    grid-row: 3/4;
  }

  .product .product-company {
    font-size: 12px;
    font-weight: 400;
    grid-column: 2/3;
    grid-row: 2/3;
  }

  .product .product-view-deal {
    font-size: 14px;
    font-weight: 700;
    padding: 4px 40px;
    min-width: 170px;
    min-height: 36px;
    grid-row: 2/4;
    align-self: center;
  }

  .product .product-vendor {
    text-align: center;
    font-size: 12px;
    grid-column: 4/5;
    grid-row: 4/5;
  }
  .product .product-show-more {
    font-size: 12px;
    font-weight: 500;
    grid-column: 2/3;
    grid-row: 4/5;
  }
  .product .product-info {
    display: grid;
    grid-column: 1/-1;
    grid-template-columns: 3fr 1fr;
    grid-template-rows: 4fr 1fr;
    row-gap: 10px;
    column-gap: 20px;
  }

  .product .product-info .highlights {
    font-weight: 600;
    font-size: 18px;
    grid-column: 1/2;
    grid-row: 1/3;
  }
  .product .product-info .highlights ul li {
    font-weight: 400;
    font-size: 14px;
  }

  .product .product-info .product-price {
    align-self: end;
    display: flex;
    flex-wrap: wrap;
    row-gap: 5px;
    grid-column: 2/3;
    grid-row: 1/2;
  }
  .product .product-info .product-price .current {
    font-weight: 600;
    font-size: 16px;
    flex: 1 1 100%;
    /* flex-grow: 1; */
  }
  .product .product-info .product-price .original {
    font-weight: 400;
    font-size: 14px;
    /* flex-grow: 1; */
    flex: 1 1 50%;
  }
  .product .product-info .product-price .discount {
    font-weight: 400;
    font-size: 12px;
    /* flex-grow: 1; */
    flex: 1 1 50%;
  }
  .product .product-info .vendor-container {
    align-self: end;
    display: flex;
    grid-column: 2/3;
    grid-row: 2/3;
    gap: 15px;
  }
  .product .product-info .vendor-container img {
    min-width: 0;
    height: 100%;
    flex: 3 3;
    align-self: center;
  }
  .product .product-info .vendor-container .view-btn {
    flex: 5 5;
    width: 94px;
    height: 36px;
  }
  /* ------------------- Related Deals ------------------------ */
  .related-deals {
    display: grid;
    grid-template-columns: repeat(12, 1fr);
  }

  .col-md-4 {
    grid-column: 1 / span 4;
  }
  /* ------------------ /Related Deals ------------------------ */
}

/* Media Query for Tablets Ipads portrait mode */
@media (min-width: 768px) {
  .card {
    display: grid;
    padding: 20px 15px 10px;
    column-gap: 15px;
    grid-template-columns: 0.5fr 1fr 2fr max-content max-content;
    grid-template-rows: repeat(5, minmax(min-content, max-content));
  }

  .product .product-image {
    grid-column: 2 / 3;
    grid-row: 1/5;
  }

  .product .product-rating {
    grid-column: 4/5;
    grid-row: 1/5;
    max-width: 75px;
  }
  .product .product-rating .info > * {
    font-size: 12.25px;
  }

  .product .product-rating .numeric-rating {
    font-size: 32.8165px;
  }

  .product .product-rating .stars-rating > * {
    font-size: 11.08px;
  }

  .product .product-title {
    font-size: 16px;
    font-weight: 600;
    line-height: 22.4px;
    grid-column: 3/4;
    grid-row: 1/2;
  }

  .product .product-discount {
    font-size: 14px;
    font-weight: 500;
    grid-column: 3/4;
    grid-row: 3/4;
  }

  .product .product-company {
    font-size: 14px;
    font-weight: 500;
    grid-column: 3/4;
    grid-row: 2/3;
  }

  .product .product-view-deal {
    font-size: 16px;
    font-weight: 700;
    padding: 10px 80px;
    min-width: 170px;
    min-height: 36px;
    grid-row: 2/4;
    grid-column: 5/6;
    align-self: center;
  }

  .product .product-vendor {
    font-size: 14px;
    font-weight: 400;

    grid-column: 5/6;
    grid-row: 4/5;
    justify-content: start;
    text-align: left;
  }
  .product .product-show-more {
    font-size: 14px;
    font-weight: 600;
    grid-column: 5/6;
    grid-row: 4/5;
    justify-content: end;
  }
  .product .product-info {
    grid-column: 2/-1;
    grid-row: 5/6;
    display: grid;
    grid-template-rows: 4fr 1fr;
    gap: 10px;
    grid-template-columns: 2fr 2fr 1fr 1fr;
  }
  .product .product-info .highlights {
    font-weight: 700;
    font-size: 20px;
    grid-column: 1/3;
    grid-row: 1/3;
  }
  .product .product-info .highlights ul li {
    font-weight: 400;
    font-size: 14px;
  }
  .product .product-info .product-price {
    align-self: end;
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    grid-column: 4/5;
    grid-row: 1/3;
    margin-bottom: 25%;
  }
  .product .product-info .product-price .current {
    font-weight: 600;
    font-size: 24px;
    flex: 1 0 100%;
  }
  .product .product-info .product-price .original {
    font-weight: 400;
    font-size: 18px;
  }
  .product .product-info .product-price .discount {
    font-weight: 400;
    font-size: 14px;
  }
  .product .product-info .vendor-container {
    align-self: end;
    grid-column: 4/5;
    grid-row: 1/3;
    gap: 15px;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
  }
  .product .product-info .vendor-container img {
    max-width: 65px;
  }
  .product .product-info .vendor-container .view-btn {
    width: 246px;
    height: 44px;
    font-weight: 700;
  }
}

/* ------------------- /Media Query ------------------------ */
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>

    <link
      href="https://fonts.googleapis.com/icon?family=Material+Icons"
      rel="stylesheet"
    />
    <link
      rel="stylesheet"
      href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,[email protected],100..700,0..1,-50..200"
    />

    <link rel="stylesheet" href="index.css" />
  </head>
  <body>
    <script defer src="./index.js"></script>

    <main>
      <div class="product card">
        <div class="ribbon">
          <div class="ribbon-overlay best-choice-overlay">
            <div class="content">
              <i class="fa-solid fa-trophy"></i>
              <span>Best Choice</span>
            </div>
          </div>
          <div class="ribbon-underlay best-choice-underlay"></div>
        </div>
        <div class="product-image">
          <img src="https://images-na.ssl-images-amazon.com/images/I/916NVBBT0mS.__AC_SY300_SX300_QL70_FMwebp_.jpg" alt="" />
        </div>
        <div class="product-rating">
          <div class="info">
            <span class="material-symbols-outlined"> info </span>
          </div>
          <div class="numeric-rating">9.8</div>
          <div class="stars-rating">
            <span class="material-symbols-outlined"> star </span>
            <span class="material-symbols-outlined"> star </span>
            <span class="material-symbols-outlined"> star </span>
            <span class="material-symbols-outlined"> star </span>
            <span class="material-symbols-outlined"> star_half </span>
          </div>
        </div>
        <div class="product-title">
          Sony X95J 85 Inch TV: BRAVIA XR Full Array LED 4K Ultra HD Smart
          Google TV
        </div>
        <div class="product-discount">50% OFF</div>
        <div class="product-company">SONY</div>
        <button class="product-view-deal btn">View Deal</button>
        <div class="product-vendor">On Amazon</div>
        <div class="product-show-more">
          Show more
          <span class="material-symbols-outlined expand_more">
            expand_more
          </span>
        </div>
        <div class="product-info hidden">
          <div class="highlights">
            <h3 class="title">Main Hightlights</h3>
            <ul>
              <li>
                4K Ultra HD (2160p resolution): Enjoy breathtaking 4K movies and
                TV shows at 4 times the resolution of Full HD, and upscale your
                current content to Ultra HD-level picture quality.
              </li>
              <li>
                Voice remote with Alexa: Use the sound of your voice to do
                everything a regular remote would do: Switch streaming services,
                inputs, channels and much more.
              </li>
              <li>
                Access thousands of shows with Fire TV - Watch over 1 million
                streaming movies and TV episodes with access to thousands of
                channels, apps and Alexa skills, including Apple TV+, Disney+,
                Hulu, Netflix, Prime Video, Sling TV, YouTube and other services
                right from this TV.
              </li>
              <li>
                Hands-free control: Pair with your Amazon Echo and go
                hands-free, controlling volume, search, channels and more.
              </li>
            </ul>
          </div>
          <div class="product-price">
            <div class="current">$99.00</div>
            <div class="original">$199.00</div>
            <div class="discount">(26% off)</div>
          </div>
          <div class="vendor-container">
            <img src="https://pngimg.com/uploads/amazon/amazon_PNG24.png" />
            <button class="view-btn btn">View</button>
          </div>
        </div>
      </div>

      <div class="related-deals">
        <h2>Related Deals You Might Like For Tv</h2>
      </div>
    </main>
    <script src="https://kit.fontawesome.com/ead62a3458.js"></script>
  </body>
</html>


Solution

  • So the problem was that my Card class was changing as I ordered it according to the screen resolution, but I wanted it to change according to the Card container resolution so that on a tablet, I can spread 3 small (mobile layout) cards. I know I could have created a new class and named it small-card, but I wanted to give the same class name for all cards.

    The solution for using the same class but changing its CSS properties without duplicating the same properties to a new class was to use SASS.

    SASS has many capabilities, one of them is called mixins -

    Mixins allow you to define styles that can be re-used throughout your stylesheet. They make it easy to avoid using non-semantic classes like .float-left, and to distribute collections of styles in libraries.

    Also, one of SASS's great capabilities is to allow you to put the media queries inside the class, which allowed me to keep my card class well-organized.

    Here's the improved code, written with SASS:

    @mixin small-card() {
        // ...
    }
    @mixin medium-card() {
        // ...
    }
    @mixin large-card() {
        // ...
    }
    
    @media (max-width: 480px) {
      .card {
        @include small-card();
      }
    }
    @media (min-width: 481px) and (max-width: 768px) {
      .card {
        @include medium-card();
      }
    }
    @media (min-width: 768px) {
      .card {
        @include large-card();
      }
    }
    
    .related-deals {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
    
     .card {
        @include small-card();
        margin: auto;
      }
    }