SASS – mixin for responsive design

In this post, I am going to explain how to use SASS mixin for responsive design which is very powerful way.

In CSS, there is a media query you can use to present a different layout for different screen size. However, it would be very tedious to write media query break points in every CSS component you write. What if screen size changes for a phone or tablet? You probably do not want to go through everything to fix it. There needs to be a better way to handle different screen size flexibly and let’s take a look at each option

mixin directive

what is mixin? It is a directive and lets you create CSS code that is to be reused throughout the website. It’s basically like a function in different programming languages.

Let’s first define each breakpoints. These are the breakpoints I want to be able to handle.

0 - 600px: phone
600 - 900px: Tablet portrait
900 - 1200px: Tablet landscape
[1200-1800] is where our normal styles apply
1800px + : Big Desktop

Use mixin directive – option 1

We are going to create a mixin called respond-phone so that you can just include the mixin in each component/layout for phone.
Please note that @content directive allows to pass in a block of code to mixin.

// _mixins.scss
@mixin respond-phone {
    // content directive allows to pass in a block of code to mixin
    @media (max-width: 600px) { @content };
}

// _base.scss
html {
    font-size: 62.5%;
    
    @include respond-phone {
        font-size: 50%;
    }
}

Originally, the font size is 62.5% of the root font size. However, by using the respond-phone mixin, it changes to 50%. It is pretty convenient because you don’t need to hard code media query every place. Instead, you just need to create necessary mixin functions (i.e., respond-tablet-portrait, respond-tablet-landscape) and use it. Although this is certainly an improvement, is there a better way? Is there a way to use just one mixin to represent all?

Use mixin directive – option 2

Since mixin is like a function, it can take an argument. It enables us to write a mixin that specifies proper media query breakpoint based on the argument!

Please note that unlike the example above, I used em instead of px. Why? It’s because if a user changes the default font size in a browser media queries will not be affected by that. However, we want the media query to reflect the change in default font size in a browser setting. Since em is a relative unit, it is the perfect one to use.

In a media query, em is always affected by the one that comes from the browser which is 16px. 1em = 16px

// _mixins.scss
/*
$breakpoint argument choices:
- phone
- tab-port
- tab-land
- big-desktop
*/
@mixin respond($breakpoint) {
  @if $breakpoint == phone {
    @media (max-width: 37.5em) { @content }; // 600px
  }
  @if $breakpoint == tab-port {
    @media (max-width: 56.25em) { @content }; // 900px
  }
  @if $breakpoint == tab-land {
    @media (max-width: 75em) { @content }; // 1200px
  }
  @if $breakpoint == big-desktop {
    @media (min-width: 112.5em) { @content }; // 1800px
  }
}

Now, we have a single mixin to use for all different screen sizes. Let’s take a look at how to use them

// _base.scss
html {
  font-size: 62.5%; // 1rem = 10px; 10 / 16 = 62.5%

  @include respond(tab-land) { // width < 900?
    font-size: 56.25%; // 1rem = 9px, 9 / 16 = 56.25%
  }

  @include respond(tab-port) { // width < 600 ?
    font-size: 50%; // 1rem = 8px, 8 / 16 = 50%
  }

  @include respond(phone) {
    font-size: 43.75%; // 1rem = 7px, 7 / 16 = 43.75%
  }

  @include respond(big-desktop) {
    font-size: 75%; // 1rem = 12px, 12/16 = 75%;
  }
}

Here, we are trying to use font-size as a breakpoint for different screen layout. You can see that I want to use different font-size for different screen and I converted to percentage to be flexible.

Please note the order of the mixin. All the max-width mixins are in a decreasing order. It’s ordered to prevent unintentional side effect. For example, if the screen size is 500px, it will fall under all the tablets and phone size. In that case, the last property written is selected and that’s why I ordered them in a decreasing size way.

Conclusion

It’s not simple to write a responsive designs due to its complexity. However, using the mixin could help a lot.

CSS – Build Animating Nav Button

Today, we are going to take a look at how to build animating nav button using css properties.
Please note that I have learned this from the udemy course by Jonas Schmedtmann.

Example

As soon as you click the button, you will see there is a cool animation making the button X. This is built by pure CSS properties which we will take a look at it soon.

HTML Code

<div class="navigation">
    <input type="checkbox" class="navigation__checkbox" id="navi-toggle">
    <label for="navi-toggle" class="navigation__button">
        <span class="navigation__icon"> </span>
    </label>
</div>

checkbox input type is used here to make toggle easier. Please note that the checkbox input is hidden and the navigation__icon class is used to style like a button.

CSS Code – SCSS

Please don’t forget to change the SCSS variables to actual numbers when you use it.

.navigation {
  &__button {
    background-color: $color-white;
    height: 7rem;
    width: 7rem;
    border-radius: 50%;
    position: fixed;
    top: 6rem;
    right: 6rem;
    z-index: 1000;
    box-shadow: 0 1rem 3rem rgba($color-black, .1);
    text-align: center;
    cursor: pointer;
  }    
    
  &__icon {
    position: relative;
    margin-top: 3.5rem;;

    &,
    &::before,
    &::after {
      width: 3rem;
      height: 2px;
      background-color: $color-gray-dark-3;
      display: inline-block;
    }

    &::before,
    &::after {
      content: "";
      position: absolute;
      left: 0;
      transition: all .2s;
    }

    &::before { top: -.8rem; }
    &::after { top: .8rem; }
  }

  &__button:hover &__icon:before {
    top: -1rem;
  }
  &__button:hover &__icon:after {
    top: 1rem;
  }

  &__checkbox:checked + &__button &__icon {
    // make the middle one invisible
    background-color: transparent;
  }

  &__checkbox:checked + &__button &__icon::before {
    top: 0;
    transform: rotate(135deg);
  }

  &__checkbox:checked + &__button &__icon::after {
    top: 0;
    transform: rotate(-135deg);
  }
}

I skipped navigation class here as it’s explained in different post. Please refer to the post here for navigation logic.

As you can see in the html code, there is only 1 icon element in the button. How can there be three lines? Take a look at the line 20-27. 3 selectors are chosen – before, the element, after – and each selector make a line in the button. Please note that content, display properties are mandatory for pseudo-element usage.

Now, let’s see how you can make the animation when hover. There is a couple of steps for this.

Make a space between the lines when hover

Take a look at lines 41 – 46 in the CSS code. You need to choose before, after when hover happens and adjust the position accordingly. Don’t forget to have transition property to make it animation in line 34.

Animating the lines to form X

Take a look at lines 48 – 61 in the CSS code. What you need to do is the followings.
1. hide the middle one (background-color: transparent)
2. re-position the top and bottom (before, after pseudo elements) and rotate in opposite directions. Here 135 degree is chosen (180 – 45)

The logic is pretty simple. We just need to carefully select the element – only when the checkbox is checked. In order to do that, sibling selector (+) is used then choose navigation__icon::before and after

CSS – Build Cool Forms

Today, we are going to build a cool form using CSS. I am specifically going to use followings.

1. solid color gradients
2. sibling selectors
3. ::input-placeholder pseudo-element
4. :focus, :invalid, placeholder-shown, :checked pseudo classes

Please note that I have learned this from the udemy course by Jonas Schmedtmann.

Let’s take a look at the final result first

Form

There is a couple of things to note here.

  • Animation from placeholder to label to indicate the field
  • CSS created radio button
  • cool shape with background image

Let’s take a look at the code now

HTML Code

<section class="section-book">
    <div class="row">
        <div class="book">
            <div class="book__form">
                <form action="#" class="form">
                    <div class="u-margin-bottom-medium">
                        <h2 class="heading-secondary">
                            Start booking now
                        </h2>
                    </div>

                    <div class="form__group">
                        <input type="text" class="form__input" placeholder="Full name" id="name" required>
                        <label for="name" class="form__label">Full name</label>
                    </div>

                    <div class="form__group">
                        <input type="email" class="form__input" placeholder="Email address" id="email" required>
                        <label for="email" class="form__label">Email address</label>
                    </div>

                    <div class="form__group u-margin-bottom-medium">
                        <div class="form__radio-group">
                            <input type="radio" class="form__radio-input" id="small" name="size">
                            <label for="small" class="form__radio-label">
                                <span class="form__radio-button"></span>
                                Small tour group
                            </label>
                        </div>

                        <div class="form__radio-group">
                            <input type="radio" class="form__radio-input" id="large" name="size">
                            <label for="large" class="form__radio-label">
                                <span class="form__radio-button"></span>
                                Large tour group
                            </label>
                        </div>
                    </div>

                    <div class="form__group">
                        <button class="btn btn--green">Next step →</button>
                    </div>
                </form>
            </div>
        </div>
    </div>
</section>

There are multiple form groups under form to click the form when label is clicked. It is a typical html forms you can find.

CSS Code

This is the CSS code. Please note that I used SCSS and there are some variables for colors and font size which could be replaced with normal values you would like. I am going to explain each class separately for better understanding

section-book and book

.section-book {
  padding: 15rem 0;
  background-image: linear-gradient(to right bottom, $color-primary-light, $color-primary-dark);
}

.book {
  background-image: linear-gradient(105deg,
    rgba($color-white, .9) 0%, 
    rgba($color-white, .9) 50%,
    transparent 50%), 
    url(../img/nat-10.jpg);
  background-size: cover;
  // this is also possible
  // background-size: 100%; 

  border-radius: 3px;
  box-shadow: 0 1.5rem 4rem rgba($color-black, .2);

  height: 50rem;

  &__form {
    width: 50%;
    padding: 6rem;
  }
}

As you can see in the example, there is a trapezoid shape (form) on top of the background image. You could use clip-path to make the shape. But you could also use another properties of linear-gradient. In linear-gradient, you can specify color at each percentage. And as you see in the code, white color at 50% and transparent at 50% will actually make the shape because it’s gradient – white and transparent will be mixed at 50%.

Form classes

.form {
  &__group:not(:last-child) {
    margin-bottom: 2rem;
  }

  &__input {
    font-size: 1.5rem;
    font-family: inherit;
    color: inherit;
    padding: 1.5rem 2rem;
    border-radius: 2px;
    background-color: rgba($color-white, .5);
    border: none;
    border-bottom: 3px solid transparent;
    width: 90%;
    display: block;
    transition: all .3s;

    &:focus {
      outline: none;
      box-shadow: 0 1rem 2rem rgba($color-black, .1);
      border-bottom: 3px solid $color-primary;
    }

    &:focus:invalid {
      border-bottom: 3px solid $color-secondary-dark;
    }

    &::-webkit-input-placeholder {
      color: $color-gray-dark-2;    
    }
  }

  &__label {
    font-size: 1.2rem;
    font-weight: 700;
    margin-left: 2rem;
    margin-top: .7rem;
    display: block;
    transition: all .3s;
  }

  // select when placeholder is shown in the input
  // adjacent sibling selector (only if it's adjacent. no other elements in the between allowed
  // + adjacent (order matters), ~ general
  &__input:placeholder-shown + &__label{
    opacity: 0;
    visibility: hidden;
    transform: translateY(-4rem);
  }

  &__radio-group {
    width: 49%;
    display: inline-block;
  }

  &__radio-input {
    display: none;
  }

  &__radio-label {
    font-size: $default_font-size;
    cursor: pointer;
    position: relative;
    padding-left: 4.5rem;
  }

  &__radio-button {
    height: 3rem;
    width: 3rem;
    border: 5px solid $color-primary;
    border-radius: 50%;
    display: inline-block;

    // position just need to be specified for child position absolute
    // it doesn't need to be relative
    position: absolute;
    left: 0;
    top: -.4;

    // content, display property is mandatory for all pseudo element    
    &::after {
      content: "";
      display: block;
      height: 1.3rem;
      width: 1.3rem;
      border-radius: 50%;
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      background-color: $color-primary;
      opacity: 0;
      transition: opacity .2s;
    }
  }

  // as soon as selected, radio input checked will be on. then this selector will be enabled
  // radio-label (sibling) and then child ::after
  &__radio-input:checked ~ &__radio-label &__radio-button::after {
    opacity: 1;
  }
}

This is the nutshell of the form and let’s take a look some important classes.

form__input

Mainly it has all common css properties such as font-size, border, padding and so on. Things I would like to notes are pseudo classes such as :focus, :invalid, ::-webkit-input-placeholder.

:focus is selected when input is selected (focus). Since I didn’t want any default behavior, it is overriden that it has bottom outline with green color and box shadow

:invalid is selected when the input is evaluated to be invalid. Take a look at email address example in the video. Since I used input email type in the html, it will automatically evaluate as I type the letters. &:focus:invalid means I only want to show orange bottom color to indicate the error only if the input is focused and invalid

::-webkit-input-placeholder is to style placeholder text in the input.

form__label

The class itself is not that special if you take a look at it but I still want to go over since this is the place animation is happening.
Initially I want to hide the label but want to display via animation once the user types in the input. How do I do that?

&__input:placeholder-shown is the pseudo class when placeholder text in the input is shown – nothing is typed yet. I need to use the class because I want to animate the label only when input is focused and something is typing. For this, you can use a sibling selector.
form__label is a direct sibling of the input so the line &__input:placeholder-shown + &__label will select the label when nothing is in the input. Since I am hiding everything, I set opacity to 0, and visibility is hidden to make it completely gone.

Once the user types something, then &__input:placeholder-shown + &__label is not selected and label is the only effective one. That’s how the animation is happening. Please note that + is the one making sibling selector. It only looks for direct sibling which is very sensitive to the order of the elements.

How to build a css component for radio button?

You cannot style radio button directly. Then, how can I apply CSS style? You can hide the original button and create a CSS component to look like radio button. It’s okay to hide the original radio button because it will still be selected thanks to form-group and the matching label.

If you look at &__radio-button, it has all the styles to make it look like an outline button. I had to use absolute position to place the button nicely. Please note that parent class also needs to specify the position, either relative or absolute.

Now, how do I create another circle inside the button outline when it’s clicked? We can use :checked property.
&__radio-input:checked ~ &__radio-label &__radio-button::after means once radio input is selected :checked will be enabled then look for a sibling called form__radio-label which is just a sibling (+ direct sibling, ~ general sibling, you could have used + here but just wanted to show ~ too) then selects ::after pseudo elements. Please note that initial opacity is 0 for ::after but if clicked, opacity now is 1 which creates another circle.

CSS – video and shape-outside

In this post, I am going to show how to implement a webpage that plays a background video and let texts floats around the image.
Please note that I have learned this from the udemy course by Jonas Schmedtmann.
Let’s take a look at the example demo first.

Example Demo

As you can see in the above example, background video is playing. You will also notice that texts are floating around the image (circle shape) and image gets blurred when mouse hover over the image. It is actually pretty simple to do and let’s take a look at the code!

HTML Code

<section class="section-stories">
    <div class="bg-video">
        <video class="bg-video__content" autoplay muted loop>
            <source src="img/video.mp4" type="video/mp4">
            <source src="img/video.webm" type="video/webm">
            Your browser is not supported!
        </video>
    </div>
    <div class="u-center-text u-margin-bottom-big">
        <h2 class="heading-secondary">
            This is a test page
        </h2>
    </div>

    <div class="row">
        <div class="story">
            <figure class="story__shape">
                <img src="img/nat-8.jpg" alt="Person on a tour" class="story__img"/>
                <figcaption class="story__caption">Jade Moore</figcaption>
            </figure>
            <div class="story__text">
                <h3 class="heading-tertiary u-margin-bottom-small">This is great!</h3>
                <p>
                    Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eligendi iure itaque est obcaecati consequuntur perspiciatis hic quasi quisquam mollitia reiciendis ipsum fugit, sequi, minima accusantium earum nam ad. Voluptate, nulla!
                </p>
            </div>
        </div>
    </div>
    <div class="row">
        <div class="story">
            <figure class="story__shape">
                <img src="img/nat-9.jpg" alt="Person on a tour" class="story__img"/>
                <figcaption class="story__caption">Will Smith</figcaption>
            </figure>
            <div class="story__text">
                <h3 class="heading-tertiary u-margin-bottom-small">I like this!</h3>
                <p>
                    Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eligendi iure itaque est obcaecati consequuntur perspiciatis hic quasi quisquam mollitia reiciendis ipsum fugit, sequi, minima accusantium earum nam ad. Voluptate, nulla!
                </p>
            </div>
        </div>
    </div>
    <div class="u-center-text u-margin-top-huge">
        <a href="#" class="btn btn-text">Read all stories →</a>
    </div>
</section>

There are two main parts – div with bg-video class and two rows with story class.
Note that the reason I put multiple source in video tag is to assure video is loaded and played for all browsers. There is a couple of video properties worth to mention. autoplay makes video played automatically when the page is loaded. muted make sure it doesn’t make any sound (because it’s a background video). loop makes sure video replays forever.

CSS Code

.section-stories {
  padding: 15rem 0;
  position: relative;
}

.story {
  width: 75%;
  margin: 0 auto;
  box-shadow: 0 3rem 6rem rgba($color-black, .1);
  background-color: rgba($color-white, .6);
  border-radius: 3px;
  padding: 6rem;
  padding-left: 9rem;
  font-size: $default-font-size;
  transform: skewX(-12deg);

  &__shape {
    // has to be floated and width and height for circle
    width: 15rem;
    height: 15rem;
    float: left;
    // vectorized shape
    // 7.5 rem radius at exactly center (50%, 50%)
    // define where the contents floats around the symbol which is circle
    -webkit-shape-outside: circle(50% at 50% 50%);
    shape-outside: circle(50% at 50% 50%);

    -webkit-clip-path: circle(50% at 50% 50%);
    clip-path: circle(50% at 50% 50%);
    transform: translateX(-3rem) skewX(12deg);

    position: relative;
  }

  &__img {
    height: 100%;
    transform: translateX(-4rem) scale(1.4);
    backface-visibility: hidden;
    transition: all .5s;
  }

  &__text {
    transform: skewX(12deg);
  }

  &__caption {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, 0);
    color: $color-white;
    text-transform: uppercase;
    font-size: 1.7rem;
    text-align: center;
    opacity: 0;
    transition: all .5s;
    backface-visibility: hidden;
  }

  &:hover &__caption {
    opacity: 1;
    transform: translate(-50%, -50%);
  }

  &:hover &__img {
    transform: translateX(-4rem) scale(1);
    filter: blur(3px) brightness(80%);
  }
}

.bg-video {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  z-index: -1;
  opacity: .15;
  overflow: hidden;

  &__content {
    height: 100%;
    width: 100%;

    // element will fill the parent element while maintaining aspect ratio
    object-fit: cover;
  }
}

This is the entire SCSS code for the full picture. We will take a look at each class to see more details.

SCSS – story class

.story {
  width: 75%;
  margin: 0 auto;
  box-shadow: 0 3rem 6rem rgba($color-black, .1);
  background-color: rgba($color-white, .6);
  border-radius: 3px;
  padding: 6rem;
  padding-left: 9rem;
  font-size: $default-font-size;
  transform: skewX(-12deg);

  &__shape {
    // has to be floated and width and height for circle
    width: 15rem;
    height: 15rem;
    float: left;
    // vectorized shape
    // 7.5 rem radius at exactly center (50%, 50%)
    // define where the contents floats around the symbol which is circle
    -webkit-shape-outside: circle(50% at 50% 50%);
    shape-outside: circle(50% at 50% 50%);

    -webkit-clip-path: circle(50% at 50% 50%);
    clip-path: circle(50% at 50% 50%);
    transform: translateX(-3rem) skewX(12deg);

    position: relative;
  }

  &__img {
    height: 100%;
    transform: translateX(-4rem) scale(1.4);
    backface-visibility: hidden;
    transition: all .5s;
  }

  &__text {
    transform: skewX(12deg);
  }

  &__caption {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, 0);
    color: $color-white;
    text-transform: uppercase;
    font-size: 1.7rem;
    text-align: center;
    opacity: 0;
    transition: all .5s;
    backface-visibility: hidden;
  }

  &:hover &__caption {
    opacity: 1;
    transform: translate(-50%, -50%);
  }

  &:hover &__img {
    transform: translateX(-4rem) scale(1);
    filter: blur(3px) brightness(80%);
  }
}

This is the class with the largest code but if you loo closer it’s not that complicated. In parent story class, I used skewX to give a shape to the rectangle that surrounds the image and the text. If you just skew once, it will skew everything including the text and the image. That’s why I skewed back in each &__text and &__shape.

In order to have circle shape, we can use clip-path css property with circle value. Note that circle required width, height and floats properties. Then, you can use shape-outside css property of circle which will make texts next to the shape floats around the circle. Note that shape-outside itself won’t make circle shape. It just creates a virtual circle and make text floats. clip-path is the property to make it circle.

For blur, zoom-in effect when you hover, I just used opacity, filter, and transform property. Filter is the one that makes it darker and blur.

SCSS – bg-video class

.bg-video {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  z-index: -1;
  opacity: .15;
  overflow: hidden;

  &__content {
    height: 100%;
    width: 100%;

    // element will fill the parent element while maintaining aspect ratio
    object-fit: cover;
  }
}

Video is actually really simple. I used absolute position (note that position relative is used in section-stories class to anchor the video) to make it a background. z-index=-1 so that it doesn’t hide any components such as stories and button.

It is worthwhile to explain object-fit css property. object-fit property will make the element fill the parent element while maintaining aspect ratio. If you comment out the property, you will see the size of the video doesn’t always match the parent element. Controlling with widht/height will be hard because of aspect ratio. However, with object-fit property, it will automagically fill the parent element and maintain the aspect ratio. Here I used cover value but there are other properties you can use such as fill.

SCSS – Flipping Card Component

Today, I am going to implement a CSS card that you can flip round when you hover the mouse using SCSS.
Please note that I have learned this from the udemy course by Jonas Schmedtmann.
Main CSS properties I use are perspective, backface-visibility, background-blend-mode.
Let’s jump to the code directly and explain each part

Example

HTML Code

<div class="card">
    <div class="card__side card__side--front">
        <div class="card__picture">
             
        </div>
        <h4 class="card__heading">
            <span class="card__heading-span">
                Front Side
            </span>
        </h4>
        <div class="card__details">
            <ul>
                <li>Lorem ipsum dolor sit amet consectetur adipisicing elit. Amet nemo, qui cum excepturi repudiandae dolorum, ex necessitatibus iusto quo vitae maxime itaque ad accusamus. Vero eveniet quam ipsam nihil? Quibusdam!</li>
            </ul>
        </div>
    </div>
    <div class="card__side card__side--back">
        <div class="card__cta">
            <div class="card__price-box">
                <p class="card__price-value">Back Side</p>
            </div>
        </div>
    </div>
</div>

CSS Code

Both front/back card size will be children of card class. Since I use absolute position for the children classes we need to have relative position in the parent. The most important thing is perspective. As you can see in the video, we need perspective property to give flipping visual effect. I chose 150rem because a smaller number gives more dramatic flipping.

.card {
  // FUNCTIONALITY

  // perspective has to be in parent
  perspective: 150rem;
  -moz-perspective: 150rem;
  position: relative;
  height: 50rem;

  &__side {
    height: 50rem;
    transition: all .8s ease;
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;

    // hides the back part of the element
    backface-visibility: hidden;

    border-radius: 3px;
    overflow: hidden;
    box-shadow: 0 1.5rem 4rem rgba($color-black, .15);

    &--front {
      background-color: $color-white;
    }

    &--back {
      transform: rotateY(180deg);
      background-image: linear-gradient(
          to right bottom, 
          $color-secondary-light, 
          $color-secondary-dark);
    }
  }

  // when hover, I want .card__side--front to transform
  &:hover &__side--front {
    transform: rotateY(-180deg);
  }

  &:hover &__side--back { 
    transform: rotateY(0);
  }

  // FRONT SIDE STYLING
  &__picture {
    background-size: cover;
    height: 23rem;
    background-blend-mode: screen;
    -webkit-clip-path: polygon(0 0, 100% 0, 100% 85%, 0 100%);
    clip-path: polygon(0 0, 100% 0, 100% 85%, 0 100%);

    background-image: linear-gradient(to right bottom, $color-secondary-light, $color-secondary-dark),
      url(../img/nat-5.jpg);
  }

  &__heading {
    font-size: 2.8rem;
    font-weight: 300;
    text-transform: uppercase;
    text-align: right;
    color: $color-white;
    position: absolute;
    top: 12rem;
    right: 2rem;
    width: 75%;
  }

  &__heading-span {
    padding: 1rem 1.5rem;
    -webkit-box-decoration-break: clone;
    box-decoration-break: clone;
    
    background-image: linear-gradient(
        to right bottom, 
        rgba($color-secondary-light, .85), 
        rgba($color-secondary-dark, .85));
  }

  &__details {
    padding: 3rem;

    ul {
      list-style: none;
      width: 80%;
      margin: 0 auto;

      li {
        text-align: center;
        font-size: 1.5rem;
        padding: 1rem;

        &:not(:last-child) {
          border-bottom: 1px solid $color-grey-light-2;
        }
      }
    }
  }
}

This is a pretty long code and let’s take a look at each class.

CSS – card__side

This class represents a class of both front/back side. Let’s take a look at closer look.

  &__side {
    height: 50rem;
    transition: all .8s ease;
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;

    // hides the back part of the element
    backface-visibility: hidden;

    border-radius: 3px;
    overflow: hidden;
    box-shadow: 0 1.5rem 4rem rgba($color-black, .15);

    &--front {
      background-color: $color-white;
    }

    &--back {
      transform: rotateY(180deg);
      background-image: linear-gradient(
        to right bottom, 
        $color-secondary-light, 
        $color-secondary-dark);
    }
  }

// when hover, I want .card__side--front to transform
  &:hover &__side--front {
    transform: rotateY(-180deg);
  }

  &:hover &__side--back { 
    transform: rotateY(0);
  }

Since we have two sides and they need to be in the same position to flip, I used position absolute – top:0, left: 0. This is why I used position:relative in the parent card class.

Note that I put rotateY(180deg) on backside because they need to stay on 180 degree flipped while the mouse hovers over the component.

Note that on hover state I also specified &__side–front/back to transform the card whenever the mouse hover the card.

CSS – card__picture

// FRONT SIDE STYLING
  &__picture {
    background-size: cover;
    height: 23rem;
    background-blend-mode: screen;
    -webkit-clip-path: polygon(0 0, 100% 0, 100% 85%, 0 100%);
    clip-path: polygon(0 0, 100% 0, 100% 85%, 0 100%);

    background-image: linear-gradient(to right bottom, $color-secondary-light, $color-secondary-dark),
      url(../img/nat-5.jpg);
  }

It is not just an ordinary picture. It is actually blended to linear-gradient color I specified. The property for the blend is background-blend-mode. There are multiple blend mode you can explorer but I used screen here. I also used clip-path to clip the image to make it look nicer.

CSS – card__heading

  &__heading {
    font-size: 2.8rem;
    font-weight: 300;
    text-transform: uppercase;
    text-align: right;
    color: $color-white;
    position: absolute;
    top: 12rem;
    right: 2rem;
    width: 75%;
  }

  &__heading-span {
    padding: 1rem 1.5rem;
    -webkit-box-decoration-break: clone;
    box-decoration-break: clone;
    
    background-image: linear-gradient(
        to right bottom, 
        rgba($color-secondary-light, .85), 
        rgba($color-secondary-dark, .85));
  }

Heading is on top of the image. I used absolute position again in order to manually place the heading on the image.
I am going to skip explanation about. details as it’s pretty simple.

Now we have a nice looking and working flipping card component!