Loading…
Loading…
A visual separator used to divide sections of content.
The Divider (also known as a separator, rule, or horizontal rule) is a subtle but essential layout primitive that creates visual boundaries between sections of content. While it may seem trivial, the divider plays a critical role in establishing visual hierarchy, grouping related content, and improving scannability across interfaces.
At its core, the divider leverages the Gestalt principle of proximity: by inserting a visible boundary between groups, you reinforce that items above and below the line belong to different conceptual units. Without dividers — or an equivalent amount of whitespace — dense interfaces become an undifferentiated wall of information.
When to use a Divider:
When NOT to use a Divider:
Use the Spacing Scale Generator to find the right margin values around your dividers. Check that your divider color maintains sufficient contrast against the background with the Contrast Checker.
| Variant | Description | Common Use Case |
|---|---|---|
| Horizontal | Spans the full width of its container. The default and most common form. | Separating sections in a page, form groups, list items |
| Vertical | Spans the full height of its container. Used inside horizontal layouts. | Separating items in a toolbar, splitting panel regions, inline content groups |
| Variant | Description | Visual Effect |
|---|---|---|
| Full-bleed | Extends edge-to-edge within the container. | Strong separation, clearly delineates sections |
| Inset | Indented from the left (or both sides) by a fixed amount, typically matching content padding. | Softer separation, maintains visual connection between items (common in lists with avatars) |
| Middle | Indented equally from both edges. | Centered rule, often used in content-heavy layouts |
| Variant | Description | Example |
|---|---|---|
| Plain | A simple line with no content. The semantic default. | <hr /> |
| With text | A label centered on the divider line. | "OR", "Section 2", "Continue reading" |
| With icon | An icon centered on the divider line. | A decorative star, arrow, or brand mark |
| Dashed / Dotted | Alternative line styles for lighter visual weight. | Draft states, placeholder boundaries |
| Variant | Border Width | Use Case |
|---|---|---|
| Hairline | 1px | Default — subtle separation |
| Medium | 2px | Emphasized section breaks |
| Bold | 4–8px | Decorative accents, brand-colored section dividers |
Generate and preview border styles with the Divider Generator.
| Property | Type | Default | Description |
|---|---|---|---|
orientation | 'horizontal' | 'vertical' | 'horizontal' | Axis along which the divider renders |
variant | 'fullWidth' | 'inset' | 'middle' | 'fullWidth' | Controls inset spacing |
thickness | number | string | 1 | Border width in pixels |
color | string | Token-based | Override the divider color. Prefer design tokens. |
spacing | number | string | 0 | Margin above and below (horizontal) or left and right (vertical) |
children | ReactNode | — | Content rendered centered on the divider (text or icon) |
textAlign | 'left' | 'center' | 'right' | 'center' | Alignment of child content along the divider |
component | ElementType | 'hr' | Override the rendered HTML element |
flexItem | boolean | false | When true, applies align-self: stretch for use inside flex containers |
role | string | 'separator' | ARIA role override. Set to 'presentation' for purely decorative dividers. |
className | string | — | Custom CSS class |
style | CSSProperties | — | Inline style overrides |
| Custom Property | Description | Default |
|---|---|---|
--divider-color | Line color | var(--color-border-subtle) |
--divider-thickness | Line thickness | 1px |
--divider-spacing | Margin on both sides | 0 |
--divider-inset-start | Left/top inset | 0 |
--divider-inset-end | Right/bottom inset | 0 |
| Token | Role | Typical Value |
|---|---|---|
color.border.subtle | Default divider color | #e5e7eb (light) / #374151 (dark) |
color.border.default | Emphasized divider | #d1d5db (light) / #4b5563 (dark) |
color.border.strong | Bold/accent divider | #9ca3af (light) / #6b7280 (dark) |
color.border.brand | Brand-colored divider | Brand primary at reduced opacity |
space.1 – space.6 | Divider spacing (margin) | 4px – 24px |
space.4 | Default inset value | 16px |
radius.none | Divider border-radius (typically none) | 0 |
opacity.divider | Divider opacity for semi-transparent styles | 0.12 (Material), 1.0 (most systems) |
Use the Spacing Scale Generator to visualise how different spacing tokens affect divider margins. Preview divider colors against backgrounds using the Contrast Checker.
Dividers are non-interactive elements and therefore have a minimal state model:
| State | Description | Visual Treatment |
|---|---|---|
| Default | Standard rendering | Thin line in color.border.subtle |
| Decorative | Purely visual, no semantic meaning | Set role="presentation" and aria-hidden="true" |
| With content | Displays centered label or icon | Line splits around centered content with consistent gaps |
Because dividers are not focusable or interactive, they do not have hover, active, focus, or disabled states. If a divider contains interactive content (e.g., a clickable "Show more" link), that interactive element carries its own state model independently.
Dividers between list items should conditionally render — the last item in a list should not be followed by a divider. In React:
items.map((item, i) => (
<Fragment key={item.id}>
<ListItem {...item} />
{i < items.length - 1 && <Divider />}
</Fragment>
))
The HTML <hr> element carries an implicit role="separator" and is universally understood by assistive technologies. This makes the native <hr> the strongest semantic choice for horizontal dividers.
WCAG Success Criteria:
<hr> or role="separator" so screen readers announce the boundary. When purely decorative, use role="presentation" or aria-hidden="true" to suppress announcement.<div>) instead of <hr>, explicitly set role="separator". For vertical dividers with role="separator", set aria-orientation="vertical".Screen Reader Behavior:
| Implementation | Announcement |
|---|---|
<hr> | "Separator" or "Horizontal rule" |
<div role="separator"> | "Separator" |
<div role="presentation"> | Not announced |
<div aria-hidden="true"> | Not announced |
Semantic vs Decorative Decision:
If the divider separates content that a sighted user would perceive as distinct sections, keep it semantic. If it's purely aesthetic flourish (e.g., a decorative line in a hero section), mark it decorative. The test: if you removed the divider, would a sighted user lose understanding of content grouping? If yes, it's semantic.
Do:
<hr> as the base element whenever possible for built-in semanticsrole="presentation" to decorative dividers that do not communicate structureDon't:
<hr> inside a <button> or <a> is invalid HTMLSpacing Recommendations:
| Context | Spacing Above/Below | Divider Type |
|---|---|---|
| Between page sections | 24–48px | Full-bleed, hairline |
| Between list items | 0 (built into list padding) | Inset, hairline |
| Between form groups | 16–24px | Full-bleed or inset |
| Inside a card | 12–16px | Inset, hairline |
| In dropdown menus | 4–8px | Full-bleed, hairline |
<!-- Basic horizontal divider -->
<hr class="divider" />
<!-- Divider with text -->
<div class="divider divider--with-text" role="separator">
<span class="divider__text">OR</span>
</div>
<!-- Vertical divider inside a flex container -->
<div class="toolbar" style="display: flex; align-items: center; gap: 8px;">
<button>Cut</button>
<button>Copy</button>
<div class="divider divider--vertical" role="separator" aria-orientation="vertical"></div>
<button>Paste</button>
</div>
<!-- Inset divider in a list -->
<ul class="list">
<li class="list__item">
<img class="list__avatar" src="user1.jpg" alt="" />
<span>Alice</span>
</li>
<hr class="divider divider--inset" />
<li class="list__item">
<img class="list__avatar" src="user2.jpg" alt="" />
<span>Bob</span>
</li>
</ul>
<!-- Decorative divider (no semantic meaning) -->
<div class="divider divider--decorative" role="presentation" aria-hidden="true"></div>
<style>
.divider {
border: none;
border-top: var(--divider-thickness, 1px) solid var(--divider-color, #e5e7eb);
margin: var(--divider-spacing, 0) 0;
}
.divider--vertical {
border-top: none;
border-left: var(--divider-thickness, 1px) solid var(--divider-color, #e5e7eb);
align-self: stretch;
margin: 0 var(--divider-spacing, 0);
}
.divider--inset {
margin-left: var(--divider-inset-start, 56px);
}
.divider--with-text {
display: flex;
align-items: center;
gap: 16px;
border: none;
color: #6b7280;
font-size: 0.875rem;
}
.divider--with-text::before,
.divider--with-text::after {
content: '';
flex: 1;
border-top: 1px solid #e5e7eb;
}
</style>import React from 'react';
interface DividerProps {
orientation?: 'horizontal' | 'vertical';
variant?: 'fullWidth' | 'inset' | 'middle';
thickness?: number;
color?: string;
spacing?: number;
decorative?: boolean;
children?: React.ReactNode;
textAlign?: 'left' | 'center' | 'right';
className?: string;
}
function Divider({
orientation = 'horizontal',
variant = 'fullWidth',
thickness = 1,
color,
spacing = 0,
decorative = false,
children,
textAlign = 'center',
className,
}: DividerProps) {
const isHorizontal = orientation === 'horizontal';
const role = decorative ? 'presentation' : 'separator';
const ariaOrientation = !isHorizontal && !decorative ? 'vertical' : undefined;
const insetStart = variant === 'inset' ? 56 : variant === 'middle' ? 16 : 0;
const insetEnd = variant === 'middle' ? 16 : 0;
if (children) {
return (
<div
role={role}
aria-orientation={ariaOrientation}
aria-hidden={decorative || undefined}
className={className}
style={{
display: 'flex',
alignItems: 'center',
gap: 16,
margin: isHorizontal ? `${spacing}px 0` : `0 ${spacing}px`,
color: '#6b7280',
fontSize: '0.875rem',
}}
>
<span style={{ flex: textAlign === 'left' ? '0 0 24px' : 1, borderTop: `${thickness}px solid ${color || '#e5e7eb'}` }} />
<span>{children}</span>
<span style={{ flex: textAlign === 'right' ? '0 0 24px' : 1, borderTop: `${thickness}px solid ${color || '#e5e7eb'}` }} />
</div>
);
}
if (!isHorizontal) {
return (
<div
role={role}
aria-orientation="vertical"
aria-hidden={decorative || undefined}
className={className}
style={{
borderLeft: `${thickness}px solid ${color || '#e5e7eb'}`,
alignSelf: 'stretch',
margin: `0 ${spacing}px`,
}}
/>
);
}
return (
<hr
role={decorative ? 'presentation' : undefined}
aria-hidden={decorative || undefined}
className={className}
style={{
border: 'none',
borderTop: `${thickness}px solid ${color || '#e5e7eb'}`,
margin: `${spacing}px 0`,
marginLeft: insetStart,
marginRight: insetEnd,
}}
/>
);
}
// Usage
function SettingsList() {
const sections = [
{ title: 'Account', items: ['Profile', 'Email', 'Password'] },
{ title: 'Notifications', items: ['Push', 'Email', 'SMS'] },
];
return (
<div>
{sections.map((section, i) => (
<React.Fragment key={section.title}>
{i > 0 && <Divider spacing={16} />}
<h3>{section.title}</h3>
{section.items.map((item, j) => (
<React.Fragment key={item}>
{j > 0 && <Divider variant="inset" />}
<div style={{ padding: '12px 16px' }}>{item}</div>
</React.Fragment>
))}
</React.Fragment>
))}
</div>
);
}Material Design 3 (MUI) provides a <Divider> component with props for orientation (horizontal | vertical), variant (fullWidth | inset | middle), flexItem (boolean for use in flex containers), textAlign (left | center | right when children are provided), and light (reduces opacity). MUI renders <hr> by default and supports child content (text/chips) centered on the line. The component automatically handles aria-orientation for vertical dividers. MUI's divider color is tied to the divider palette channel (rgba(0,0,0,0.12) in light mode).
Ant Design provides a <Divider> component with type (horizontal | vertical), orientation (left | center | right for text placement), orientationMargin (distance from edges when text is left/right-aligned), dashed (boolean for dashed line style), plain (boolean for plain text vs styled heading), and children (inline text). Ant renders <div role="separator"> rather than <hr>. Its vertical divider is implemented as an inline-block element with fixed height.
Chakra UI offers <Divider> with orientation (horizontal | vertical), variant (solid | dashed), and standard style props (borderColor, borderWidth). Chakra renders <hr> for horizontal and <div> for vertical. It does not natively support child content on the line — you would compose that with <Flex> and pseudo-elements or use the <AbsoluteCenter> utility.
Radix UI does not provide a dedicated Divider primitive. Their recommendation is to use a styled <hr> or <div role="separator"> since the component is simple enough not to need a headless abstraction. This is a reasonable stance — dividers have no complex interaction or state management needs.
Headless UI does not include a divider component for the same reasons as Radix.
Spectrum (Adobe) provides <Divider> with size (S | M | L), orientation (horizontal | vertical), and staticColor (for placement over colored backgrounds). Spectrum uses three thickness tiers: small (1px for list separators), medium (2px for section breaks), and large (4px for header underlines). Their implementation uses <hr> with role="separator" and handles aria-orientation automatically.