# Hiding cursor with JS or CSS

**URL:** <https://forum.readymag.com/t/hiding-cursor-with-js-or-css/6053>\
**Category:** Custom code\
**Created:** [April 1, 2026, 2:09am UTC](https://forum.readymag.com/t/hiding-cursor-with-js-or-css/6053 "2026-04-01T02:09:09Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![smokebreakz](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.readymag.com/smokebreakz/32/4228_2.png) [@smokebreakz](https://forum.readymag.com/u/smokebreakz)\
**Post date:** [April 1, 2026, 2:09am UTC](https://forum.readymag.com/t/hiding-cursor-with-js-or-css/6053/1 "2026-04-01T02:09:09Z")

</div>

Hi! I have been trying to inject custom code into my Readymag site in order to make a short line of text the “cursor” instead of the default. I have exhausted everything I know to do to achieve this. I was able to effectively hide the default cursor in most scenarios, even as I navigate pages. Still, when the browser is in full-screen mode, something is overriding my JS, and the cursor reappears and stays on screen until I refresh or the mouse leaves the screen. This is the current code I am using:

AFTER :

`<div class="cursor-tagline" data-id="698805bcdc1b184afb0e6b3d">Constructing worlds for moving and still image.</div>`

 html, html \* { cursor: none !important; }

BEFORE :

```javascript
<script>
(function () {

  // ── Config ──────────────────────────────────────────────
  var TAGLINE_ID = "698805bcdc1b184afb0e6b3d";
  var OFFSET_X = 8; // px right of cursor
  var OFFSET_Y = 10; // px below cursor
  var IDLE_MS = 80; // ms before tagline fades out
  var OBSERVE_MAX = 15000; // stop watching DOM after 15s
  // ────────────────────────────────────────────────────────

  var selector = '[data-id="' + TAGLINE_ID + '"]';

  function init(el) {

    // Don't run twice on the same element
    if (!el || el.__cursorInit) return;
    el.__cursorInit = true;

    // Keep the tagline out of the normal document flow
    el.style.setProperty("position", "absolute", "important");

    // ── Stage: find the container that fills the viewport ──
    var stage = pickStage();
    var stageRect = stage.getBoundingClientRect(); // cached

    function pickStage() {
      var vw = window.innerWidth, vh = window.innerHeight;
      var node = el.parentElement;
      while (node && node !== document.body) {
        var r = node.getBoundingClientRect();
        if (r.width >= vw * 0.9 && r.height >= vh * 0.9) return node;
        node = node.parentElement;
      }
      return document.body;
    }

    // Refresh cached rect on resize (not every frame)
    window.addEventListener("resize", function () {
      stage = pickStage();
      stageRect = stage.getBoundingClientRect();
    }, { passive: true });

    // ── Cursor state ───────────────────────────────────────
    var cursorX = 0, cursorY = 0;
    var hasMoved = false;
    var lastMoved = performance.now();
    var rafId = null;

    // ── Animation loop (only runs when cursor is active) ───
    function frame() {
      var now = performance.now();

      if (hasMoved) {
        // Convert viewport coords → stage-local coords
        var x = (cursorX - stageRect.left) + OFFSET_X;
        var y = (cursorY - stageRect.top) + OFFSET_Y;

        el.style.setProperty("left", x + "px", "important");
        el.style.setProperty("top", y + "px", "important");

        el.classList.add("is-on");
        hasMoved = false;
      }

      // Fade out after idle threshold
      if (now - lastMoved > IDLE_MS) {
        el.classList.remove("is-on");
      }

      // Keep looping only while cursor is active;
      // stop after a short idle to save CPU/battery
      if (now - lastMoved < IDLE_MS + 2000) {
        rafId = requestAnimationFrame(frame);
      } else {
        rafId = null; // loop paused — restarts on next mousemove
      }
    }

    // ── Events ─────────────────────────────────────────────
    window.addEventListener("mousemove", function (e) {
      cursorX = e.clientX;
      cursorY = e.clientY;
      hasMoved = true;
      lastMoved = performance.now();

      // Restart the loop if it went idle
      if (!rafId) rafId = requestAnimationFrame(frame);
    }, { passive: true });

    window.addEventListener("mouseleave", function () {
      el.classList.remove("is-on");
    });

  } // end init()

  // ── Wait for the element to appear in the DOM ───────────
  // (ReadyMag injects elements dynamically, so we can't
  // assume the tagline exists at script-run time.)
  var obs = new MutationObserver(function () {
    var el = document.querySelector(selector);
    if (el) {
      init(el);
      obs.disconnect();
    }
  });

  obs.observe(document.documentElement, { childList: true, subtree: true });

  // Failsafe: stop observing after 15 seconds
  setTimeout(function () { obs.disconnect(); }, OBSERVE_MAX);

  // Also try immediately in case the element already exists
  var existing = document.querySelector(selector);
  if (existing) { init(existing); obs.disconnect(); }

})();
  
  // Force cursor back to hidden after every click
window.addEventListener("click", function () {
  // Hammer cursor hidden every 16ms for 2 seconds after any click
  // This covers the entire duration of Readymag's page transition
  var hammerId = null;
  var hammerCount = 0;
  var MAX = 120; // 120 × 16ms = ~2 seconds

  function hammer() {
    enforce();
    injectStyle();
    hammerCount++;
    if (hammerCount < MAX) {
      hammerId = setTimeout(hammer, 16);
    }
  }

  if (hammerId) clearTimeout(hammerId);
  hammerCount = 0;
  hammer();
}, true);
  
</script>

<script>
(function () {
  var style = document.createElement("style");
  style.textContent = [
    "html, html *, html *:hover,",
    "html *:active, html *:focus,",
    "html *:focus-visible,",
    ".mag, .mag *, .mag-pages-container,",
    ".mag-pages-container *, .page,",
    ".page *, .center-page, .center-page *,",
    ".prev-page, .prev-page *,",
    ".next-page, .next-page * {",
    " cursor: none !important;",
    "}"
  ].join("\n");

  function injectStyle() {
    if (!document.head.contains(style)) {
      document.head.appendChild(style);
    }
    // Stamp it directly on the html element as a fallback
    document.documentElement.setAttribute("style",
      (document.documentElement.getAttribute("style") || "") +
      "; cursor: none !important;"
    );
  }

  injectStyle();

  // Re-inject if Readymag removes it during a transition
  var obs = new MutationObserver(injectStyle);
  obs.observe(document.head, { childList: true });

  // Also watch the html element's own attributes
  var obsHtml = new MutationObserver(enforce);
  obsHtml.observe(document.documentElement, {
    attributes: true,
    attributeFilter: ["style", "class"]
  });

  // Hammer the body style directly on any interaction
  function enforce() {
    document.documentElement.style.setProperty("cursor", "none", "important");
    document.body.style.setProperty("cursor", "none", "important");
  }

  window.addEventListener("mousemove", enforce, { passive: true });
  window.addEventListener("mousedown", enforce, { passive: true });
  window.addEventListener("click", enforce, true);
  window.addEventListener("mouseup", enforce, true);

})();
</script>

```

CSS:

```css
/* ── Cursor-follow tagline ─────────────────────────────── */
[data-id="698805bcdc1b184afb0e6b3d"] {
  position: absolute !important;
  left: 0 !important;
  top: 0 !important;
  pointer-events: none !important;
  z-index: 999999 !important;
  opacity: 0 !important;
  mix-blend-mode: difference !important;
  transition: opacity 120ms linear !important;
  transform: none !important;
  will-change: left, top, opacity;
  font-family: "Times New Roman", Times, serif !important;
  font-size: 17pt !important;
  color: #ffffff !important;
}
[data-id="698805bcdc1b184afb0e6b3d"] * {
  color: #ffffff !important;
  font-family: "Times New Roman", Times, serif !important;
  font-size: 17pt !important;
}
[data-id="698805bcdc1b184afb0e6b3d"].is-on {
  opacity: 1 !important;
}

```

If anyone has suggestions it would be much appreciated. I’m not even sure this can be done haha

---

<div class="post-metadata">

**Author:** ![Charles-Arthur](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.readymag.com/charles-arthur/32/3745_2.png) [@Charles-Arthur](https://forum.readymag.com/u/Charles-Arthur)\
**Post date:** [April 1, 2026, 1:06pm UTC](https://forum.readymag.com/t/hiding-cursor-with-js-or-css/6053/2 "2026-04-01T13:06:40Z")

</div>

Looks like the full-screen mode is overriding your JS. Try continuously forcing `cursor: none !important` on `html` and `body` and using `pointer-events: none` on the tagline.
