Goggles
Night-vision goggles that turn whatever they pass over green. They gain up over controls; a click flashes them.
npx shadcn@latest add https://hoverui.fun/ r/ goggles.jsonAdds components/cursors/goggles.tsx. The CLI also installs the useCursor hook it runs on, once, into your hooks folder. The files are yours from there.
Move through the preview, then hover the controls and press.Open this on a computer to move it around: it follows a mouse, not a finger.
- Kind
- Lenses
- Hook
- useCursor
- Dependencies
- None
- License
- MIT
How it behaves
Night-vision goggles that turn whatever they pass over green; over anything clickable they gain up and blink REC, and a press flashes them.
Usage
import { GogglesCursor } from "@/components/cursors/goggles";
export function Section() {
return (
<section>
<GogglesCursor />
{/* this section now wears Goggles */}
</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 any outline and the pointer dot through currentColor, e.g. text-pink-500. Keep blend modes and filters off it: either would cut the lens off from the page it sees.
- 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";
/** Any page, through a night-vision tube: drained of color, then pushed to phosphor green. */
const NIGHT = "grayscale(1) sepia(1) hue-rotate(70deg) saturate(6)";
/** Night-vision goggles that turn whatever they pass over green; over anything clickable they gain up and blink REC, and a press flashes them. */
export function GogglesCursor({ className = "" }: { className?: string }) {
const s = useRef({ x: 0, y: 0, t: 0, gain: 1, flash: 0, filter: "", wasDown: false });
const ref = useCursor((p, el) => {
const st = s.current;
st.t += p.dt;
st.x += (p.x - st.x) * p.ease(18);
st.y += (p.y - st.y) * p.ease(18);
st.gain += ((p.hover ? 1.6 : 1.05) - st.gain) * p.ease(8);
if (p.down && !st.wasDown && !p.reduced) st.flash = 1;
st.wasDown = p.down;
st.flash = Math.max(st.flash - p.dt * 4, 0);
const [goggles, dot] = el.children as HTMLCollectionOf<HTMLElement>;
goggles.style.transform = `translate(${st.x}px, ${st.y}px)`;
// Only touch the filter when it visibly changes: rebuilding a backdrop filter is not free.
const filter = `${NIGHT} brightness(${(st.gain + st.flash * 2).toFixed(2)})`;
if (filter !== st.filter) {
st.filter = filter;
goggles.querySelectorAll<HTMLElement>("[data-lens]").forEach((lens) => (lens.style.backdropFilter = filter));
}
(goggles.querySelector("[data-rec]") as HTMLElement).style.opacity = p.hover && (p.reduced || Math.floor(st.t * 2) % 2) ? "1" : "0";
dot.style.transform = `translate(${p.x}px, ${p.y}px)`;
});
// No blend mode or filter on the root: either would cut the lenses off from the page they see.
return (
<div ref={ref} aria-hidden className={`${cursorRoot} ${className}`}>
<div className="absolute flex -translate-1/2 items-center">
{[0, 1].map((i) => (
<div key={i} data-lens className="relative size-9 overflow-hidden rounded-full border-2 border-current" style={{ backdropFilter: `${NIGHT} brightness(1.05)` }}>
<div className="absolute inset-0 bg-[repeating-linear-gradient(0deg,rgb(0_0_0/0.18)_0_1px,transparent_1px_3px)]" />
</div>
))}
<div data-rec className="absolute -top-1 -right-1 size-1.5 rounded-full bg-[#ff3b30] opacity-0" />
</div>
<div className="absolute size-1 -translate-1/2 rounded-full bg-current" />
</div>
);
}
It runs on the useCursor hook, which has its own source and API.
More lenses cursors
- BlendInverts whatever is beneath it. Grows over anything clickable.
- GlassA lens of frosted glass that blurs whatever it passes over. Widens over anything clickable.
- FocusBlurs its area except a sharp circle at the pointer, like a shallow depth of field. Opens up over controls.
- MagnifierA magnifying glass that really enlarges the page under it. Closer over controls, closer still while you hold.
- DropletA water drop that refracts the page under it. Swells over controls; a click sends ripples through the page, bending its words.
- LiquidA drop of liquid glass that bends the page at its rim. Over controls it flows out to wrap them in a glass pill; press to squeeze it.
- GlitchThe page round the pointer splits into red, green and blue and tears sideways in fits, with no frame. It takes over controls and glitches harder; a click throws a burst.
Made a cursor of your own? Contributed cursors carry their maker's name on their page.
Contribute a cursor