How to implement animated cursor into existing shopify theme

I have been trying for hours but can’t seem to get it to work, either my cursor entire disappears or the original cursor is there but only when hovering over clickable boxes… I tried multiple ways, but something keeps on overwriting or clashing with my code. anyone who can help?

I tried to seclude my code entirely from shopify’s codes by making a clean page and purely adding the cursor code in and there it works perfect… (unless you change layout from; none to theme → theme.liquid)

code on the clean page:

{% layout none %}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Firefly Cursor Test</title>
  <style>
    html, body {
      margin: 0;
      height: 100%;
      background: #111;
      cursor: none;
      overflow: hidden;
    }

    #cursor {
      position: fixed;
      width: 10px;
      height: 10px;
      background: white;
      border-radius: 50%;
      transform: translate(-50%, -50%);
      pointer-events: none;
      z-index: 9999;
    }

    .sparkle {
      position: fixed;
      width: 6px;
      height: 6px;
      background: rgba(255,255,255,0.8);
      border-radius: 50%;
      pointer-events: none;
      z-index: 9998;
      animation: sparkleFloat 1.8s ease-out forwards;
    }

    @keyframes sparkleFloat {
      0% {
        transform: translateY(0) scale(1);
        opacity: 1;
      }
      100% {
        transform: translateY(-40px) scale(0.3);
        opacity: 0;
      }
    }
  </style>
</head>
<body>
  <div id="cursor"></div>

  <script>
    const cursor = document.getElementById("cursor");
    let mouseX = window.innerWidth / 2;
    let mouseY = window.innerHeight / 2;

    document.addEventListener("mousemove", e => {
      mouseX = e.clientX;
      mouseY = e.clientY;
    });

    function renderCursor() {
      cursor.style.left = mouseX + "px";
      cursor.style.top = mouseY + "px";
      requestAnimationFrame(renderCursor);
    }

    renderCursor();

    function spawnSparkle() {
      const sparkle = document.createElement("div");
      sparkle.className = "sparkle";
      sparkle.style.left = (mouseX + (Math.random() * 20 - 10)) + "px";
      sparkle.style.top = (mouseY + (Math.random() * 20 - 10)) + "px";
      document.body.appendChild(sparkle);
      setTimeout(() => sparkle.remove(), 1800);
    }

    function sparkleLoop() {
      spawnSparkle();
      setTimeout(sparkleLoop, 100 + Math.random() * 100);
    }

    sparkleLoop();
  </script>
</body>
</html>

You are doing great. But I found typos in your code which I fix it in the updated code.

Replace the previous code with this one.


Let me know if you need more assistance.

Thanks

Hi @Eralys ,

Please send me the link that is not working, I will check it

Couple of points:

  1. I guess you may be using Dawn.
    Modern themes by Shopify has this CSS rule https://github.com/Shopify/dawn/blob/main/assets/base.css#L468-L482
div:empty,
section:empty,
article:empty,
p:empty,
h1:empty,
h2:empty,
h3:empty,
h4:empty,
h5:empty,
h6:empty {
  display: none;
}

which sets display: none; on empty element and yours are empty, so they are not shown.

So you need a rule to override this:

#cursor, .sparkle {
  display: block !important;
}
  1. You have
html, body {
       .. 
      cursor: none;
    }

but every interactive element has it’s own cursor and it overrides your rule.

So you should rather use something like

* {
  cursor: none !important;
}

If you implement these, your code will work.

See how it works in my test store (i’ve just pasted your code with my edits into “Custom Liquid” section in footer (removed body and html tags, of course))

https://3q1i7alo0l2asagx-23104437.shopifypreview.com

  1. It’s a good idea to still reflect in the cursor that it’s over interactive element, so this part of @TheScriptFlow code worth considering:
const interactiveElements = ['a', 'button', 'input', 'select', 'textarea', '[role="button"]'];
  interactiveElements.forEach(selector => {
    document.querySelectorAll(selector).forEach(el => {
      el.addEventListener('mouseenter', () => {
        cursor.style.transform = 'translate(-50%, -50%) scale(1.5)';
      });
      el.addEventListener('mouseleave', () => {
        cursor.style.transform = 'translate(-50%, -50%) scale(1)';
      });
    });
  });

However, I’d rather use just a pair of event listeners on the body:

const interactable = "a, summary, input, button, select, textarea, [role=button]";
document.body.addEventListener('mouseenter', (evt) => {
  if ( evt.target.matches(interactable)) {
    cursor.classList.add('interactive');
  }
}, { passive: true, capture: true});

document.body.addEventListener('mouseleave', (evt) => {
  if ( evt.target.matches(interactable)) {
    cursor.classList.remove('interactive');
  }
}, { passive: true, capture: true});

and add a rule like:

#cursor.interactive {
  background: red;
  transform: translate(-50%, -50%) scale(1.5);
}
1 Like

Could you show me the full code how it should look like, maybe, if that isn’t a hassle for you? I tried implementing my own code with the adjustments you provided and it still fails me, so I assume I made a slight error myself which my fried brain just can’t seem to find, seeing as it indeed works perfect on the link you provided…

Of course – taken straight from the “Custom Liquid” section:


1 Like

thank you for editing it for me,
My original code works perfect on an empty page so I hope my typos weren’t to bad :sweat_smile: (it’s been a while since I last coded websites as I am more into data now a days :rofl: )

I must add tho - that if I implement your code although it does make the cursor appear (which I originally struggled with. so, thank you for that) the sparkle effect doesn’t show up anymore and it’s very laggy, Read → it doesn’t directly show on the page and you need to move your mouse around for good 5-10 second before it appears.

Thanks allot! it works now :slightly_smiling_face:
Also thank you for the time for the extra explanation.
last time I did HTML, CSS and JS is long ago, my skills watered down allot apparently, luckily my understanding of code not entirely seeing as I do other languages haha, so the explanation was a nice refresher for me.

1 Like