Ralph J. Smit Laravel Software Engineer
Custom cursors and hover effects have been advancing steadily on the web for the last two years. It makes a website more sophisticated and gives a certain elegance to it. I've been playing with custom animated cursors and hover effects and it turns out that it's really easy to create a custom animated cursor – without much impact on page load and without huge JS libraries.
In this tutorial, I'll show you how to create a custom animated cursor and implement that on your website.
Overview
What do we need to do to display a custom cursor? First, we'll create the HTML markup for the cursor and style it with CSS. Then, we'll track the position of the cursor with a simple bit of JavaScript and position our custom cursor correctly. Finally, we'll hide the default cursor.
Creating the HTML & CSS markup
The HTML markup is very simple. We'll create a new element, with one child element, and give that a position absolute.
<div id="rjs_cursor" class="rjs-cursor"> <div class="rjs-cursor-icon"></div></div>
To style this, we have the following CSS:
.rjs-cursor { position: fixed; /* Fixed position.. */ top: 0; /* at the top left.. */ left: 0; z-index: 999999; /* above everything else. */ pointer-events: none; /* Cant't be clicked. */ transition: none; /* Cursor is always accurate, e.g. not delayed by a transition*/ opacity: 0; /* Hidden by default */} .rjs-cursor-icon { /* Styling for the visible part */ width: 12px; height: 12px; border-radius: 100%; /* Circle */ background-color: rgba(123, 123, 123, 0.7); /* Backup value */} /* Classes to show and hide the cursor */.rjs-cursor.rjs_cursor_visible { opacity: 1; }.rjs-cursor.rjs_cursor_hidden { opacity: 0; }
Positioning the custom cursor
We'll now use JavaScript to position the custom cursor. Our JavaScript is made up of several 'components'.
First, we'll get the cursor from the DOM and store the reference in a variable.
var rjs_cursor = document.getElementById("rjs_cursor"); //Getting the cursor
Then, we'll write a function that shows or hides the cursor. It adds/removes classes from the CSS in order to show or hide it. Sometimes it must be hidden, for example when the user opens the Browser Inspector. If you do not hide it, the custom cursor would remain visible at the side of the browser window.
function rjs_show_cursor(e) { //Function to show/hide the cursor if(rjs_cursor.classList.contains('rjs_cursor_hidden')) { rjs_cursor.classList.remove('rjs_cursor_hidden'); } rjs_cursor.classList.add('rjs_cursor_visible'); }
Then, we'll register a function that determines the position of the cursor.
function rjs_mousemove(e) { //Function to correctly position the cursor rjs_show_cursor(); //Toggle show/hide var rjs_cursor_width = rjs_cursor.offsetWidth * 0.5; //The actual cursor is in the centre of the custom cursor var rjs_cursor_height = rjs_cursor.offsetHeight * 0.5; var rjs_cursor_x = e.clientX - rjs_cursor_width; //x-coordinate var rjs_cursor_y = e.clientY - rjs_cursor_height; //y-coordinate var rjs_cursor_pos = `translate(${rjs_cursor_x}px, ${rjs_cursor_y}px)`; rjs_cursor.style.transform = rjs_cursor_pos;}
Last, we'll attach an eventlistener to the event mousemove
to call the positioning function.
window.addEventListener('mousemove', rjs_mouse); //Attach an event listener
<iframe id="cp_embed_poEQzMZ" src="//codepen.io/anon/embed/poEQzMZ?height=250&theme-id=1&slug-hash=poEQzMZ&default-tab=result" height="250" scrolling="no" frameborder="0" allowfullscreen allowpaymentrequest="" name="CodePen Embed poEQzMZ" title="CodePen Embed poEQzMZ" class="cp_embed_iframe" style="width:100%;overflow:hidden">CodePen Embed Fallback</iframe>
Showing and hiding the cursor at the correct times
To show and to hide the cursor at the correct time, I have two EventListener
s. The second EventListener
hides the mouse when it leaves the body element. The first EventListener
fires when the mouse moves.
When the mouse enters the viewport again, the mousemove
constantly fires, making sure that the cursor is visible at the correct time (by adding/removing CSS classes).
Hiding the default cursor
We can hide the default cursor with the following simple CSS:
* { cursor: none; }
But what about devices that don't have a cursor, like touch screens? To accommodate for that, we'll only hide the cursor on devices that have a pointing device which is also accurate (mouse, trackpad, etc.). We can do this with the CSS pointer media feature. This media query has one of three values:
-
none – the device doesn't have a pointing device.
-
coarse – the device has a pointing device of limited accuracy (things like gaming consoles or cars with which you can access the internet).
-
fine – the device has an accurate pointing device, like a mouse or a trackpad.
We only want to show our cursor on devices with an accurate pointing device, meaning that we'll only hide the default cursor on those devices.
@media (pointer: fine) { * { cursor: none; }}
But at the same time, we also need to hide our new cursor at those other devices. That's why I prefer to do this:
* { cursor: none; } @media (pointer: none), (pointer: coarse) { #rjs_cursor, #rjs_cursor .rjs-cursor-icon { display: none !important; visibility: hidden; opacity: 0; } * { cursor: auto !important; }}
And here we have our fully working custom cursor! 🥁
Why are the Codepen banners not working?
If you're viewing this page on a mobile device, the cursor will not be visible in the Codepen embed, because the Codepen acts like your browser. Thus, if you view this on a touch device, you should not see the cursor because it would be a redundant element without function.
Adding hover effects
Now that we have a fully functioning cursor, we can start to add hover effects.
//Hover behaviourfunction rjs_hover_cursor(e) { rjs_cursor.classList.add('rjs_cursor_hover'); }function rjs_unhover_cursor(e) { rjs_cursor.classList.remove('rjs_cursor_hover'); } document.querySelectorAll('a').forEach(item => { item.addEventListener('mouseover', rjs_hover_cursor); item.addEventListener('mouseleave', rjs_unhover_cursor);})
First, we'll register two functions. The first function adds a certain class to the rjs_cursor
variable. The second one removes the class. Now, what we're doing is basically adding an EventListener
to every link and to every item on the page that need to be hovered.
As you see, we'll select every link (every 'a'
). Then we'll add two EventListener
s. One addEventListener
listens to the moment that the cursor moves over the element ('mouseover'
). The other addEventListener
listens to the moment that the mouse leaves the element ('mouseleave'
), just like we did with the <body>
element.
.rjs-cursor-icon { /* Earlier styles */ transition: all 0.2s ease; /* Short & sweet animation */ transform-origin: 50% 50%;/* When we use the transform property to e.g. scale an animation, we'll do it from the center and not the top left */} .rjs-cursor.rjs_cursor_hover .rjs-cursor-icon { transform: scale(2);}
These simple CSS styles result in the following great effect:
Adding hover effects to clickable elements that are not <a>
The above example works great for <a>
elements. But on most websites, there will also be elements clickable that are not an <a>
. Think of <input>
elements and submit buttons in forms. Or an other element to open the mobile menu. To add the hover effect to them is really easy too.
For each (sort of) element you want the hover effect to appear on, add the following lines. You only need to change the parameter of the querySelectorAll
. You can just use normal CSS selectors. Repeat for each element you want the hover effect for.
document.querySelectorAll('input').forEach(item => { //Input tags item.addEventListener('mouseover', rjs_hover_cursor); item.addEventListener('mouseleave', rjs_unhover_cursor);}) document.querySelectorAll('button').forEach(item => { //Button tags item.addEventListener('mouseover', rjs_hover_cursor); item.addEventListener('mouseleave', rjs_unhover_cursor);}) document.querySelectorAll('.mycustomclass').forEach(item => { //A custom class item.addEventListener('mouseover', rjs_hover_cursor); item.addEventListener('mouseleave', rjs_unhover_cursor);})
Check out the below demo webpage, featuring the four sorts of elements I mentioned (<a>
, <input>
, <button>
& '.mycustomclass'
).
Examples of CSS cursor effects
Now that you have the framework for creating custom cursors, you can creating different variants on the cursor. For example changing the colors, the opacity, adding a border, etc. Below I created a few variants of the cursor.
Cursor with different color
Cursor with border
A custom cursor like this gives a website a whole new look and feel. I've used it on several websites and it's a great way to enhance a site. Let me know if you have used this on a website!
Published by Ralph J. Smit on in Guides . Last updated on 10 March 2022 .