Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/windows-98/llms.txt

Use this file to discover all available pages before exploring further.

CustomCursor replaces the browser’s native mouse pointer with a hand-crafted pixel-art SVG cursor that adapts to context and responds to clicks with a burst animation. It is a purely cosmetic, self-contained layer that adds to the Windows 98 desktop illusion without interfering with any interactive elements beneath it. All cursor elements are rendered with pointer-events-none so they never block clicks or hover states on the actual UI.

How it works

On mount, CustomCursor attaches two window event listeners inside a useEffect:
  • mousemove — Updates an { x, y } state position (sourced from event.clientX / event.clientY). A fixed div is translated to this position via transform: translate(Xpx, Ypx), keeping the SVG cursor anchored to the real pointer at all times. The component also inspects event.target on every move to determine whether the pointer mode cursor should be shown (see below).
  • click — Appends a new sparkle object { id: Date.now(), x, y } to a state array. Each sparkle is automatically removed after 400 ms via setTimeout, matching the length of its exit animation.
Both listeners are removed in the useEffect cleanup function to prevent memory leaks on unmount. The component also sets cursor: none globally on document.body (via a CSS rule in the project stylesheet) so the browser’s default arrow is hidden. CustomCursor itself renders as a fixed top-0 left-0 element at z-[10000], above all other UI layers.

Normal mode cursor

When the pointer is over a non-interactive element, a black pixel-art arrow SVG (24×24px viewBox) is displayed. The arrow shape is constructed from two <path> elements:
  • A black (#0a0a0a) outer fill forming the arrow silhouette.
  • A white (#ffffff) inner fill inset by one pixel, producing a bright pixel-art outline typical of 1990s desktop cursors.
The cursor div also has drop-shadow-md applied for subtle depth on lighter backgrounds.

Pointer mode cursor

The pointer mode activates when event.target matches any of the following conditions:
  • The element’s computed style has cursor: pointer.
  • The element’s tag name is <a> or <button>.
  • The element is a descendant of an <a> or <button> (checked via .closest()).
In pointer mode the arrow is swapped for a pink (#ec4899) pixel-art diamond/crosshair SVG (also 24×24px), with a white inner highlight, signalling that a click is available. This replaces the browser’s default hand cursor that would otherwise appear.

Click sparkle animation

Every click spawns a turquoise sparkle burst centered on the click coordinates. The sparkle is a 16×16px SVG built from five colored rectangles arranged as a four-point star:
      ▪▪          ← top arm   (retro-teal #5eead4)
   ▪▪▪▪▪▪▪▪      ← center    (cyan #00cfd1)
      ▪▪          ← bottom arm
▪▪              ▪▪ ← left/right arms
Each sparkle is a motion.div managed inside AnimatePresence. Its animation:
PropertyInitialAnimate
opacity10
scale0.51.5
yclick Y − 10pxclick Y − 30px
Duration0.4s, easeOut
Sparkles are positioned at fixed top-0 left-0 z-[9999] (one layer below the main cursor) so they always appear above page content but below the cursor itself. Multiple sparkles can exist simultaneously if the user clicks in rapid succession.

Touch device fallback

After mounting and attaching its event listeners, CustomCursor evaluates:
window.matchMedia('(hover: none)').matches
If true — meaning the primary input device does not support hover, as is the case on touchscreens — the component returns null and renders nothing. The browser’s default touch interaction is left completely untouched.

Usage

CustomCursor accepts no props. Add it once at the application root alongside Taskbar so it covers all pages:
import { C as CustomCursor } from './components/CustomCursor';
import Taskbar from './components/Taskbar';
import { Outlet } from 'react-router-dom';

export default function App() {
  return (
    <>
      <CustomCursor />
      <Outlet />
      <Taskbar />
    </>
  );
}
Make sure your global CSS includes * { cursor: none !important; } or targets body { cursor: none; } so the browser’s native cursor is fully hidden on desktop. Without this, both cursors will be visible simultaneously.

VisitorCounter

The LED counter in the system tray, exported from VisitorCounter.js.

Taskbar

Add both CustomCursor and Taskbar at the app root for the full Win98 feel.

Architecture: Styling

How the retro color palette (turquoise, magenta, navy) is defined.

Getting Started

Setting up the project from scratch.

Build docs developers (and LLMs) love