#app
View Compiled
@use "sass:math";

@import url("https://fonts.googleapis.com/css?family=Open+Sans:400,400i,700");

:root {
  --font-size: 16px;
  @for $i from 1 through 16 {
    --n-#{$i}: #{0.25 * $i}rem;
  }
  --button-color: #fff059;
  --button-color-light: #fff58a;
  --button-color-dark: #ffed24;
}

#app {
  color: #ffffff;
  font-family: "Open Sans", sans-serif;
  font-size: var(--font-size);
  background-image: linear-gradient(45deg, #fafafa, #e5e5e5);
  width: 100vw;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
}

.sub {
  --width: calc(100vw - var(--n-16));
  background-image: linear-gradient(20deg, #3084ff, #1dbdff);
  width: var(--width);
  height: var(--height);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  position: relative;
  border-radius: var(--n-4);
  box-shadow: 0 var(--n-3) var(--n-5) rgba(#000000, 0.3);
  box-sizing: border-box;
}

.sub-text, .sub-form {
  width: 100%;
  box-sizing: border-box;
}

.sub-text {
  padding: var(--n-8);
  padding-bottom: var(--n-2);
  h2 {
    font-size: var(--n-8);
  }
  h2, p {
    margin: 0;
    margin-bottom: var(--n-4);
    &:last-child {
      margin-bottom: 0;
    }
  }
}

.sub-form {
  padding: var(--n-2) 0 var(--n-6);
  .field {
    padding: var(--n-2) var(--n-8);
    input[type="email"], a {
      font-family: inherit;
      font-size: inherit;
      width: 100%;
      padding: var(--n-3);
      border: 0;
      margin: 0;
      outline: none;
      border-radius: var(--n-2);
      box-sizing: border-box;
    }
    input[type="email"] {
      background-color: #ffffff;
    }
    a {
      color: #5c4400;
      text-align: center;
      text-decoration: none;
      background-color: var(--button-color);
      display: inline-block;
      transition: all 128ms ease-out;
      &:hover {
        background-color: var(--button-color-light);
      }
      &:active {
        background-color: var(--button-color-dark);
      }
      &:focus {
        box-shadow: 0 0 0 var(--n-1) rgba(#fff059, 0.6);
      }
      span {
        margin-left: var(--n-1);
      }
    }
  }
}

.sub-mask {
  background-image: linear-gradient(20deg, #009116, #48e30b);
  width: 100%;
  height: 100%;
  overflow: hidden;
  position: absolute;
  top: 0;
  left: 0;
  border-radius: 50%;
  transform: scale(0);
}

.sub-check-circle {
  fill: transparent;
  stroke: rgba(#ffffff, 0.5);
  stroke-width: 8;
  stroke-dasharray: 0 #{2 * math.$pi * 45};
  width: 60%;
  height: 60%;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) rotate(-90deg) scaleY(-1);
}

.sub-check {
  --width: 0%;
  --height: 0%;
  width: 30%;
  height: 20%;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) rotate(-55deg);
  span {
    background-color: rgba(#ffffff, 0.5);
    position: absolute;
    &:first-child {
      width: 16%;
      height: var(--height);
      top: 0;
      left: 0;
    }
    &:last-child {
      width: var(--width);
      height: 24%;
      bottom: 0;
      left: 16%;
    }
  }
}

@media screen and (min-width: 768px) {
  .sub {
    --width: 720px;
    width: var(--width);
    max-width: var(--width);
    max-height: var(--width);
    flex-direction: row;
  }

  .sub-text {
    flex-shrink: 1;
    padding-bottom: var(--n-8);
  }

  .sub-form {
    width: 360px;
    padding: var(--n-6) 0;
    flex-shrink: 0;
  }
}
View Compiled
import React, { useState, useRef } from "https://cdn.skypack.dev/react@17.0.1";
import ReactDOM from "https://cdn.skypack.dev/react-dom@17.0.1";
import gsap from "https://cdn.skypack.dev/gsap@3.6.1";

// GSAP timeline instance
const tl = gsap.timeline();

// Circumference of the circle of check mark
const CHECK_CIRC = 2 * Math.PI * 45;

/**
 * Subscribe interaction JSX element
 */
function SubInteraction() {
  // E-mail address value
  const [email, setEmail] = useState("");
  
  // If the element is animating
  const [isAnimating, setIsAnimating] = useState(false);
  
  // Subscribe element
  const subElm = useRef(null);
  
  // Subscribe mask element
  const subMaskElm = useRef(null);
  
  // Circle part of the subcribe element
  const subCheckCircleElm = useRef(null);
  
  // Check part of the subcribe element
  const subCheckElm = useRef(null);
  
  // Subscribe button click event
  const subscribeClick = (evt) => {
    if (isAnimating === true) {
      return;
    }
    
    setIsAnimating(true);
    
    // Main box to circle
    tl.to(subElm.current, {
      width: "75vmin",
      height: "75vmin",
      borderRadius: "50%",
      ease: "power4.out",
      duration: 0.5
    });
    
    // Green circle scale to normal
    tl.to(subMaskElm.current, {
      scale: 1,
      ease: "power4.out",
      duration: 0.5
    }, "-=0.5");
    
    // Draw a circle
    tl.to(subCheckCircleElm.current, {
      strokeDasharray: CHECK_CIRC + " " + CHECK_CIRC,
      ease: "power3.out",
      duration: 0.5
    });
    
    // Draw a check mark
    tl.to(subCheckElm.current, {
      "--height": "100%",
      ease: "power2.out",
      duration: 0.25
    }, "-=0.5");
    
    // Draw a check mark
    tl.to(subCheckElm.current, {
      "--width": "84%",
      ease: "power2.out",
      duration: 0.25
    }, "-=0.25");
    
    // Do nothing, just to delay the reset
    tl.to(subElm.current, {
      duration: 2.5,
      onComplete: () => {
        // Clear email
        setEmail("");
        
        // Reset animating status
        setIsAnimating(false);
        
        // Reset the animation to beginning
        tl.progress(0);
        tl.clear();
        
        // Unset the size of the box
        tl.set(subElm.current, {
          width: null,
          height: null,
        });
      }
    });
  }
  
  // Render JSX
  return (
    <div className="sub" ref={subElm}>
      <div className="sub-text">
        <h2>Great! Ain't it?</h2>
        <p>Do you want to hear from us more?</p>
      </div>
      <div className="sub-form">
        <div className="field">
          <input
            type="email"
            placeholder="Enter your e-mail address"
            value={email}
            onInput={evt => { setEmail(evt.target.value); }}
          />
        </div>
        <div className="field">
          <a
            href="#"
            role="button"
            onClick={subscribeClick}
          >
            <i className="far fa-bell"></i>
            <span>Subscribe now</span>
          </a>
        </div>
      </div>
      <div className="sub-mask" ref={subMaskElm}>
        <svg
          viewBox="0 0 100 100"
          className="sub-check-circle"
          ref={subCheckCircleElm}
        >
          <circle r="45" cx="50" cy="50"></circle>
        </svg>
        <div className="sub-check" ref={subCheckElm}>
          <span></span>
          <span></span>
        </div>
      </div>
    </div>
  );
}

ReactDOM.render(<SubInteraction />, document.querySelector("#app"));
View Compiled

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css

External JavaScript

This Pen doesn't use any external JavaScript resources.