Toggle bomb animation with delayed CSS variable

by AntowaKartowa

HTML

<style>
  @property --bomb-angle {
    syntax: "<angle>";
    inherits: true;
    initial-value: 0deg;
  }
  @property --delayed {
    syntax: "<angle>";
    inherits: false;
    initial-value: 0deg;
  }
  @property --fuse-angle {
    syntax: "<angle>";
    inherits: false;
    initial-value: 0deg;
  }

  .bomb {
    transition:
      --bomb-angle var(--parent-easing) var(--parent-duration);
    transform: rotate(var(--bomb-angle));
    transform-origin: 9px 15px;
    
    &.is-booped {
      --bomb-angle: 15deg;
    }
  }
  .fuse {
    transition:
      --delayed 0.2s ease-in,
      transform 0.1s ease-out;
    --delayed: var(--bomb-angle);
    --fuse-angle: clamp(-45deg, calc(var(--delayed) - var(--bomb-angle)) * 2, 45deg);
    transform: rotate(var(--fuse-angle));
    transform-origin: 18px 6px;
  }
</style>

<button class="wrapper">
  <svg viewBox="0 0 24 24">
    <g class="bomb">
      <path
        class="fuse"
        stroke="currentColor"
        stroke-linecap="round"
        stroke-linejoin="round"
        stroke-width="2"
        d="m20.468 1.037-.466 2.96 2.96-.468-2.96.472 1.361 2.671L20 4.003 17 7l2.998-3-2.67-1.36 2.671 1.357z"
      />
      
      <path
        stroke="currentColor"
        stroke-linecap="round"
        stroke-width="2"
        d="M13.444 8.347A8 8 0 1 0 15.93 11"
      />
      <path
        stroke="currentColor"
        stroke-linecap="round"
        stroke-linejoin="round"
        stroke-width="2"
        d="m12.359 7.649 1.06-1.061a2 2 0 0 1 2.829 0l1.414 1.414a2 2 0 0 1 0 2.829l-1.06 1.06"
      />
    </g>
  </svg>
</button>

CSS

html {
  /* Stiffness: 300. Damping: 10  */
  --parent-easing: linear(0, 0.011, 0.045 1.7%, 0.181 3.6%, 0.896 9.8%, 1.136 12.4%, 1.283 14.7%, 1.325, 1.347 17.2%, 1.351, 1.347, 1.334, 1.313 20.6%, 1.249 22.6%, 1.052 27.4%, 0.969 29.7%, 0.907 32.3%, 0.889, 0.879 35%, 0.878 36.7%, 0.89, 0.913 40.6%, 0.981 45.3%, 1.01 47.6%, 1.032, 1.042 52.7%, 1.043 54.5%, 1.039 56.5%, 0.997 65.4%, 0.989, 0.986 70.2% 74.4%, 1.001 83%, 1.005 87.3% 92%, 1);
  --parent-duration: 1.167s;
  
  /* Stiffness: 400. Damping: 10  */
  --child-easing: linear(0, 0.012, 0.049 1.1%, 0.2 2.3%, 0.982 6.2%, 1.234, 1.385 9.1%, 1.425, 1.443, 1.442, 1.429 11.7%, 1.363 13%, 1.004 17%, 0.892 18.6%, 0.829 20%, 0.811, 0.803 21.4% 21.9%, 0.809 22.5%, 0.838 23.8%, 0.998 27.9%, 1.048, 1.076, 1.087, 1.085, 1.072 34.6%, 1.001 38.7%, 0.979, 0.966, 0.961 42.9%, 0.962, 0.966 45%, 1.007 50.6%, 1.014, 1.017 53.6%, 1.015 55.8%, 0.997 61.4%, 0.992 64.4%, 1.003, 0.999 85.7%, 1);
  --child-duration: 1.5s;
}
body {
  height: 100vh;
  height: 100svh;
  display: grid;
  place-content: center;
  border: 1px solid hsl(210deg 15% 25%);
  border-radius: 4px;
  background: hsl(210deg 15% 6%);
  color: white;
  /*
    Don't add scrollbars if the rotation
    forces the element beyond the edge
    of the viewport:
  */
  overflow: hidden;
}
.wrapper {
  position: relative;
  width: 100px;
  height: 100px;
  background: transparent;
  border: none;
  color: white;
  cursor: pointer;
}
svg {
  display: block;
  width: 100%;
  height: 100%;
  fill: none;
  overflow: visible;
}

JavaScript

const wrapper = document.querySelector('button');
const bomb = document.querySelector('.bomb');

wrapper.addEventListener('mouseenter', () => {
  bomb.classList.add('is-booped');
  
  window.setTimeout(() => {
    bomb.classList.remove('is-booped');
  }, 150);
});