Context Cursor
ericts-ui/context-cursorFreeComponent
A scoped custom cursor for pointer-rich surfaces where each target can register its own label, icon, and interaction intent.
Live preview
live · rendered by the registry
Rendered by ericts-ui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://ui.ericts.com/r/context-cursor.jsonSource
1"use client";23import * as React from "react";4import {5 motion,6 useMotionValue,7 useReducedMotion,8 useSpring,9 type MotionValue,10 type SpringOptions,11} from "motion/react";1213import { cn } from "@/lib/utils";1415export type ContextCursorVariant = "default" | "open" | "drag" | "preview";16export type ContextCursorFollow = "instant" | "spring";1718export type ContextCursorState = {19 label: React.ReactNode;20 icon?: React.ReactNode;21 variant?: ContextCursorVariant;22};2324export type ContextCursorAnimation = {25 edgeFade?: boolean;26 edgeFadeDistance?: number;27 edgeFadeEasing?: (progress: number) => number;28 hideDelay?: number;29 opacity?: {30 hidden?: number;31 visible?: number;32 };33 scale?: {34 hidden?: number;35 visible?: number;36 };37 opacitySpring?: SpringOptions;38 scaleSpring?: SpringOptions;39};4041export type ContextCursorTargetAnimation = Omit<42 ContextCursorAnimation,43 "opacitySpring" | "scaleSpring"44>;4546type ContextCursorContextValue = {47 showCursor: (48 cursor: ContextCursorState,49 targetId: string,50 point?: CursorPoint,51 targetBounds?: DOMRectReadOnly,52 targetAnimation?: ContextCursorTargetAnimation,53 ) => void;54 hideCursor: (targetId?: string, point?: CursorPoint) => void;55 isDisabled: boolean;56};5758type CursorPoint = {59 x: number;60 y: number;61};6263type ResolvedContextCursorAnimation = {64 edgeFade: boolean;65 edgeFadeDistance: number;66 edgeFadeEasing: (progress: number) => number;67 hideDelay: number;68 hiddenOpacity: number;69 visibleOpacity: number;70 hiddenScale: number;71 visibleScale: number;72};7374const ContextCursorContext =75 React.createContext<ContextCursorContextValue | null>(null);7677const defaultSpring: SpringOptions = {78 mass: 0.1,79 stiffness: 320,80 damping: 26,81};8283const opacitySpring: SpringOptions = {84 mass: 0.1,85 stiffness: 480,86 damping: 24,87};8889const scaleSpring: SpringOptions = {90 mass: 0.12,91 stiffness: 420,92 damping: 22,93};9495const nativeCursorStyleId = "context-cursor-native-hidden";96const defaultEdgeFadeDistance = 40;97const defaultCursorHideDelay = 140;98const defaultHiddenOpacity = 0;99const defaultVisibleOpacity = 1;100const defaultHiddenScale = 0.4;101const defaultVisibleScale = 1;102// Once the edge-fade has shrunk the badge below this much progress it is103// effectively gone, so the native cursor is handed back at that exact point.104const nativeHandoffProgress = 0.08;105106const variantClassNames: Record<ContextCursorVariant, string> = {107// … truncated
What it pulls in
npm packages
Files it writes
More from ericts-ui
All 33 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Adaptive Draweradaptive-drawer | ericts-ui | Components | Free | 2 deps | |
| Check Animationcheck-animation | ericts-ui | Components | Free | no deps · 2 files | |
| Copy Buttoncopy-button | ericts-ui | Components | Free | 2 deps | |
| Expandable Dialogexpandable-modal | ericts-ui | Components | Free | 1 dep | |
| Expandable Tabsexpandable-tabs | ericts-ui | Components | Free | 1 dep | |
| Expandable Toolbarexpandable-toolbar | ericts-ui | Components | Free | 1 dep | |
| Expanding Panelexpanding-panel | ericts-ui | Components | Free | 2 deps | |
| Expanding Segmented Tabsexpanding-segmented-tabs | ericts-ui | Components | Free | 1 dep |
