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.

Leave a Reply

Your email address will not be published.