Separator
A one-pixel divider in horizontal or vertical orientation - decorative by default, accessible when it should not be.
import { Separator } from "@/components/ui/separator"
export default function SeparatorDemo() {
return (
<div className="flex w-full max-w-sm flex-col gap-4">
<div className="flex h-5 items-center gap-3 text-sm">
<span>Docs</span>
<Separator orientation="vertical" />
<span>Changelog</span>
<Separator orientation="vertical" />
<span>Blog</span>
</div>
<Separator label="OR" variant="dashed" />
</div>
)
}Installation
Run the following command
npx shadcn@latest add @ai2/separatorDependencies, the @ai2/tokens theme and the component file are installed together.
Install dependencies
npm install radix-ui@^1.6.1Add the cn util
lib/utils.tsimport { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
/* Adds a source-attribution ref param to a URL (the inspiration exports mark their
outbound links with an ai2.design attribution). An invalid URL is returned as is.
This file is SHOWN TO THE CONSUMER: the docs component pages render the source of
`cn` in a code block, so a Turkish comment here would reach every one of those
pages. Keep it English. */
export function withRef(url: string, ref = "ai2.design"): string {
try {
const u = new URL(url)
u.searchParams.set("ref", ref)
return u.toString()
} catch {
return url
}
}Copy the source code
components/ui/separator.tsx"use client"
import type * as React from "react"
import { Separator as SeparatorPrimitive } from "@/components/ui/primitives"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const separatorVariants = cva("shrink-0", {
variants: {
variant: {
solid:
"bg-border data-[orientation=horizontal]:h-px data-[orientation=vertical]:w-px",
dashed:
"border-border data-[orientation=horizontal]:border-t data-[orientation=horizontal]:border-dashed data-[orientation=vertical]:border-l data-[orientation=vertical]:border-dashed",
},
},
defaultVariants: { variant: "solid" },
})
interface SeparatorProps
extends React.ComponentProps<typeof SeparatorPrimitive.Root>,
VariantProps<typeof separatorVariants> {
/** Optional centered label; renders a horizontal divider with text (e.g. "OR"). */
label?: React.ReactNode
}
function Separator({
className,
orientation = "horizontal",
decorative = true,
variant,
label,
...props
}: SeparatorProps) {
if (label != null) {
return (
<div
data-slot="separator"
data-orientation="horizontal"
role={decorative ? "none" : "separator"}
className={cn(
"flex items-center gap-3 text-xs font-medium text-muted-foreground",
className
)}
{...props}
>
<span
data-orientation="horizontal"
className={cn(separatorVariants({ variant }), "h-px flex-1")}
/>
{label}
<span
data-orientation="horizontal"
className={cn(separatorVariants({ variant }), "h-px flex-1")}
/>
</div>
)
}
return (
<SeparatorPrimitive.Root
data-slot="separator"
decorative={decorative}
orientation={orientation}
className={cn(
separatorVariants({ variant }),
"data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full",
className
)}
{...props}
/>
)
}
export { Separator, separatorVariants, type SeparatorProps }Manual installs skip the @ai2/tokens theme - add the token CSS from the theming guide or the border color will be missing.
Usage
import { Separator } from "@/components/ui/separator"
<Separator />
<Separator orientation="vertical" />Horizontal separators fill the available width. Vertical ones fill the parent's height - give the containing flex row a height for the divider to show.
Examples
Horizontal
ai2 design system
Tokens, components and docs.
Distributed via the shadcn CLI.
Vertical
Variant
solid is the default filled rule; dashed swaps it for a dashed border in either orientation.
Label
A label renders a horizontal two-line divider with centered text, ideal for an "OR" break between form sections. The label is always horizontal and honors the chosen variant.
Props
Separator also accepts every radix Separator.Root prop and native <div> prop.
| Prop | Type | Default | Description |
|---|---|---|---|
orientation | "horizontal" | "vertical" | "horizontal" | Direction of the divider line. |
variant | "solid" | "dashed" | "solid" | Line style of the divider: a filled rule or a dashed border. |
label | ReactNode | undefined | Optional centered label; renders a horizontal two-line divider with text (e.g. "OR") instead of the plain rule. |
decorative | boolean | true | When true the separator is hidden from assistive tech; set to false to expose role=separator semantics. |
ai2 Separator: a semantic divider for React
The ai2 Separator is a shadcn-compatible separator component for React, built on the radix-ui Separator primitive and styled with Tailwind CSS v4. It draws a one-pixel divider in horizontal or vertical orientation using the shared border token, and it gets the accessibility question right: decorative by default, real separator semantics when you ask for them.
Because it ships through the shadcn registry format, you install it with one CLI command, an MCP agent, or a copy-paste, and the source lands in your own project. You own the file; there is no runtime dependency on ai2 itself. The live example above is the exact component you get.
What is the ai2 Separator?
It is a single part: Separator. A few props do all the work: orientation switches between a full-width horizontal rule and a full-height vertical one through data attributes, variant picks a solid or dashed line, label turns it into a divider with centered text, and decorative decides whether assistive technology should see it at all. The anatomy matches shadcn/ui exactly.
It stays deliberately structural: no tone or size axis, just a solid or dashed line on the border token that follows your theme in both light and dark mode. Anything beyond that, thickness, color or spacing, is a className away because the source lives in your project.
Why use it
- Correct semantics either way: decorative defaults to true, hiding a purely visual divider from screen readers. Set it to false and the radix primitive exposes a real separator role with the right orientation.
- Orientation-aware sizing: data-orientation drives the styling: horizontal fills the available width at one pixel tall, vertical fills the parent height at one pixel wide. One component, no duplicated markup.
- One token, both themes: The line uses the border token from the ai2 theme, so every divider matches your inputs, cards and table borders in light and dark mode automatically.
- Deliberately tiny API: No tones and no sizes: a divider should not carry meaning through color. The only style choice is solid or dashed, plus an optional label, so there is almost nothing to configure and nothing to get wrong.
- Agent-readable metadata: The registry item describes its purpose and props in plain words, so an MCP agent can find, inspect and install it without guessing.
Features
- shadcn registry install: One command adds the component, its dependencies and the @ai2/tokens theme to your project.
- Horizontal and vertical orientation: One prop covers section breaks in a column of content and inline dividers in a toolbar or breadcrumb-style row.
- Solid or dashed variant: The variant prop switches between a filled rule and a dashed border in either orientation, both drawn from the same border token.
- Optional centered label: Pass a label and the separator becomes a two-line divider with centered text, the classic "OR" break between form sections, with no extra markup.
- Decorative prop: Defaults to true for visual-only dividers; set decorative to false when the separation itself carries meaning that screen reader users need.
- Data attributes for styling: The element exposes data-slot and data-orientation, so you can restyle horizontal and vertical dividers independently from CSS.
- Freely overridable via className: Thickness, color and margins are plain Tailwind utilities on top: my-4 for spacing, h-0.5 for a heavier rule, a gradient background if the design calls for it.
- TypeScript source: The file you install is typed end to end and forwards every radix Separator prop plus native div props.
Production tips
- Give vertical separators a height: A vertical separator fills its parent, so a flex row with no height renders an invisible line. Set a height on the row, like h-5 in the example above.
- Space with className, not wrappers: Add my-4 or mx-3 directly on the separator instead of wrapping it in a spacer div. The margins ship with the divider wherever you reuse it.
- Keep decorative unless it means something: Most dividers are visual rhythm and should stay hidden from assistive tech. Only set decorative to false when removing the divider would genuinely lose structure.
- Do not double up inside menus: The ai2 Dropdown Menu and Select ship their own separator parts scoped to menu semantics. Use those inside menus and keep this component for page content.
- Prefer separators over ad hoc borders: A border-b on a random div does the same pixel but skips the semantics and drifts from the system. One component keeps every divider consistent and themeable.
Works with the rest of ai2
The separator composes naturally with the rest of the registry. Split sections inside an ai2 Card, separate the header from the body of an ai2 Alert Dialog, or divide grouped content in an ai2 Sheet panel.
Vertical separators shine in horizontal chrome: between links next to an ai2 Breadcrumb or between action clusters in a toolbar built from ai2 Button groups. Everything shares one token source, so combinations stay visually consistent in both modes.