<h1>
  This demo requires <a href="https://caniuse.com/#feat=web-animation">a supporting browser.</a>
</h1>
<div class="modal">
  <div class="text">
    <h2>I am a modal!</h2>
    <p>Lorem ipsum dolor, sit amet consectetur adipisicing elit.</p>
  </div>
</div>

<button>Click Me</button>
.modal {
  background: #daeafa;
  border: 1px solid #33b4ff;
  padding: 1.5rem;
  width: 300px;
  border-radius: 20px;
  transform: scale(0);
  transform-origin: bottom;
}

.text {
  opacity: 0;
}

/* Etc */

h1 {
  font-size: 1.5rem;
}

h2 {
  font-size: 1.5rem;
  margin-bottom: 1rem;
}

body {
  font-family: system-ui, serif;
  display: grid;
  height: 100vh;
  place-items: center;
}
// Demo by Una Kravets & Kevin Ellis

const $ = (name) => {
  return document.querySelector(name);
};

const openModal = [
  { transform: "scaleY(0) scaleX(0)" },
  { transform: "scaleY(1) scaleX(0.01)", offset: 0.3, easing: "ease-in" },
  { transform: "scaleX(0.01)", offset: 0.3, easing: "ease-out" },
  { transform: "scaleX(1)", offset: 1 }
];

const openModalSettings = {
  duration: 1000, // 1s
  iterations: 1, // single iteration
  easing: "ease-out", // easing function
  fill: "both" // animation fill mode
};

let open = false;

$("button").addEventListener("click", () => {
  // Create two play-pending animations.
  let animations = [
    $(".modal").animate(openModal, openModalSettings),
    $(".text").animate({ opacity: [0, 1] }, openModalSettings)
  ];

  // Pause one of the animations and delay it's start until the
  // other is finished.
  if (open) {
    animations.forEach((anim) => {
      anim.currentTime = anim.effect.getTiming().duration;
      anim.reverse();
    });
    animations = animations.reverse();
  }

  // Run animations sequentially.
  animations[1].pause();
  animations[0].finished.then(() => {
    animations[1].play();
  });
  open = !open;
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.