Skip to content

NDOY3M4N/crowdfunding-product-page

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Frontend Mentor - Blogr landing page solution

This is a solution to the Blogr landing page challenge on Frontend Mentor. Frontend Mentor challenges help you improve your coding skills by building realistic projects.

Table of contents

Overview

The challenge

Users should be able to:

  • View the optimal layout for the site depending on their device's screen size
  • See hover states for all interactive elements on the page

Screenshot

Screenshot of my solution

Links

My process

Built with

  • Semantic HTML5 markup
  • SASS and some CSS custom properties
  • Flexbox
  • CSS Grid
  • Mobile-first workflow
  • Vanilla Javascript

What I learned

In the design, the item card in the main page is kinda similar to the item card on the modal. So I thought to myself why not use the same markup. But then I realized that the disposition of certain element is not the same.

ITEM MAIN ITEM MODAL
item--main item--modal

As you can see the header and the footer of our items are not the same. So I come up with this markup:

<div class="item item--main" id="bamboo">
  <div class="item__header">
    <div class="item__header__title">Bamboo Stand</div>
    <div class="item__header__pledge">Pledge $25 or more</div>
  </div>
  <div class="item__desc">
    <p>You get an ergonomic stand made of natural bamboo. You've helped us launch our promotional campaign, and you’ll be added to a special Backer member list.</p>
  </div>
  <div class="item__footer">
    <div class="item__footer__stock">
      <h1>101</h1>
      <p>left</p>
    </div>
    <button class="btn" data-pledge="pledgeBambooStand">Select Reward</button>
  </div>
</div>
<div class="item item--modal">
  <div class="item__header">
    <div class="item__header__radio">
      <input type="radio" name="radioPledge" id="pledgeBambooStand">
      <span class="custom-radio"></span>
    </div>
    <label for="pledgeBambooStand" class="item__header__title">Bamboo Stand</label>
    <div class="item__header__pledge">Pledge $25 or more</div>
  </div>
  <div class="item__desc">
    <p>You get an ergonomic stand made of natural bamboo. You've helped us launch our promotional campaign, and you’ll be added to a special Backer member list.</p>
  </div>
  <div class="item__footer">
    <div class="item__footer__stock">
      <h1>101</h1>
      <p>left</p>
    </div>
    <form action="#" class="item__footer__action">
      <p>Enter your pledge</p>
      <div action="#" class="item__footer__action__checkout">
        <div class="item__footer__action__checkout__input">
          <span class="unit">$</span>
          <input type="number" name="pledgeMoney" min="25" value="25" data-item="bamboo-stand">
        </div>
        <button class="btn">Continue</button>
      </div>
    </form>
  </div>
</div>

Here's the SASS for these classes (sorry for the long sass code 😭):

/* Style for an item */
.item {
  --border-width: 2px;
  --padding: 1.5rem;
  @extend .card; // some padding and margin styles

  &.selected { border-color: var(--light-cyan); }
  & > * + * { margin-top: 1.5rem; }

  &__header {
    &__title {
      font-weight: 700;
      cursor: pointer;
      @include mixins.transition(color, .4s);
    }

    &__pledge {
      font-weight: 500;
      color: var(--light-cyan);
    }
  } /* end of .item__header */

  &__footer {
    &__stock {
      display: flex;
      align-items: center;

      h1 {
        margin-bottom: 0;
        margin-right: 1rem;
      }
    }
  } /* end of .item__footer */


  /* Style for a basic item (item shown in the main element)*/
  &--main {
    .item__header > * + * { margin-top: .5rem; }
    .item__footer > * + * { margin-top: 1.5rem; }

    .item__header,
    .item__footer {

      @include mixins.screen('small') {
        display: flex;
        align-items: center;
        justify-content: space-between;

        & > * + * { margin-top: 0; }
      }
    }
  } /* end of .item--main */

  /* Style for a complex item (item shown in the modal element)*/
  &--modal {
    --padding: 1.5rem 0;

    & > *:not(.item__footer) { padding: 0 1rem; }

    &.selected {
      .item__footer__action { display: block; }
    }
    @include mixins.screen('small') {
      &.selected {
        .item__footer__action { display: flex; }
      }
    }

    .item__header {
      display: grid;
      grid-template-columns: 48px 1fr;
      grid-template-rows: 20px 20px;
      column-gap: .2rem;
      row-gap: .5rem;

      &__title { /* shamelessly copied from tailwindcss */
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }

      &__radio {
        grid-row: 1 / -1;
        position: relative;
        cursor: pointer;
        user-select: none;

        input[type="radio"] {
          position: absolute;
          opacity: 0;
          width: 24px;
          height: 24px;
          transform: translate(50%, 50%);
          z-index: 5;

          & + .custom-radio {
            position: absolute;
            left: 0;
            top: 0;
            width: 24px;
            height: 24px;
            border: 2px solid var(--light-gray);
            border-radius: 50%;
            background-color: transparent;
            transform: translate(50%, 50%);
            @include mixins.transition(border, .4s);
          }

          &:checked + .custom-radio:after {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            margin: .25rem;
            border-radius: 50%;
            background-color: var(--light-cyan);
          }
        }
      } /* end of .item__header__radio */

      &:hover {
        .item__header {
          &__title { color: var(--light-cyan); }
          &__radio input + .custom-radio { border: 2px solid var(--light-cyan); }
        }
      }

      &--no-reward .item__header__title {
        grid-row: 1 / -1;
        align-self: center;
      }

      @include mixins.screen('small') {
        grid-template-columns: 48px max-content max-content;

        &__title,
        &__pledge {
          grid-row: 1 / -1;
          align-self: center;
        }

        &__pledge { margin-left: .5rem; }
      }
    } /* end of .item__header (modal) */

    .item__footer {
      &__stock { padding: 0 1rem; }

      &__action {
        border-top: 1px solid var(--light-gray);
        margin-top: 1rem;
        text-align: center;
        padding: 1.5rem 1rem 0;
        display: none;

        &__checkout {
          display: grid;
          grid-template-columns: repeat(2, 1fr);
          gap: .5rem;
          margin-top: 1rem;

          &__input  {
            position: relative;

            input[type="number"] {
              font-weight: 700;
              text-align: center;
              border: 2px solid var(--light-gray);
              border-radius: 10rem;
              padding: 1.25rem;
              width: 100%;
              -moz-appearance: textfield;

              &::-webkit-outer-spin-button,
              &::-webkit-inner-spin-button {
                -webkit-appearance: none;
                margin: 0;
              }

              &:focus {
                outline: none;
                border: 2px solid var(--light-cyan);
              }
            }

            span.unit {
              position: absolute;
              top: 50%;
              left: 0;
              transform: translateY(-50%);
              margin-left: 1.5rem;
              margin-top: -1px;
              font-weight: 700;
              color: var(--dark-gray);
            }
          }
        }

        @include mixins.screen('small') {
          text-align: left;
          align-items: center;
          justify-content: space-between;

          &__checkout {
            margin-top: 0;
            flex: 0;
            &__input { width: 100px; }
          }
        }
      }
    } /* end of .item__footer (modal) */

    @include mixins.screen('small') {
      position: relative;

      .item__footer__stock {
        position: absolute;
        top: 2rem;
        right: 0;
        display: inline-flex;
      }
    }
  } /* end of .item--modal */
}

Continued development

In my Sass files I tried to use the BEM notations for my class but I'm pretty sure I'm not using it correctly. Also I would like if I have some time to refactor my javascript code, specially the part where the user try to pledge.

Useful resources

Author