1. Home
  2. /Blog
  3. /Basic Custom Cursor Using CSS Only (No JavaScript)
2026-01-153 min readLoading views...Frontend

Basic Custom Cursor Using CSS Only (No JavaScript)

A practical guide to building a basic custom cursor using pure CSS—no JavaScript involved—including plain CSS and Tailwind CSS approaches, with clear real-world limitations.

CSSFrontendTailwindCursorNo JavaScript

Basic Custom Cursor Using CSS Only (No JavaScript)

2026-01-153 min readFrontend
Table of contents
Core Concept: Custom Cursors in CSSCustom Cursor with an Image (CSS Only)1) Prepare the Cursor Asset2) Apply the Cursor in CSS3) Different Cursor on HoverMinimal Editorial-Style Cursor ExampleHonest Limitations of CSS-Only CursorsCustom Cursor with Tailwind CSS (CSS-Only)1) Using Arbitrary Values2) Recommended Tailwind Best PracticeAccessibility Considerations (Do Not Skip)When CSS-Only Is EnoughTry It YourselfConclusion

Before talking about animations, inertia, or fancy trailing effects, it’s important to understand the hard limits of custom cursors on the web:

CSS can change the cursor’s appearance, but not its behavior.

That means:

  • CSS cannot track mouse movement freely
  • CSS cannot create trailing or easing effects
  • CSS cannot build interactive or animated cursors

But CSS is perfect for:

  • replacing the default cursor icon
  • defining consistent hover states
  • lightweight branding
  • accessibility-safe customization

This article focuses strictly on basic, CSS-only custom cursors.

Try it live: View Demo | Download HTML (right-click → Save As)


Core Concept: Custom Cursors in CSS

CSS exposes a single property for this:

css
cursor: ...;

The value can be:

  • a built-in keyword (pointer, grab, etc.)
  • a custom image via url(...)
  • a fallback cursor

The simplest example:

css
body {
cursor: pointer;
}

This is rarely recommended, but it illustrates the idea.


Custom Cursor with an Image (CSS Only)

This is the most common and production-safe way to build a custom cursor using CSS.

1) Prepare the Cursor Asset

Recommended specs:

  • Format: png or svg
  • Ideal size: 32×32 px (max 128×128)
  • Transparent background

Example file name: cursor-dot.png


2) Apply the Cursor in CSS

css
body {
cursor:
url('/cursor-dot.png') 16 16,
default;
}

Explanation:

  • 16 16 defines the hotspot (click position)
  • default acts as a fallback

3) Different Cursor on Hover

css
a,
button {
cursor:
url('/cursor-hover.png') 16 16,
pointer;
}

This is pure CSS, zero JavaScript, and extremely stable in production.


Minimal Editorial-Style Cursor Example

css
body {
cursor:
url('/cursor-dot.svg') 8 8,
default;
}
a:hover,
button:hover {
cursor:
url('/cursor-ring.svg') 12 12,
pointer;
}

This style works well for:

  • blogs
  • landing pages
  • portfolios

Honest Limitations of CSS-Only Cursors

With CSS alone, you cannot:

❌ Create trailing effects
❌ Add easing or interpolation
❌ Follow real-time pointer position
❌ Build context-aware cursor behavior

With CSS alone, you can:

✅ Customize cursor visuals
✅ Define hover affordances
✅ Maintain visual consistency
✅ Avoid performance overhead

If you need behavior, JavaScript is not optional.


Custom Cursor with Tailwind CSS (CSS-Only)

Tailwind does not extend cursor capabilities—it only changes how you author styles.

1) Using Arbitrary Values

html
<div class="cursor-[url('/cursor-dot.png')_16_16,_default]">Content</div>

Hover state:

html
<a
class="cursor-[url('/cursor-dot.png')_16_16,_default]
hover:cursor-[url('/cursor-ring.png')_16_16,_pointer]"
>
Link
</a>

Note: underscores (_) represent spaces


2) Recommended Tailwind Best Practice

Define reusable cursor utilities instead of cluttering markup.

tailwind.config.js

js
module.exports = {
theme: {
extend: {
cursor: {
dot: "url('/cursor-dot.png') 16 16, default",
ring: "url('/cursor-ring.png') 16 16, pointer",
},
},
},
};

Usage

html
<body class="cursor-dot">
<a class="hover:cursor-ring">Link</a>
<button class="hover:cursor-ring">Button</button>
</body>

This approach is clean, reusable, and scalable.


Accessibility Considerations (Do Not Skip)

Always account for non-mouse input:

css
@media (pointer: coarse) {
body {
cursor: auto;
}
}

Avoid:

  • overly small cursors
  • incorrect hotspots
  • low-contrast designs

The cursor is a navigation tool—not decoration.


When CSS-Only Is Enough

CSS-only cursors make sense when:

  • you do not need animation
  • branding is minimal
  • the site is mostly static
  • performance risk must be zero

Use JavaScript when:

  • motion matters
  • interaction is contextual
  • cursor behavior changes dynamically

Try It Yourself

A complete, standalone HTML demo is available:

  • View Live Demo: See the CSS-only cursor in action
  • Download HTML File: Right-click and "Save As" to get the complete code

The demo file shows both plain CSS and data URI implementations. You can open it directly in your browser or use it as a reference for your own projects.


Conclusion

CSS-only custom cursors are simple, honest, and reliable.

They do one thing well: replace the cursor visually without introducing performance or maintenance risk. For many editorial sites and landing pages, this is exactly the right level of customization.

If you need more than this, that's not a limitation of CSS—it's a signal that JavaScript belongs in the solution.

Comments

No comments yet

Loading comments...

Table of contents
Core Concept: Custom Cursors in CSSCustom Cursor with an Image (CSS Only)1) Prepare the Cursor Asset2) Apply the Cursor in CSS3) Different Cursor on HoverMinimal Editorial-Style Cursor ExampleHonest Limitations of CSS-Only CursorsCustom Cursor with Tailwind CSS (CSS-Only)1) Using Arbitrary Values2) Recommended Tailwind Best PracticeAccessibility Considerations (Do Not Skip)When CSS-Only Is EnoughTry It YourselfConclusion
or search for other articles
Previous

Vim Shortcuts I Actually Use (Daily Cheatsheet)

2025-12-12Tools
Next

Building a Custom Cursor with Vanilla CSS & JavaScript (No React)

Frontend2026-01-15

Let's Talk.

LinkedInGitHubTwitter

© 2024 idnasirasira.

Designed & Engineered with ♥ in Jakarta.