Separator
A gently wandering pen rule, horizontal or vertical.
Built on Base UI SeparatorBallpoint
Components drawn in blue ballpoint.
DocsComponentsSource
import { Separator } from "@/components/ui/separator";
export default function SeparatorDemo() {
return (
<div className="w-full max-w-sm">
<p className="text-lg font-bold">Ballpoint</p>
<p className="text-ink-3">Components drawn in blue ballpoint.</p>
<Separator className="my-4" seed="sep-h" />
<div className="flex h-6 items-center gap-4">
<span>Docs</span>
<Separator orientation="vertical" seed="sep-v1" />
<span>Components</span>
<Separator orientation="vertical" seed="sep-v2" />
<span>Source</span>
</div>
</div>
);
}Installation
pnpm dlx shadcn@latest add @ballpoint/ separatorOr copy the source
"use client";
import { useMemo } from "react";
import { Separator as SeparatorPrimitive } from "@base-ui/react/separator";
import { cn } from "@/lib/utils";
import { penStyle, useInkBox, useInkSeed, usePen, type Pen } from "@/hooks/use-ink-box";
import { InkSvg, Stroke } from "@/lib/ink";
import { ruleStroke, verticalStroke } from "@/lib/ink-sketch";
// Drawn long and stretched along its length only, so the line keeps its
// weight at any size and never needs redrawing as it resizes.
const LENGTH = 700;
function Separator({
className,
orientation = "horizontal",
seed,
draw,
weight,
speed,
...props
}: SeparatorPrimitive.Props & Pick<Pen, "draw" | "weight" | "speed"> & { seed?: string | number }) {
const pen = usePen({ draw, weight, speed });
const s = useInkSeed(seed);
const [ref] = useInkBox([LENGTH, 4]);
const horizontal = orientation === "horizontal";
const d = useMemo(() => (horizontal ? ruleStroke(s, LENGTH) : verticalStroke(s, LENGTH)), [horizontal, s]);
const mode = pen.draw ?? "auto";
return (
<SeparatorPrimitive
data-slot="separator"
orientation={orientation}
className={cn(
"relative shrink-0 text-ink-4 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px data-[orientation=vertical]:self-stretch",
className,
)}
{...props}
>
<InkSvg
ref={ref}
pending={mode === "auto"}
box={horizontal ? [0, -1, LENGTH, 5] : [0, -1, 3, LENGTH + 2]}
stretch
className={horizontal ? "inset-x-0 -top-0.5 h-[5px] w-full" : "-left-[1px] inset-y-0 h-full w-[3px]"}
style={penStyle(pen)}
>
<Stroke d={d} draw={mode} duration={700} width={1.1} />
</InkSvg>
</SeparatorPrimitive>
);
}
export { Separator };Usage
import { Separator } from "@/components/ui/separator"
<Separator />
<Separator orientation="vertical" />API reference
- orientation
"horizontal" | "vertical"default"horizontal" - Which way the rule runs.
- seed
string | number - Pins the drawing.
Every other prop goes to Base UI's Separator.
Pen settings
The drawing also takes draw, weight and speed, as props or from the nearest InkProvider. What each one does.