Selection Presence
Dashed-border overlay marking what another user has selected on the canvas. Install Selection Presence from the VLLNT UI registry with the shadcn CLI.
Dashed-border overlay marking what another user has selected on the canvas. Part of the utility family in VLLNT UI, it ships as a machine-readable registry entry — copy the source directly into your app with the shadcn CLI and own it, no runtime dependency on a component library.
Preview
Switch between light and dark to inspect the embedded Storybook preview.
Installation
Add Selection Presence to your project with the shadcn CLI. The source lands in your codebase, ready to adapt:
pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/selection-presence.jsonSource
"use client";
import type { ComponentPropsWithoutRef, ReactNode } from "react";
import { cn } from "../../lib/utils";
/**
* Localizable strings.
*
* @public
*/
export type SelectionPresenceLabels = {
/** Aria-label override. Defaults to `"Selection presence"`. */
region?: string;
};
const DEFAULT_LABELS = {
region: "Selection presence",
} as const satisfies Required<SelectionPresenceLabels>;
/**
* Props for {@link SelectionPresence}.
*
* @public
*/
export type SelectionPresenceProps = {
/** Tailwind / CSS color used for the border + chip. Defaults to `var(--foreground)`. */
color?: string;
/** Selection rectangle height in pixels. */
height: number;
/** Localizable strings. */
labels?: SelectionPresenceLabels;
/** Optional name shown in the corner chip (e.g. owner of the selection). */
name?: ReactNode;
/** Selection rectangle width in pixels. */
width: number;
/** Selection rectangle X (top-left) in canvas pixels. */
x: number;
/** Selection rectangle Y (top-left) in canvas pixels. */
y: number;
} & ComponentPropsWithoutRef<"div">;
/**
* Overlay marking what another user has selected on the canvas. The
* dashed border + soft fill stay calm so they communicate
* "someone-else's-selection" without blocking primary content. Pure
* presentation; the host computes the rect from the remote user's
* viewport and supplies an accent color per user.
*
* The wrapper is `pointer-events: none` so host gestures pass through.
*
* @example
* ```tsx
* <div className="relative h-screen w-screen">
* <Canvas />
* <SelectionPresence
* x={120} y={80} width={240} height={120}
* color="#5b8def"
* name="Bea"
* />
* </div>
* ```
*
* @public
*/
export const SelectionPresence = ({
ref,
...props
}: SelectionPresenceProps & { ref?: React.Ref<HTMLDivElement> }) => {
const { className, color, height, labels, name, width, x, y, ...rest } =
props;
const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
const accent = color ?? "var(--foreground)";
const ariaLabel =
typeof name === "string"
? `${resolvedLabels.region}: ${name}`
: resolvedLabels.region;
return (
<div
aria-label={ariaLabel}
className={cn(
"pointer-events-none absolute z-20 rounded-md border-2 border-dashed",
className,
)}
data-selection-presence
ref={ref}
role="img"
style={{
backgroundColor: `color-mix(in srgb, ${accent} 10%, transparent)`,
borderColor: accent,
height,
left: x,
top: y,
width,
}}
{...rest}
>
{name === null || name === undefined ? null : (
<span
className="absolute -top-5 left-0 inline-flex items-center rounded-md px-1.5 py-0.5 text-[10px] font-medium text-white shadow-sm"
data-selection-presence-chip
style={{ backgroundColor: accent }}
>
{name}
</span>
)}
</div>
);
};
SelectionPresence.displayName = "SelectionPresence";
Stories
Explore every variant and state in the interactive Storybook:
Preview
Switch between light and dark to inspect the embedded Storybook preview.