Element UI is best viewed on desktop
/
/
/
/
Magnetic cursor CTA
Profile

Magnetic cursor CTA

PRO

Turn your team cards into an interactive experience with a custom cursor CTA.

Released

3 weeks ago

Category

Custom code

CSS

JS

Built with

Need help?

Custom css

The CSS to make the animation for this power up is included in the top level parent container Advanced > Custom CSS. No additional setup needed. Just copy the component and the styles come with it.

  /* Hide cursor inside each team card (desktop only) */
  @media (min-width: 1025px) {
  .team-card,
  .team-card * {
    cursor: none !important;
  }
}
/* =========================
   Cursor pill wrapper
========================= */
.eui-cursor-cta {
  position: fixed;
  top: 0;
  left: 0;
  transform: translate(-50%, -50%);  /* centered on the mouse */
  z-index: 999;              /* stays above everything */

  /* IMPORTANT: prevents flicker by stopping the pill stealing hover */
  pointer-events: none;

  opacity: 0;
  visibility: hidden;
  transition: opacity 160ms ease, visibility 160ms ease;
  font-family: "Figtree", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  font-weight: 600;
}

.eui-cursor-cta.is-active {
  opacity: 1;
  visibility: visible;
}


/* =========================
   Cursor pill styling
   This targets BOTH setups:
   - <div class="eui-cursor-inner"> ... </div>
   - OR <a> ... </a>

   Edit safely in the sections below.
========================= */
.eui-cursor-cta .eui-cursor-inner,
.eui-cursor-cta > a {
  display: inline-flex !important;
  align-items: center !important;
  gap: 10px !important;

  /* =========================
     EDIT: SIZING
  ========================= */
  padding: 12px 16px !important;
  border-radius: 999px !important;
  font-size: 14px !important;
  line-height: 1 !important;

  /* =========================
     EDIT: COLORS
  ========================= */
  background-color: rgba(11, 31, 58, 0.88) !important;  /* pill background */
  border: 2px solid rgba(20, 184, 166, 0.18) !important; /* pill border */
  color: #fff !important;

  /* =========================
     EDIT: EFFECTS (optional)
     - Remove blur if you don't want the glass look
  ========================= */
  backdrop-filter: blur(10px) !important;
  -webkit-backdrop-filter: blur(10px) !important;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45) !important;

  white-space: nowrap !important;
  opacity: 1 !important;
  background-image: none !important;
}


/* If the pill is an <a>, remove default link styles */
.eui-cursor-cta > a {
  text-decoration: none !important;
}


/* =========================
   Safety: prevent theme effects from breaking the pill
   (Usually leave this alone)
========================= */
.eui-cursor-cta,
.eui-cursor-cta * {
  mix-blend-mode: normal !important;
  filter: none !important;
}


/* =========================
   Tablet/mobile: disable the pill entirely
   (So you can use normal LinkedIn icons/links instead)
========================= */
@media (max-width: 1024px) {
  .eui-cursor-cta {
    display: none !important;
  }

  .team-card,
  .team-card * {
    cursor: auto !important;
  }
}


/* =========================
   Accessibility: reduced motion
========================= */
@media (prefers-reduced-motion: reduce) {
  .eui-cursor-cta {
    transition: none !important;
  }
}

Custom Javascript

JavaScript is loaded via a HTML widget. Don’t remove it – it powers the animation.

<script>
(function () {
  const isDesktop = window.matchMedia("(min-width: 1025px)").matches;
  const canHover = window.matchMedia("(hover: hover) and (pointer: fine)").matches;
  if (!isDesktop || !canHover) return;

  // Create the floating cursor pill (visual only)
  const cursor = document.createElement("div");
  cursor.className = "eui-cursor-cta";
  cursor.innerHTML = `
    <div class="eui-cursor-inner">
      <span>View LinkedIn Profile</span>
      <span aria-hidden="true">↗</span>
    </div>
  `;
  document.body.appendChild(cursor);

  let activeCard = null;
  let activeUrl = null;

  let lastX = 0;
  let lastY = 0;
  let rafId = null;

  function render() {
    cursor.style.left = lastX + "px";
    cursor.style.top = lastY + "px";
    rafId = null;
  }

  function onMove(e) {
    lastX = e.clientX;
    lastY = e.clientY;
    if (!rafId) rafId = requestAnimationFrame(render);

    // Determine what's under the cursor (ignores the pill because it's pointer-events:none)
    const el = document.elementFromPoint(e.clientX, e.clientY);
    const card = el && el.closest ? el.closest(".team-card") : null;

    if (!card) {
      deactivate();
      return;
    }

    activate(card);
  }

  function getCardLink(card) {
    const attrUrl = card.getAttribute("data-linkedin");
    if (attrUrl) return attrUrl;

    const a = card.querySelector('a[href]:not([href="#"]):not([href=""])');
    if (a && a.href) return a.href;

    if (card.tagName === "A" && card.href) return card.href;

    return null;
  }

  function activate(card) {
    if (activeCard === card) return;

    const url = getCardLink(card);
    if (!url) return;

    activeCard = card;
    activeUrl = url;

    cursor.classList.add("is-active");
  }

  function deactivate() {
    activeCard = null;
    activeUrl = null;
    cursor.classList.remove("is-active");
  }

  // Track mouse globally (desktop only)
  window.addEventListener("mousemove", onMove, { passive: true });

  // Click anywhere on the active card opens the link
  document.addEventListener("click", (e) => {
    if (!activeCard || !activeUrl) return;

    const clickedCard = e.target.closest && e.target.closest(".team-card");
    if (clickedCard !== activeCard) return;

    // Let normal links inside the card behave normally
    const clickedLink = e.target.closest && e.target.closest("a");
    if (clickedLink) return;

    window.open(activeUrl, "_blank", "noopener,noreferrer");
  });

  window.addEventListener("blur", deactivate);
})();
</script>
Copied to clipboard!