Snap Guides

Alignment guide overlay — dashed vertical and horizontal lines that surface during a drag. Install Snap Guides from the VLLNT UI registry with the shadcn CLI.

Report a bug

Alignment guide overlay — dashed vertical and horizontal lines that surface during a drag. Part of the data-display 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 Snap Guides 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/snap-guides.json

Source

"use client"; import type { ComponentPropsWithoutRef } from "react"; import { cn } from "../../lib/utils"; /** * One alignment line. * * @public */ export type SnapGuide = | { /** Stable id (used as React key). */ id: string; /** Horizontal guide — runs left to right at this `y` in container px. */ orientation: "horizontal"; y: number; } | { /** Stable id (used as React key). */ id: string; /** Vertical guide — runs top to bottom at this `x` in container px. */ orientation: "vertical"; x: number; }; /** * Localizable strings. * * @public */ export type SnapGuidesLabels = { /** Aria-label for the layer. Defaults to `"Snap guides"`. */ region?: string; }; const DEFAULT_LABELS = { region: "Snap guides", } as const satisfies Required<SnapGuidesLabels>; /** * Props for {@link SnapGuides}. * * @public */ export type SnapGuidesProps = { /** Active alignment lines. Pass an empty array to hide all. */ guides: SnapGuide[]; /** Localizable strings. */ labels?: SnapGuidesLabels; } & ComponentPropsWithoutRef<"div">; /** * Alignment guide overlay for a canvas. Renders dashed lines at the * `x` / `y` coordinates supplied by the host snapper. Pure * presentation — the host computes which guides are active during a * drag and unmounts them on release. * * @example * ```tsx * <SnapGuides * guides={[ * { id: "x-200", orientation: "vertical", x: 200 }, * { id: "y-160", orientation: "horizontal", y: 160 }, * ]} * /> * ``` * * @public */ export const SnapGuides = ({ ref, ...props }: SnapGuidesProps & { ref?: React.Ref<HTMLDivElement> }) => { const { className, guides, labels, ...rest } = props; const resolvedLabels = { ...DEFAULT_LABELS, ...labels }; return ( <div aria-label={resolvedLabels.region} className={cn("pointer-events-none absolute inset-0 z-30", className)} data-snap-guide-count={guides.length} ref={ref} {...rest} > {guides.map((guide) => { const isVertical = guide.orientation === "vertical"; const offset = isVertical ? guide.x : guide.y; return ( <span aria-hidden="true" className={cn( "absolute border-primary/70", isVertical ? "inset-y-0 w-px border-l border-dashed" : "inset-x-0 h-px border-t border-dashed", )} data-snap-guide-id={guide.id} data-snap-orientation={guide.orientation} key={guide.id} style={ isVertical ? { left: `${offset.toString()}px` } : { top: `${offset.toString()}px` } } /> ); })} </div> ); }; SnapGuides.displayName = "SnapGuides";

Stories

Explore every variant and state in the interactive Storybook:

Preview

Switch between light and dark to inspect the embedded Storybook preview.

Dependencies

  • @vllnt/ui@^0.3.0