Ring
Exact dot, lagging ring. Swells on links, squeezes on click.
npx shadcn@latest add https://hoverui.fun/ r/ ring.jsonAdds components/cursors/ring.tsx. The CLI also installs the useCursor hook it runs on, once, into your hooks folder. The files are yours from there.
- Kind
- Followers
- Hook
- useCursor
- Dependencies
- None
- License
- MIT
How it behaves
Exact dot + lagging ring that swells over interactive elements and squeezes on press.
Usage
import { RingCursor } from "@/components/cursors/ring";
export function Section() {
return (
<section>
<RingCursor />
{/* this section now wears Ring */}
</section>
);
}Put it inside <body> in your root layout to wear it on every page. Nested cursors take over inside their own element.
Props
- className
- Colors the cursor through currentColor, e.g. text-pink-500. To read on any background, pass text-white mix-blend-difference, as this site does.
- size
- The ring's diameter, in px. Default 32.
- speed
- How quickly the ring catches up with the dot; lower trails further behind. Default 12.
- swell
- How much the ring grows over anything clickable: 1.8 is 80% larger. Default 1.8.
- Hover targets
- a, button, input, select, textarea, label, [role=button], [data-cursor-label]
Source
"use client";
import { useRef } from "react";
import { cursorRoot, useCursor } from "@/hooks/use-cursor";
/** Exact dot + lagging ring that swells over interactive elements and squeezes on press. */
export function RingCursor({
className = "",
size = 32,
speed = 12,
swell = 1.8,
}: {
className?: string;
/** The ring's diameter, in px. */
size?: number;
/** How quickly the ring catches up with the dot; lower trails further behind. */
speed?: number;
/** How much the ring grows over anything clickable: 1.8 is 80% larger. */
swell?: number;
}) {
const ring = useRef({ x: 0, y: 0, scale: 1 });
const ref = useCursor((p, el) => {
const r = ring.current;
const [circle, dot] = el.children as HTMLCollectionOf<HTMLElement>;
r.x += (p.x - r.x) * p.ease(speed);
r.y += (p.y - r.y) * p.ease(speed);
r.scale += ((p.down ? 0.7 : p.hover ? swell : 1) - r.scale) * p.ease(16);
circle.style.transform = `translate(${r.x}px, ${r.y}px) scale(${r.scale})`;
dot.style.transform = `translate(${p.x}px, ${p.y}px)`;
});
return (
<div ref={ref} aria-hidden className={`${cursorRoot} ${className}`}>
<div className="absolute -translate-1/2 rounded-full border border-current" style={{ width: size, height: size }} />
<div className="absolute size-1.5 -translate-1/2 rounded-full bg-current" />
</div>
);
}
It runs on the useCursor hook, which has its own source and API.
More followers cursors
- TrailA tapering chain of dots, each chasing the one ahead. The head swells over anything clickable.
- MagnetA dot with a pull: over anything clickable, the control leans toward the pointer and the dot settles between them.
- RimlightA light that catches the edges of the controls near the pointer, brighter the closer they are. The hovered one lights all round; a click sends a ring of light out.
Made a cursor of your own? Contributed cursors carry their maker's name on their page.
Contribute a cursor