Adding a custom cursor to a website with pure CSS
Ship a pixel-art cursor on your homepage in three lines of CSS. No JS, no framework, every browser.
60+ practical recipes for shipping custom cursors on the web, in game engines, and across operating systems. Every tutorial has copy-paste code and a clear “use this when” note.
Ship a pixel-art cursor on your homepage in three lines of CSS. No JS, no framework, every browser.
Scope your custom cursor to interactive elements so the rest of the site keeps the system pointer.
Eliminate the first-hover flash by inlining your SVG cursor as a data URI. Saves one HTTP request.
Browsers smooth small cursor images. Ship a 2x version with shape-rendering or pre-rasterized PNG to keep pixels sharp.
Stack SVG, PNG, and a system keyword in one rule so every browser gets the best cursor it can render.
Extend tailwind.config.js to register a named custom cursor and use it as a utility class.
A <CustomCursor src='...' hotspot={[8,4]}> wrapper that injects scoped cursor CSS for its children.
Register a custom Vue directive so any element can accept a cursor: <button v-cursor='hand'>.
Use Svelte's built-in style: directive or scoped <style> blocks to apply a custom cursor per component.
Drop your cursor PNG into /public, set body cursor in globals.css. Works in app router and pages router.
Spawn a small element on every mousemove and animate it out — the modern take on the early-2000s cursor trail.
Hide the OS cursor and draw your own at the mouse position inside requestAnimationFrame for full control.
Use requestPointerLock() to capture the cursor for FPS-style aiming. Standard in browser game engines.
Modern marketing-site effect: a custom cursor that gets pulled toward CTAs as you approach them. Pure JS.
Hide cursor: none everywhere, then move a fixed-positioned div on every mousemove. The modern 'designer cursor.'
Render multiple cursors over a shared Canvas — useful for collaborative whiteboards or co-op pixel art.
Use Input.set_custom_mouse_cursor() with a 16x16 Texture2D for pixel-perfect cursors that scale with the viewport.
Wrap your cursor frames in an AnimatedTexture and pass it to set_custom_mouse_cursor for a true frame-by-frame animated cursor.
The Godot 3 API is nearly identical to 4 but lives on Input rather than InputServer. Tested on 3.5.
Cursor.SetCursor with a Texture2D and a Vector2 hotspot. Works in editor and in standalone builds.
Swap the cursor on UI hover with EventSystem hooks for proper drag-and-drop game UI.
Cursor.lockState = CursorLockMode.Locked is the standard FPS pattern. Hide and unlock on pause.
Drive Cursor.SetCursor each frame from a sprite array to animate the pointer at any framerate.
Set the project's Default Cursor in Project Settings, or override with PlayerController->SetMouseCursorWidget at runtime.
Phaser exposes game.canvas.style.cursor — same CSS cursor URL syntax as the rest of the web.
Set the cursor on the renderer's canvas with CSS. For 3D 'in-world' cursors, raycast and place a sprite.
Use the .cursor property on any DisplayObject to apply a CSS cursor when hovered.
Defold ships a native cursor module. Set the cursor sprite per platform with sys.set_engine_arg.
love.mouse.newCursor() reads an ImageData and turns it into a system cursor with a hotspot.
SDL_CreateColorCursor takes an SDL_Surface plus a hotspot. Pair with SDL_SetCursor to apply.
raylib's HideCursor + manual DrawTextureV pattern is the simplest way to ship a custom cursor in a tiny C/C++ game.
PICO-8 has no system cursor. Hide the default with poke(0x5f2d, 1) and draw your own with spr() at stat(32), stat(33).
From silhouette to highlights in five passes. The standard pixel-art workflow for cursor design.
Use Aseprite's frame timeline + onion skin to build a smooth 8-frame busy cursor that loops cleanly.
Piskel runs in the browser with full pixel-art tooling. Perfect for designing a quick cursor without installing anything.
If you've drawn your animated cursor as a strip, ImageMagick or Aseprite can slice it for export to .ani / .cape / xcursor.
A pixel cursor that looks great on dark blue can vanish on a YouTube thumbnail. Test on three backgrounds before shipping.
If your cursor 'feels weird,' the hotspot is almost certainly off by one or two pixels. Here's how to fix it.
Figma is a vector tool, but with the right settings (no smoothing, integer scale) you can export pixel-perfect 16x16 cursors.
CC0 packs let you re-color and ship as your own. Workflow for honest re-skins.
SVG cursors can include filter='url(#glow)' for a subtle bloom on retina displays — a synthwave staple.
RealWorld Cursor Editor is the de-facto Windows tool. Open your PNG, set the hotspot, save as .cur. Repeat for animation frames to make .ani.
Mousecape's .cape format is a plist of base64-encoded PNGs per cursor type and per scale.
Build a folder of PNGs into an XCursor theme using xcursorgen and a config file mapping each cursor symlink.
Windows lets you replace any individual cursor. Useful for swapping just the link cursor without changing the pointer.
Hyprland reads HYPRCURSOR_THEME at startup; older XCursor themes work via XCURSOR_THEME.
GNOME no longer surfaces cursor selection in Settings. Use GNOME Tweaks or gsettings.
Plasma's cursor settings live in System Settings -> Colors & Themes -> Cursors.
ChromeOS does not support custom system cursors. Browser-only is your only option; here's how.
For CC BY packs, your attribution should name the author, link the source, and state the license. Here's a copy-paste template.
Use the contact page to submit a pack. Include all files, license, and a one-paragraph description.
How to organize a multi-platform cursor pack so installers find every file.
The install.inf is the file that registers a cursor scheme. It's plain text; here's the canonical template.
Trigger a build on every push that runs xcursorgen for Linux and packages all platforms into release artifacts.
Use CSS.supports('cursor', 'url(x.png) 0 0, auto') to confirm a browser will accept your URL syntax before applying it.
Custom cursors interact badly with high-contrast OS themes, pointer-magnification tools, and reduced motion. Here's the responsible default.
Use prefers-color-scheme to ship a light-mode and dark-mode variant of your custom cursor — same shape, different palette.
Use a small overlay div that displays the cursor coordinates while you tune the hotspot.
Stream capture cursors look smaller than they do on your monitor. Pick a 48px+ pack with a high-contrast outline.
Twelve themed cursors (bone, candle, pumpkin, skull, claw, etc.) in a tight orange/purple/black palette.