The cursor property sets the mouse pointer shown over an element, for example cursor: pointer for clickable things, not-allowed for disabled controls and grab for draggable items. Custom cursors use url() with an image, optional hotspot coordinates and a required keyword fallback. Hover each tile below to see all 36 standard values in your browser.
| State | CSS | Tailwind |
|---|---|---|
| Link or button | cursor: pointer | cursor-pointer |
| Disabled control | cursor: not-allowed | cursor-not-allowed |
| Can be dragged / dragging | cursor: grab / grabbing | cursor-grab / cursor-grabbing |
| Editable text | cursor: text | cursor-text |
| Resize a column | cursor: col-resize | cursor-col-resize |
| Busy but usable | cursor: progress | cursor-progress |
Updated
Cursor names are easy to guess wrong, and each operating system draws them a little differently. This gallery lets you hover every keyword in your own browser, then shows how to make a custom image cursor that still falls back gracefully, plus the matching opacity tricks for faded and disabled states.
| Value | Group | Meaning |
|---|---|---|
| auto | General | Browser decides from context |
| default | General | The platform arrow |
| none | General | No cursor drawn |
| context-menu | Links and status | A context menu is available |
| help | Links and status | Help is available |
| pointer | Links and status | A link or clickable control |
| progress | Links and status | Busy, but you can still interact |
| wait | Links and status | Busy, cannot interact yet |
| cell | Selection | A table cell or cells can be selected |
| crosshair | Selection | Precise two-dimensional selection |
| text | Selection | Text can be selected or edited |
| vertical-text | Selection | Vertical text can be selected |
| alias | Drag and drop | A shortcut or alias will be made |
| copy | Drag and drop | Something will be copied |
| move | Drag and drop | Something will be moved |
| no-drop | Drag and drop | Cannot drop here |
| not-allowed | Drag and drop | The action is not allowed |
| grab | Drag and drop | Can be grabbed and dragged |
| grabbing | Drag and drop | Currently being dragged |
| all-scroll | Resize and scroll | Can scroll in any direction |
| col-resize | Resize and scroll | Resize a column horizontally |
| row-resize | Resize and scroll | Resize a row vertically |
| n-resize | Resize and scroll | Move the top edge |
| e-resize | Resize and scroll | Move the right edge |
| s-resize | Resize and scroll | Move the bottom edge |
| w-resize | Resize and scroll | Move the left edge |
| ne-resize | Resize and scroll | Move the top right corner |
| nw-resize | Resize and scroll | Move the top left corner |
| se-resize | Resize and scroll | Move the bottom right corner |
| sw-resize | Resize and scroll | Move the bottom left corner |
| ew-resize | Resize and scroll | Resize left or right |
| ns-resize | Resize and scroll | Resize up or down |
| nesw-resize | Resize and scroll | Resize along the rising diagonal |
| nwse-resize | Resize and scroll | Resize along the falling diagonal |
| zoom-in | Zoom | Can zoom in |
| zoom-out | Zoom | Can zoom out |
The 12 12 hotspot puts the click at the ring's centre. crosshair is used if the image is not accepted.
<style>
.area { height: 110px; display: grid; place-items: center; background: #f1f5f9; border-radius: 12px;
cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Ccircle cx='12' cy='12' r='9' fill='none' stroke='%234F98FB' stroke-width='3'/%3E%3C/svg%3E") 12 12, crosshair; }
</style>
<div class="area">Move the mouse over this area</div>Hover each element. The draggable card switches to grabbing while the mouse button is held.
<style>
button { padding: 8px 14px; margin: 4px; cursor: pointer }
button:disabled { opacity: .5; cursor: not-allowed }
.card { display: inline-block; padding: 12px; margin: 4px; background: #dbeafe; border-radius: 10px; cursor: grab; user-select: none }
.card:active { cursor: grabbing }
.panel { resize: horizontal; overflow: auto; width: 180px; min-width: 120px; padding: 8px; border: 1px solid #94a3b8; margin-top: 8px }
.split { display: inline-block; width: 8px; height: 40px; background: #cbd5e1; cursor: col-resize; vertical-align: middle }
</style>
<button>Save</button><button disabled>Publish</button>
<div class="card">Drag me</div> <span class="split" title="Column divider"></span>
<div class="panel">Drag the corner to resize this panel</div>| Technique | Affects children? | Still clickable? | Animates smoothly? | Tailwind |
|---|---|---|---|---|
| opacity: 0.5 | Yes, everything inside | Yes | Yes | opacity-50 |
| visibility: hidden | Yes (children can opt back in) | No | Switches instantly | invisible |
| background: rgb(0 0 0 / 0.5) | No, only that layer | Yes | Yes | bg-black/50 |
| color: rgb(17 17 17 / 0.6) | No, only the text | Yes | Yes | text-black/60 |
The left banner fades its text along with the box. The right one keeps crisp text over a see-through background.
<style>
.bg { display: flex; gap: 10px; padding: 14px; background: linear-gradient(90deg, #f59e0b, #ec4899); border-radius: 12px }
.bg div { flex: 1; padding: 14px; color: #fff; font-weight: 600 }
</style>
<div class="bg">
<div style="background: #000; opacity: .5">opacity: .5</div>
<div style="background: rgb(0 0 0 / .5)">rgb(0 0 0 / .5)</div>
</div>
The cursor tells mouse users what will happen before they click. A pointer promises a click target, a text cursor promises selectable or editable text, and a resize arrow promises dragging. Showing a pointer on something that does nothing is a broken promise.
Keyboard, touch and screen reader users never see these hints. Interactive things still need to look interactive, have a visible focus style, and use real buttons and links so their role is announced.
A cursor image is fetched like any other image, so a slow or failed download leaves the fallback keyword in place. An inline data: SVG avoids the request entirely and stays crisp on high density screens.
The hotspot is the exact pixel that clicks. For an arrow it is the tip, usually near 0 0; for a crosshair or circle it is the centre. A wrong hotspot makes clicks land a few pixels away from where people aim.
For a disabled look, combine opacity around 0.5 with cursor: not-allowed and the disabled attribute. Check that the faded text still meets contrast requirements if people need to read it.
For hover reveals, animate opacity because it is cheap to render, and pair it with visibility so the element cannot be clicked while transparent. A colour with an alpha channel is the right tool when only one layer, such as a background or border, should be translucent.
Write cursor: url(cursor.svg) 4 4, auto; where the image is a small PNG, SVG or CUR file, the two numbers are the hotspot in pixels from the top left, and auto is the fallback keyword used if the image fails. You can list several url() images before the keyword.
not-allowed is the usual choice. Target it with button:disabled { cursor: not-allowed; opacity: 0.5; } and keep the real disabled attribute, so keyboard and screen reader users are told the control is unavailable, not just mouse users.
opacity fades the whole element, including children, and a fully transparent element still receives clicks and focus. visibility: hidden switches the element off for both pointer and assistive technology while keeping its space. Use opacity for the fade itself and switch visibility at the end so invisible items cannot be clicked.
opacity applies to everything inside the element. To make only the background see-through, use a translucent colour instead: background-color: rgb(0 0 0 / 0.5), or the older rgba(0, 0, 0, 0.5). The text stays fully opaque.
cursor: none hides it over the element, which games, kiosks and video players sometimes need. Use it sparingly on ordinary websites, because people lose track of where they are pointing.
Related: CSS transitions playground, CSS box model and display, Colour contrast checker, Website accessibility checklist, CSS selectors cheat sheet.
Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.
We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.