CSS Cursors: Indicating Interactivity and State to Users
What is the CSS Cursor Property?
The cursor property changes the mouse pointer appearance when hovering over an element. Cursors provide important visual feedback, telling users what they can do with an element — click it, resize it, grab it, or that it's disabled.
.clickable {
cursor: pointer;
}Default Cursors
auto (Default)
The browser decides the cursor based on context:
.element {
cursor: auto;
}default
The platform's default cursor (usually an arrow):
.normal {
cursor: default;
}none
Hides the cursor completely:
.hidden-cursor {
cursor: none;
}
/* Use sparingly — hiding cursors can confuse users */Pointer and Selection Cursors
pointer
The hand cursor indicating a clickable element:
.button, .link, .clickable {
cursor: pointer;
}Use pointer for any non-link element that performs an action when clicked.
text
The I-beam cursor for selectable text:
.editable {
cursor: text;
}crosshair
A precision cursor for selection or targeting:
.color-picker {
cursor: crosshair;
}cell
For spreadsheet-like cell selection:
td {
cursor: cell;
}Status Cursors
wait
Indicates the application is busy:
.loading {
cursor: wait;
}progress
Shows activity but the user can still interact:
.processing {
cursor: progress;
}not-allowed
Indicates an action is not permitted:
.disabled {
cursor: not-allowed;
opacity: 0.5;
}
button:disabled {
cursor: not-allowed;
}no-drop
Shows that dragged item cannot be dropped here:
.invalid-drop-zone {
cursor: no-drop;
}Help and Context Cursors
help
Question mark cursor indicating help is available:
.has-tooltip {
cursor: help;
}
abbr[title] {
cursor: help;
text-decoration: underline dotted;
}context-menu
Indicates a context menu is available:
.right-clickable {
cursor: context-menu;
}Resize Cursors
For elements that can be resized:
.resize-horizontal { cursor: ew-resize; } /* ↔ East-West */
.resize-vertical { cursor: ns-resize; } /* ↕ North-South */
.resize-diagonal-1 { cursor: nwse-resize; } /* ↘ NW to SE */
.resize-diagonal-2 { cursor: nesw-resize; } /* ↙ NE to SW */
/* Generic resize */
.resize-corner { cursor: se-resize; } /* Southeast corner */
.resize-any { cursor: move; } /* Or use 'all-scroll' */Practical Resize Example
.resizable {
resize: both;
overflow: auto;
}
.resizable::-webkit-resizer {
cursor: se-resize;
}
/* Custom resize handle */
.resize-handle {
position: absolute;
bottom: 0;
right: 0;
width: 20px;
height: 20px;
cursor: nwse-resize;
}Drag Cursors
grab and grabbing
For draggable elements:
.draggable {
cursor: grab;
}
.draggable:active {
cursor: grabbing;
}
/* Drag and drop example */
.drag-item {
cursor: grab;
}
.drag-item.dragging {
cursor: grabbing;
}move
Indicates something can be moved:
.moveable {
cursor: move;
}Zoom Cursors
.zoom-in-area {
cursor: zoom-in;
}
.zoom-out-area {
cursor: zoom-out;
}
/* Image gallery zoom */
.gallery-image {
cursor: zoom-in;
}
.gallery-image.zoomed {
cursor: zoom-out;
}Custom Cursors
Use your own image as a cursor:
.custom {
cursor: url('cursor.png'), auto;
}
/* With coordinates for the hotspot (click point) */
.custom-hotspot {
cursor: url('cursor.png') 10 10, auto;
}
/* Multiple fallbacks */
.custom-fallbacks {
cursor: url('cursor.svg'), url('cursor.png'), pointer;
}Custom Cursor Best Practices
.custom-cursor {
/* SVG preferred for sharpness at any resolution */
cursor: url('cursor.svg') 12 12, auto;
}- Keep cursor images small (32x32 or smaller recommended)
- Always include a fallback keyword (pointer, auto, default)
- SVG cursors scale better than PNG
- Consider users with high-DPI displays
Complete Cursor Reference
/* General */
cursor: auto;
cursor: default;
cursor: none;
/* Links & Actions */
cursor: pointer;
cursor: context-menu;
/* Selection */
cursor: text;
cursor: vertical-text;
cursor: cell;
cursor: crosshair;
/* Status */
cursor: wait;
cursor: progress;
cursor: not-allowed;
cursor: no-drop;
/* Help */
cursor: help;
cursor: alias;
cursor: copy;
/* Dragging */
cursor: grab;
cursor: grabbing;
cursor: move;
cursor: all-scroll;
/* Resizing */
cursor: col-resize;
cursor: row-resize;
cursor: n-resize;
cursor: e-resize;
cursor: s-resize;
cursor: w-resize;
cursor: ne-resize;
cursor: nw-resize;
cursor: se-resize;
cursor: sw-resize;
cursor: ew-resize;
cursor: ns-resize;
cursor: nesw-resize;
cursor: nwse-resize;
/* Zooming */
cursor: zoom-in;
cursor: zoom-out;Common Patterns
Disabled Button
.btn:disabled,
.btn--disabled {
cursor: not-allowed;
opacity: 0.5;
pointer-events: none; /* Optional: also blocks clicks */
}Clickable Card
.card-clickable {
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
}
.card-clickable:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}Sortable List
.sortable-item {
cursor: grab;
}
.sortable-item:active,
.sortable-item.sorting {
cursor: grabbing;
}Interactive Map
.map-container {
cursor: grab;
}
.map-container:active {
cursor: grabbing;
}
.map-marker {
cursor: pointer;
}Accessibility Considerations
- pointer should only be used for clickable elements
- not-allowed helps indicate disabled state but also consider opacity and color changes
- Don't rely solely on cursor changes — provide visual styling too
- Custom cursors should have sufficient contrast
- Avoid hiding cursors except in specific cases (games, presentations)
Key Takeaways
- Use pointer for clickable non-link elements
- Use not-allowed for disabled elements
- Use grab/grabbing for drag-and-drop
- Use help for elements with tooltips or additional info
- Always provide a fallback for custom cursors
- Cursor changes should reinforce, not replace, visual styling
The cursor property enhances user experience by providing immediate visual feedback about element interactivity. This connects to the Styling Links article and broader accessibility principles.