<div class="demo">
  <main>
    <section class="heart-start">
      <div class="heart heart-outline">
      </div>
    </section>
    <section>
      <div class="heart heart-inside">
      </div>
    </section>
    <section>
    </section>
    <section>
      <span class="text">CSS</span>
    </section>
  </main>
  <div class="scroll-info">
    <span>Scroll</span>
    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
      <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 13.5L12 21m0 0l-7.5-7.5M12 21V3" />
    </svg>
  </div>
</div>
@import url('https://fonts.googleapis.com/css2?family=Mukta:wght@800&display=swap');
:root {
    --heart-left: linear-gradient(to right, #f221c1, #fa0e2d, #e00a49);
    --heart-right: linear-gradient(to left, #f221c1, #fa0e2d, #e00a49);
}

main {
  block-size: 100vh;
  scroll-snap-type: y mandatory;
  overscroll-behavior-y: contain;
  overflow-x: hidden;
  overflow-y: auto;
  scroll-timeline: --heartTimeline block;
  scroll-padding-block: 100px;
}

section {
  position: sticky;
  top: 0;
  display: flex;
  min-block-size: 100vh;
  scroll-snap-align: center;
  align-items: center;
  justify-content: center;
  z-index: 2;
}



.heart {
  position: relative;
  width: 24px;
  height: 24px;
  transform: scale(10);
}

.heart::before,
.heart::after {
  position: absolute;
  inset-inline: 0;
  inset-block: 0;
  content: "";
  content: "";
}

.heart::before {
  background: var(--heart-left);
  transform-origin: right bottom;
}

.heart::after {
  background: var(--heart-right);
  transform-origin: left bottom;
}

.heart-outline {
  clip-path: path(
    "M12 21.593c-5.63-5.539-11-10.297-11-14.402 0-3.791 3.068-5.191 5.281-5.191 1.312 0 4.151.501 5.719 4.457 1.59-3.968 4.464-4.447 5.726-4.447 2.54 0 5.274 1.621 5.274 5.181 0 4.069-5.136 8.625-11 14.402m5.726-20.583c-2.203 0-4.446 1.042-5.726 3.238-1.285-2.206-3.522-3.248-5.719-3.248-3.183 0-6.281 2.187-6.281 6.191 0 4.661 5.571 9.429 12 15.809 6.43-6.38 12-11.148 12-15.809 0-4.011-3.095-6.181-6.274-6.181"
  );

  animation-duration: 3s;
  animation-direction: alternate;
  animation-name: zoomHeart;
  animation-timeline: --heartTimeline ;
}

.heart-outline::before,
.heart-outline::after {
  animation-duration: 3s;
  animation-direction: alternate;
  animation-timeline: --heartTimeline ;
}

.heart-outline::before {
  transform: translateX(-40%) rotate(-90deg);
  animation-name: gradientInLeft;
}

.heart-outline::after {
  transform: translateX(40%) rotate(90deg);
  animation-name: gradientInRight;
}

.heart-inside {
  position: relative;
  width: 24px;
  height: 24px;
  clip-path: path(
    "M12 4.248c-3.148-5.402-12-3.825-12 2.944 0 4.661 5.571 9.427 12 15.808 6.43-6.381 12-11.147 12-15.808 0-6.792-8.875-8.306-12-2.944z"
  );
  transform: scale(10);
  opacity: 0;
  animation-duration: 3s;
  animation-direction: alternate;
  animation-name: fillHeart;
  animation-timeline: --heartTimeline ;
}

.heart-inside::before {
  transform: translateX(-50%) rotate(0);
}

.heart-inside::after {
  transform: translateX(50%) rotate(0);
}

.text {
  font-size: 3.5rem;
  animation-duration: 3s;
  animation-direction: alternate;
  animation-name: zoomText;
  animation-timeline: --heartTimeline ;
}

.scroll-info {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 12px;
  position: fixed;
  bottom: 20px;
  left: 50%;
  z-index: 10;
  transform: translateX(-50%);
  font-size: 2.3rem;
  color: #fff;
  animation-direction: alternate;
  animation-name: fadeOut;
  animation-timeline: --heartTimeline ;
  background: black;
}

.scroll-info svg {
  display: block;
  width: 26px;
  height: 26px;
  animation: bounce 2s infinite;
  color: #fff;
}

@keyframes gradientInLeft {
  from {
    transform: translateX(-40%) rotate(-90deg);
  }
  33%,
  100% {
    transform: translateX(-50%) rotate(0);
  }
}

@keyframes gradientInRight {
  from {
    transform: translateX(40%) rotate(90deg);
  }
  33%,
  100% {
    transform: translateX(50%) rotate(0);
  }
}

@keyframes fillHeart {
  from,
  33% {
    opacity: 0;
    transform: scale(6);
  }
  66%,
  100% {
    opacity: 1;
    transform: scale(10);
  }
}

@keyframes zoomHeart {
  from,
  33% {
    transform: scale(14);
  }
  66%,
  100% {
    transform: scale(10);
  }
}

@keyframes zoomText {
  from,
  66% {
    opacity: 0;
    color: black;
  }
  96% {
    color: black;
  }
  100% {
    opacity: 1;
    color: white;
  }
}

@keyframes fadeOut {
  from {
    bottom: 50%;
    opacity: 1;
  }
  33% {
    bottom: 20px;
    opacity: 1;
  }
  80% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    bottom: 20px;
  }
}

@keyframes bounce {
  0%,
  20%,
  50%,
  80%,
  100% {
    transform: translateY(0);
  }
  40% {
    transform: translateY(-30px);
  }
  60% {
    transform: translateY(-15px);
  }
}

/* General styles */

*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 0;
  font-family: "Mukta", sans-serif;
  font-weight: 800;
  color: black;
  background: black;
}

@supports not (animation-timeline: view()) {
  section, .scroll-info {
    display: none;    
  }
}
Run Pen

External CSS

  1. https://codepen.io/utilitybend/pen/oNabmYR.css

External JavaScript

  1. https://codepen.io/utilitybend/pen/oNabmYR.js