Component · Core
Separator
Overview
A thin line that divides content into distinct groups — between sections of a card, items in a menu, or entries in a toolbar. Purely visual and structural: no click handler, no hover state, no keyboard focus.
Anatomy
A single line. No sub-parts — unlike Badge's icon-and-label or Checkbox's box-and-label, Separator has nothing to compose.
Live Example
Horizontal (decorative)
Sections of a card, stacked vertically.
A horizontal hairline divides them.
Vertical (decorative)
Semantic (role="separator")
A thematic break in prose, exposed to assistive tech.
A screen reader announces this boundary.
Code Example
import { Separator } from "@/components/ui/separator";
/**
* Live Separator example — a horizontal divider between stacked content and a
* vertical divider in a row (both decorative), plus one semantic separator
* (`decorative={false}` → `role="separator"`) for a real content boundary.
* Separator is static: no hover, no focus, no interaction.
*/
export function SeparatorShowcase() {
return (
<div className="flex flex-col gap-8">
<section className="flex flex-col gap-3">
<h3 className="text-sm font-medium leading-snug text-muted-foreground">
Horizontal (decorative)
</h3>
<div className="flex flex-col gap-4 text-sm leading-normal text-foreground">
<p>Sections of a card, stacked vertically.</p>
<Separator />
<p>A horizontal hairline divides them.</p>
</div>
</section>
<section className="flex flex-col gap-3">
<h3 className="text-sm font-medium leading-snug text-muted-foreground">
Vertical (decorative)
</h3>
<div className="flex h-8 items-center gap-4 text-sm leading-snug text-muted-foreground">
<span>Docs</span>
<Separator orientation="vertical" />
<span>API</span>
<Separator orientation="vertical" />
<span>Support</span>
</div>
</section>
<section className="flex flex-col gap-3">
<h3 className="text-sm font-medium leading-snug text-muted-foreground">
Semantic (role="separator")
</h3>
<div className="flex flex-col gap-4 text-sm leading-normal text-foreground">
<p>A thematic break in prose, exposed to assistive tech.</p>
<Separator decorative={false} />
<p>A screen reader announces this boundary.</p>
</div>
</section>
</div>
);
}Variants
None. Orientation (above) is the only axis of variation — there's no color, style, or weight variant to choose between.
Orientation
Two orientations, chosen to match the layout it sits in:
| Orientation | Spans | Use when |
|---|---|---|
| Horizontal (default) | Full width of its container, --scale-px tall | Dividing stacked content — sections in a card, rows in a list |
| Vertical | Full height of its container, --scale-px wide | Dividing side-by-side content — items in a horizontal toolbar or breadcrumb-like row |
Orientation is a layout decision, not a visual one — the token and thickness don't change, only which dimension the line spans.
States
| State | Look |
|---|---|
| Default | --border line, --scale-px thick |
Separator is static — nothing to hover, focus, activate, or disable.
Usage Guidance
Decorative vs. semantic
Most Separators in a UI are decorative: a visual grouping cue with no meaning a screen reader
user needs announced, because the grouping is already conveyed by structure (headings, landmarks,
list boundaries) or doesn't carry any real semantic weight. Default to decorative — a plain
non-interactive element with no ARIA role, invisible to assistive tech, the same way a CSS border
between two <div>s would be.
Mark a Separator semantic only when it's the sole signal of a meaningful boundary — most often
a thematic break inside a block of prose content (e.g. a long-form article's scene break), rather
than a UI layout divider. A semantic separator is exposed to assistive tech: role="separator",
plus aria-orientation="vertical" when vertical (horizontal is the implicit ARIA default, so it
needs no attribute). For a horizontal semantic break specifically, the native <hr> element is
the simpler choice — it carries an implicit role="separator" on its own. <hr> has no vertical
form, so a vertical semantic separator always needs the role/aria-orientation div pattern
instead.
In short: reach for <hr> only inside real content flow; reach for the plain decorative div for
everything else — which is most component and layout usage.
Separator vs. spacing, borders, and containers
Separator is one of three ways to create visual distance between things — reach for the one that matches the actual relationship:
| Need | Use |
|---|---|
| Related items just need breathing room, no dividing line | Spacing alone (--scale-* gap) — see Spacing |
| A contained surface needs a defined edge | The container's own --border (a card, an input) — not a Separator dropped along one side |
| Two groups need an explicit dividing line between them, distinct from ordinary spacing | Separator |
A Separator is a signal, not a default. If padding and gap already read as separated groups, an added line is usually clutter, not clarity — reach for it when the line itself communicates something spacing alone doesn't.
Tokens
Separator uses existing Monogem semantic and primitive tokens to stay consistent with the design system. Both tokens already name this exact use case in Color: --border is documented for "Lines, dividers," and --scale-px is already the border-width recipe's "Default border — cards, inputs, sidebar, dividers."
| Token | Where used |
|---|---|
--border | Line color |
--scale-px | Line thickness (1px hairline) |
Spacing around a Separator isn't a new token either — the gap on each side matches whatever
--scale-* step already separates the sections it divides (see
Spacing's task table), not a fixed value unique to Separator.
Do / Don’t
Do
- Default to decorative — most Separators are layout dividers, not content boundaries.
- Match orientation to the layout: horizontal in a vertical stack, vertical in a horizontal row.
- Reuse the surrounding layout's existing spacing token on either side, rather than a one-off gap.
Don’t
- Use a Separator as a substitute for a container's own border — a card's edge is
--borderon the card, not a Separator laid along one side. - Add a Separator where spacing alone already reads as separated groups.
- Give Separator a click handler, hover state, or focus ring — it isn't interactive.
- Stack multiple Separators, or pair one with heavy spacing on both sides — pick one.
Accessibility
- Decorative (default): no ARIA role, not present in the accessibility tree, not announced — correct, since it carries no information beyond what's already structurally conveyed.
- Semantic:
role="separator"(or native<hr>for a horizontal content break), plusaria-orientation="vertical"when vertical. - Never the only way information is conveyed — a Separator (decorative or semantic) is a visual reinforcement of a grouping, not a substitute for real structure (headings, landmarks, list markup).
- Not in the tab order either way — it's never interactive, regardless of decorative/semantic status.