DEV Community

Cover image for Creating custom mouse cursors with CSS
Megan Lee for LogRocket

Posted on • Originally published at blog.logrocket.com

Creating custom mouse cursors with CSS

Written by Samson Omojola✏️

Editor’s note: This article was last updated by Njong Emy on 5 August 2024 to update content and code blocks, as well as to offer a brief comparison of CSS and JavaScript cursors to understand scenarios when you would want to use JavaScript.

Acting as the middleman between your users and your website, cursors can either limit or greatly enhance the way your users experience your site. This is why sleek, intentionally designed, custom cursors have become a significant part of UI and UX today.

Custom cursors are an opportunity to give your users direction in an engaging way and create a memorable, immersive experience for them on your website.

In this tutorial, we’ll take a look at what custom cursors are and learn how to use CSS (and JavaScript) to create custom cursors that will give your website a creative edge. To follow along with this tutorial, you should have some knowledge of HTML, CSS, and JavaScript.

Overview of cursors in CSS

We already interact with custom cursors every day. When you hover over buttons and the pointer cursor changes to a hand, or you hover over some text and the cursor changes to a text cursor, this interactivity is achieved through custom cursors.

However, there are many other creative experiences we can provide to our users with cursors. Before we dive into creating custom cursors, you should know that CSS provides you with cursors out of the box for some frequently performed tasks.

These cursors show you what can be done at the exact location you are hovering over. Examples include cursors indicating that you should click links, drag and drop elements, zoom in and out on things, and more.

All you have to do is specify the type of cursor you want using the CSS cursor property. Some of the available cursors in CSS include:

.auto            { cursor: auto; }
.default         { cursor: default; }
.none            { cursor: none; }
.context-menu    { cursor: context-menu; }
.help            { cursor: help; }
.pointer         { cursor: pointer; }
.progress        { cursor: progress; }
.wait            { cursor: wait; }
.cell            { cursor: cell; }
.crosshair       { cursor: crosshair; }
.text            { cursor: text; }
.vertical-text   { cursor: vertical-text; }
.alias           { cursor: alias; }
.copy            { cursor: copy; }
.move            { cursor: move; }
.no-drop         { cursor: no-drop; }
.not-allowed     { cursor: not-allowed; }
.all-scroll      { cursor: all-scroll; }
.col-resize      { cursor: col-resize; }
.row-resize      { cursor: row-resize; }
.n-resize        { cursor: n-resize; }
.e-resize        { cursor: e-resize; }
.s-resize        { cursor: s-resize; }
.w-resize        { cursor: w-resize; }
.ns-resize       { cursor: ns-resize; }
.ew-resize       { cursor: ew-resize; }
.ne-resize       { cursor: ne-resize; }
.nw-resize       { cursor: nw-resize; }
.se-resize       { cursor: se-resize; }
.sw-resize       { cursor: sw-resize; }
.nesw-resize     { cursor: nesw-resize; }
.nwse-resize     { cursor: nwse-resize; }
Enter fullscreen mode Exit fullscreen mode

Hover over the boxes below to see the cursors in action:

See the Pen Untitled by Samson Omojola (@Caesar222) on CodePen.

Check out the complete list of CSS cursors here.

While these cursors are useful and have some basic styling, we can certainly get more creative.

How to create a custom cursor with CSS

Creating a custom cursor with CSS is a pretty straightforward process. The first step is to find the image you want to use to replace the default cursor. You can either design one yourself or get a free PNG that suits your needs from an icon library such as FontAwesome.

Next, point the CSS cursor property to the location of the image using url. Now, the cursor property knows that it's meant to use whatever image is at that URL as its cursor:

body {
      cursor: url('path-to-image.png'), auto;
}
Enter fullscreen mode Exit fullscreen mode

To ensure that this cursor is used on all parts of your website, the best place to use the cursor property is in the body tag of your HTML. However, if you want, you can assign custom cursors to specific elements instead of the whole website.

You can also add a fallback value to your cursor property. When using custom CSS properties, this value ensures that if the image that serves as your custom property is missing or cannot be loaded, then your users will have another option.

In this case, auto is the fallback descriptor for your custom cursor property. Your users will see the regular cursor if the custom one is unavailable. You can also provide more than one custom cursor (multiple fallbacks) for your website. All you have to do is add their paths to the cursor property:

body {
      cursor: url('path-to-image.png'), url('path-to-image-2.svg'), url('path-to-image-3.jpeg'), auto;
}
Enter fullscreen mode Exit fullscreen mode

There are three fallback cursors in the code above.

Because they draw attention to elements you want to highlight on your website, custom cursors are best used in specific scenarios, such as:

  • Indication of special interactions
  • Storytelling purposes
  • Gaming interactions
  • In hover-effects

How to change a mouse cursor to a pointer

Say you have a table and you’d like the mouse cursor to change to a pointer (i.e., the hand icon) whenever a user hovers over a row in the table. You can use the CSS cursor property to achieve this.

Here’s an example:

<style>
  /* Style the table */
  table {
    font-family: arial, sans-serif;
    border-collapse: collapse;
    width: 100%;
  }

  /* Style the table cells */
  td, th {
    border: 1px solid #dddddd;
    text-align: left;
    padding: 8px;
  }

  /* Style the table rows */
  tr:hover {
    cursor: pointer;
  }
</style>

<table>
  <tr>
    <th>Name</th>
    <th>Age</th>
    <th>City</th>
  </tr>
  <tr>
    <td>John</td>
    <td>30</td>
    <td>New York</td>
  </tr>
  <tr>
    <td>Jane</td>
    <td>25</td>
    <td>Chicago</td>
  </tr>
  <tr>
    <td>Bill</td>
    <td>35</td>
    <td>Los Angeles</td>
  </tr>
</table>
Enter fullscreen mode Exit fullscreen mode

In the above code, we use the tr:hover selector to apply the cursor property to all table rows when the mouse hovers over them. The cursor property is set to pointer, which changes the mouse cursor to a hand icon.

How to hide a mouse cursor with CSS

To hide the mouse cursor with CSS, you can use the cursor property and set its value to none. Here’s an example:

<style>
  /* Style the body element */
  body {
    cursor: none;
  }
</style>

<body>
  <!-- Your content goes here -->
</body>
Enter fullscreen mode Exit fullscreen mode

This will hide the mouse cursor throughout the entire webpage. If you only want to hide the mouse cursor for a specific element, you can apply the cursor property to that individual element instead of the body element.

There are several situations in which hiding the mouse cursor might be useful, such as:

  • In a game or interactive application, hiding the mouse cursor could help create a more immersive experience for the user
  • In a presentation or slideshow, hiding the mouse cursor could reduce distractions and keep the focus on the content
  • In fullscreen video or media, hiding the mouse cursor could help prevent the user from accidentally clicking on controls or other elements

Remember that hiding the mouse cursor can be confusing or disorienting for some users, depending on the use case. This strategy should be used carefully and only when necessary.

CSS vs. JavaScript cursors

In as much as custom cursors can be created with CSS, using JavaScript also has its added advantages. Before we dive into how to create custom cursors with JavaScript, let's take a look at the advantages and disadvantages of creating custom cursors with CSS, and the same for JavaScript.

There are numerous reasons why it is preferable to create cursors with CSS:

  • Simplicity: With just a line of CSS, it is possible to toggle between numerous default types of cursors
  • Browser support: CSS custom cursors are fully supported by all browsers, so you don’t need any extra configurations to operate your websites on multiple browsers

The main disadvantage of using CSS for custom cursors is that adding animation or further customizing the cursor would involve more complexity. This is where JavaScript comes in. With JavaScript, you can add more complex interactions when the user interacts with the cursor, i.e., hovering, clicking, or moving over specific elements. By listening to specific events, the cursor's movements can then be updated. Cursors can also be more easily animated with JavaScript. With only CSS, creating complex cursor transitions may prove to be more difficult.

Just as simplicity and browser support are reasons why you should use CSS, complexity and cross-browser issues are reasons why you shouldn't use JavaScript.

How to create a custom cursor with JavaScript

Creating a custom cursor with JavaScript involves manipulating DOM elements. We'll create some DOM elements, which will serve as our custom cursor, and then use JavaScript to manipulate them. Then, as we move our cursor around, those custom elements will move around as our cursor.

Rather than design an image or download an image online, we will use CSS to design a custom cursor. I want us to use something animated that draws users in. Move your cursor around the box below to see an example of what I’m describing:

See the Pen Untitled by Samson Omojola (@Caesar222) on CodePen.

As you can see, the cursor consists of two elements — one large circle and one small one. We’ll create two div elements and give them class names:

<div class="cursor small"></div>
<div class="cursor big"><div>
Enter fullscreen mode Exit fullscreen mode

Next, we’ll create the circles with CSS. In the code below, we assign a width and height of 50px each to the big circle. To make it a circle, we give it a border radius of 50%.

The small circle will be hollow, so we give it a border and border-radius of 50%. Then, we assign it a width and height of 6px each.

We disable the default cursor by giving cursor a value of none so that we can render the custom cursor in its place.

To add animation to the big circle, we use @keyframes.

Our animation-duration is 2.0s. At the start of this duration, we set background-color to green and opacity to 0.2. Halfway through, we set the circle’s background-color to orange. At the end of the 2s duration, we set the circle’s background-color to red.

To make the animation repeat over and over again, we set animation-iteration-count to infinite:

body{
  background-color: #171717;
  cursor: none;
  height: 120vh;
}
.small{
  width: 6px;
  height: 6px;
  border: 2px solid #fff;
  border-radius: 50%;
}
.big{
  width: 50px;
  height: 50px;
  border-radius: 50%;
  margin-bottom: 20rem;
  animation-name: stretch;
            animation-duration: 2.0s;
            animation-timing-function: ease-out;
            animation-direction: alternate;
            animation-iteration-count: infinite;
            animation-play-state: running;
}

@keyframes stretch {
            0% {

                opacity: 0.2;
                background-color: green;
                border-radius: 100%;
            }

            50% {
                background-color: orange;
            }

            100% {
                background-color: red;
            }
        }
Enter fullscreen mode Exit fullscreen mode

Now, to make the elements move as you move your mouse, we’ll use JavaScript.

In the code below, we use an event listener to listen for whenever a user moves their mouse over our webpage. Whenever this event takes place, we use a function to get the x and y coordinates of the mouse. We then use the x and y coordinates to move our div elements around as a representative of our cursor:

const cursorSmall = document.querySelector('.small');
const cursorBig = document.querySelector('.big');

const positionElement = (e)=> {
  const mouseY = e.clientY;
  const mouseX = e.clientX;

  cursorSmall.style.transform = `translate3d(${mouseX}px, ${mouseY}px, 0)`;

  cursorBig.style.transform = `translate3d(${mouseX}px, ${mouseY}px, 0)`;

}

window.addEventListener('mousemove', positionElement)
Enter fullscreen mode Exit fullscreen mode

See the complete code alongside the interactive cursor in the below CodePen:

See the Pen Untitled by Samson Omojola (@Caesar222) on CodePen.

As you can see, we used querySelector to get access to the two div elements on the DOM. Then, we added an event listener to the webpage to listen for and detect when the user moves their mouse.

When this happens, the event listener triggers a positionElement function. In this function, we mapped these coordinates to the div elements whose DOM positions we have already selected.

We move the div elements in correspondence with the x and y coordinates of the mouse using transform and translate3d. transform repositions elements in horizontal and vertical directions, while translate3d repositions elements in 3D space.

As the mouse moves, its x and y values change, transform and translate3d get updated with the help of the event listener, and the position of each div changes in correspondence.

UX and browser compatibility when creating a custom cursor

When creating a custom cursor, you should always keep the user’s best interests in mind. One way to do this is by creating a cursor that best represents or translates the essence of an element. While custom cursors give your website a unique touch, it is important to not overdo it. Overcustomizing your cursor can frustrate and confuse your users by complicating their experience on your website.

Before you decide to create a custom cursor, you should consider its functionality. For example, will a custom cursor's features work well on an old browser?

Note that some older browsers can’t support many new CSS and JavaScript features. If you design certain areas of your website around a custom cursor that uses technologies beyond your users’ browsers, they won’t be able to engage with your website.

Creating a custom cursor with accessibility in mind

As mentioned previously, custom cursors can be helpful for users with impaired mobility and other accessibility requirements. For example, people with low vision may need very large mouse pointers that are easy to track or mouse pointers with high-contrast colors that make them stand out from various backgrounds.

A mouse can also be programmed to invert the colors of any elements it hovers over. This makes it easy for users to track what they’re reading or seeing on the screen. The text cursor or caret can also be thickened so that users with low vision don’t lose track of where the cursor is on the page while typing.

Large cursors can also benefit users with motor impairment, as they are easier to move and place on elements than small cursors.

Some users may prefer to keep the default cursor for familiarity. The use of media queries such as prefers-reduced-motion can be used to toggle or disable custom cursors for users who find them distracting:

@media
(prefers-reduced-motion: reduce) {
  *{
    cursor: auto; /*reverts to the default cursor */
  }
}
Enter fullscreen mode Exit fullscreen mode

Another important technique to improve accessibility is by disabling custom cursors for screen readers. These may interfere with screen readers and create confusion for the user. Adding aria-hidden = "true" to the elements that make use of the custom cursor, ensures that they are not exposed to assistive technologies like screen readers, thereby preventing confusion.

Conclusion

In this tutorial, we learned about CSS cursors available out of the box, how to create custom cursors with CSS, how to give your webpage multiple cursors, and how to use animation in CSS and JavaScript to create custom cursors. We’ve also seen the pros and cons of using CSS and JavaScript when handling custom cursors, and when to make use of cursors that are not the default.

Custom cursors can be a great way to draw your users in, keep them engaged, and direct them efficiently if implemented correctly. If you have any further thoughts or questions about creating custom cursors with CSS, let me know in the comment section.


Is your frontend hogging your users' CPU?

As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.

LogRocket Signup

LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.

Modernize how you debug web and mobile apps — start monitoring for free.

Top comments (0)