Anchor Port

Port marker for object inputs, outputs, and bidirectional links on the canvas.

Report a bug

Preview

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

Installation

pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/anchor-port.json

Storybook

Explore all variants, controls, and accessibility checks in the interactive Storybook playground.

View in Storybook

Code

import { forwardRef } from "react"; import { cn } from "../../lib/utils"; export type AnchorPortProps = React.ComponentPropsWithoutRef<"span"> & { side?: "bottom" | "left" | "right" | "top"; state?: "active" | "blocked" | "idle"; tone?: "bidirectional" | "input" | "output"; }; const toneClasses: Record<NonNullable<AnchorPortProps["tone"]>, string> = { bidirectional: "border-violet-500/40 bg-violet-500/15 text-violet-700 dark:text-violet-300", input: "border-sky-500/40 bg-sky-500/15 text-sky-700 dark:text-sky-300", output: "border-emerald-500/40 bg-emerald-500/15 text-emerald-700 dark:text-emerald-300", }; const stateClasses: Record<NonNullable<AnchorPortProps["state"]>, string> = { active: "scale-100 opacity-100", blocked: "opacity-60 saturate-50", idle: "opacity-80", }; const sideClasses: Record<NonNullable<AnchorPortProps["side"]>, string> = { bottom: "self-end", left: "self-start", right: "self-end", top: "self-start", }; const AnchorPort = forwardRef<HTMLSpanElement, AnchorPortProps>( ( { className, side = "right", state = "idle", tone = "bidirectional", ...props }, ref, ) => ( <span aria-label={props["aria-label"] ?? `${tone} ${side} port ${state}`} className={cn( "inline-flex size-7 items-center justify-center rounded-full border shadow-sm", toneClasses[tone], stateClasses[state], sideClasses[side], className, )} data-side={side} data-state={state} data-tone={tone} ref={ref} role="img" {...props} > <span className="size-2.5 rounded-full bg-current" /> </span> ), ); AnchorPort.displayName = "AnchorPort"; export { AnchorPort };

Dependencies

  • @vllnt/ui@^0.2.1