What is really possible beyond pointer and default
The cursor property can do more than switch between a handful of system icons: url() lets you embed custom cursor images, and multi-step fallback chains keep the interface understandable even when a graphic fails to load. Anyone using custom cursors needs to know exactly where they add real value and where they only hurt usability.
Table of Contents
- 1. The cursor property beyond the default values
- 2. Embedding custom cursor images with url()
- 3. Fallback chains for images that fail to load
- 4. Sensible use cases for custom cursors
- 5. Usability pitfalls with custom cursors
- 6. Performance and loading behavior of cursor images
- 7. Accessibility: custom cursors and keyboard users
- 8. Practical example: tool selection in an image editor
- 9. A checklist before shipping custom cursors
- 10. Summary
- 11. FAQ
1. The cursor property beyond the default values
Most developers only know cursor from keywords like pointer, default, text or not-allowed. These default values draw on icons the operating system ships with, which is why they look slightly different across Windows, macOS and Linux while still being understood identically everywhere in terms of function. That consistency makes them the right choice for everyday interaction hints.
The property can do a lot more, though: the url() function embeds a custom image as the cursor, optionally paired with coordinates for the hotspot, the exact click point inside the graphic. That opens the door to individual cursor designs, for example a magnifying glass icon while zooming a product image or a tool icon inside an editor, without needing JavaScript or an extra overlay element.
2. Embedding custom cursor images with url()
The syntax cursor: url(path) x y, fallback; allows up to two optional numbers right after the path, defining the hotspot in pixels measured from the top-left corner of the image. Without that information, the browser defaults the hotspot to the top-left corner of the graphic, which for symmetric icons like a magnifying glass usually does not match the intended click point in the center.
PNG, GIF and SVG are supported, and in most browsers so is the CUR format. For crisp rendering on high-resolution displays, a sufficiently large source graphic is recommended, because unlike background images there is no automatic scaling by pixel density for cursor images, so a too-small image appears blurry when magnified on retina displays.
/* Custom magnifying-glass cursor, hotspot centered at 16,16px */
.product-zoom {
cursor: url("/icons/zoom-cursor.png") 16 16, zoom-in;
}
/* SVG cursor with a pointer fallback */
.tool-eraser {
cursor: url("/icons/eraser.svg") 4 20, pointer;
}
3. Fallback chains for images that fail to load
The cursor property accepts a comma-separated list of values, and the browser checks each entry in order, stopping at the first one that works. If an image file cannot be loaded, for example because the path is wrong, a content security policy blocks the resource, or a slow connection interrupts the download, the browser automatically moves on to the next entry in the list.
That is why a real keyword should always sit at the end of any cursor declaration that uses a custom image, never a second image without a final fallback. A sensible chain usually consists of exactly two levels: the custom image as the intended result and a semantically matching system cursor as a safety net, such as zoom-in for a magnifying glass or grab for a hand icon.
/* Two image candidates, a keyword fallback is mandatory at the end */
.map-pan {
cursor:
url("/icons/pan-hand.svg") 12 12,
url("/icons/pan-hand.png") 12 12,
grab;
}
.map-pan:active {
cursor:
url("/icons/pan-hand-closed.svg") 12 12,
grabbing;
}
4. Sensible use cases for custom cursors
Custom cursors are best justified where they communicate an interaction that no standard cursor clearly represents, for example a drawing tool inside an image editor, an eraser mode, or a specific zoom behavior on a product image gallery. In these cases, the cursor effectively replaces a toolbar indicator and reduces cognitive load, because the current mode stays visible right at the mouse pointer.
Just as established is their use in map and whiteboard applications, where a hand icon visualizes panning (grab/grabbing), often paired with a custom graphic for a consistent look across browsers, since the system icons for grab vary more visually between operating systems than other cursor types.
5. Usability pitfalls with custom cursors
The biggest pitfall is a cursor image that is too large or too detailed, obscuring the actual click point and hurting targeting accuracy when clicking. Users unconsciously rely on the exact tip of the default arrow as a reference point, and a bulky custom icon without a cleanly set hotspot breaks that expectation, making precise clicking harder, especially with small target areas.
A second problem arises when a custom cursor overrides the meaning of a system cursor without offering the same clarity, for example a decorative icon in place of the clear not-allowed symbol on disabled elements. For status indicators that must be understood unambiguously, the built-in keywords are almost always the safer choice over a custom-designed image.
6. Performance and loading behavior of cursor images
Cursor images are handled by the browser like other CSS background images and typically only load once the associated selector actually applies, meaning the pointer moves over the matching element. With cursors that switch very frequently, for example rapidly moving between different tools, a noticeable switching delay can appear if the images are not already cached.
Preloading the most important cursor graphics via <link rel="preload" as="image"> or an invisible pre-render of the images in the DOM reliably prevents that delay. For small, simple cursor icons, an inline SVG encoded as a data URI is often the most robust solution too, since it needs no extra network request and the graphic is guaranteed to be available immediately.
7. Accessibility: custom cursors and keyboard users
Custom cursors are inherently a mouse-only design tool and provide no information whatsoever to keyboard users, touch devices or screen readers. Any function that only becomes apparent through a special cursor must therefore also be communicated through a visible UI element, an ARIA label, or another visual marker, so it is not reserved exclusively for mouse-bound users.
A practical test is to try operating the page entirely with the keyboard and check whether every function hinted at through the cursor remains understandable and reachable regardless. If that redundancy is missing, the custom cursor is purely additional feedback for mouse users and must never be the only source of information for an important interaction.
8. Practical example: tool selection in an image editor
In a browser-based image editor, the cursor typically signals the currently active tool: a brush icon in paint mode, a crosshair while selecting an area, an eraser icon in erase mode. Each tool class sets its own cursor declaration with a carefully adjusted hotspot, so the visible tip of the image lands exactly at the position where the action actually happens.
It is important that the tool selection also stays visible in a toolbar, so keyboard and touch users can recognize the active mode as well. The cursor then only provides the extra, mouse-based confirmation, not the only source of information.
/* Tool cursor per active mode, hotspot adjusted per icon */
.editor[data-tool="brush"] .canvas { cursor: url("/icons/brush.svg") 2 22, crosshair; }
.editor[data-tool="eraser"] .canvas { cursor: url("/icons/eraser.svg") 4 20, cell; }
.editor[data-tool="select"] .canvas { cursor: crosshair; }
.editor[data-tool="move"] .canvas { cursor: grab; }
.editor[data-tool="move"] .canvas:active { cursor: grabbing; }
9. A checklist before shipping custom cursors
Before rolling this out, a short, systematic check pays off: is the hotspot set correctly, does a keyword fallback exist at the end of the value list, does the function remain reachable for keyboard and touch users even without a visible cursor, and is the image file small enough to load without a noticeable delay.
Anyone who consistently works through these four points avoids the typical problems custom cursors otherwise bring: invisible images without a fallback, imprecise click points, mouse-only functions without an alternative, and noticeable loading delays with frequently switching tools.
| Value | Type | Hotspot needed | Typical use |
|---|---|---|---|
pointer |
Keyword | No | Clickable links and buttons |
url() ... , fallback |
Custom image + fallback | Recommended | Tools, magnifying glass, map pan |
grab / grabbing |
Keyword | No | Draggable map and whiteboard elements |
not-allowed |
Keyword | No | Disabled elements, clear status indicator |
none |
Keyword | No | Hide the cursor, e.g. for a custom overlay pointer |
Mironsoft
Modern CSS, layout architecture and rendering performance
CSS that stays maintainable instead of breaking with every change?
We review existing stylesheets for specificity chaos and layout thrashing, then build a CSS architecture with cascade layers, custom properties and modern layout primitives that still makes sense after the tenth feature.
CSS Audit
Systematically uncovering specificity issues, cascade conflicts and unused selectors.
Architecture Refactoring
Introducing cascade layers, custom properties and design tokens cleanly.
Performance Tuning
Fixing layout thrashing, expensive selectors and rendering bottlenecks.
10. Summary
cursor: custom values: The Essentials at a Glance
url() syntax
cursor: url(path) x y, fallback; allows a custom image with an optional hotspot, followed by a mandatory keyword fallback.
Fallback chains
Several image candidates are allowed, but the last value in the list must always be a real system keyword.
Sensible use
Tool modes, zoom, pan and other interactions without a clear standard cursor benefit the most.
Limits
Purely mouse-based, no value for keyboard and touch, so it must never be the only source of information for an important function.