Overview
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:
- To separate distinct sections within a page or panel (e.g., between groups in a Sidebar or List)
- To create visual breaks in forms between field groups
- To delineate header, content, and footer regions within a Card or Dialog
- To separate items in a Dropdown Menu into logical groups
When NOT to use a Divider:
- When whitespace alone provides sufficient separation — adding a divider on top of generous spacing creates visual noise
- Between every item in a dense list — consider alternating row backgrounds or increased padding instead
- As a primary structural element — use a Grid or Stack for layout
- For purely decorative purposes where it adds no informational value
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.
Variants
Orientation Variants
| 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 |
Inset Variants
| 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 |
Content Variants
| 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 |
Thickness Variants
| 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.
Properties
Divider Properties
| 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 |
CSS Custom Properties
| 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 Mappings
Design Token Mappings
| 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.
States
Divider States
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.
Conditional Rendering
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>
))
Accessibility
Accessibility Requirements
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:
- 1.3.1 Info and Relationships (Level A): When a divider conveys structural meaning (separating distinct sections), it must be implemented with
<hr>orrole="separator"so screen readers announce the boundary. When purely decorative, userole="presentation"oraria-hidden="true"to suppress announcement. - 1.4.1 Use of Color (Level A): Do not rely solely on a colored divider to convey meaning. If a red divider indicates an error boundary, provide an additional textual or iconic cue.
- 1.4.11 Non-text Contrast (Level AA): The divider line must have at least 3:1 contrast against its adjacent background if it conveys meaning. Purely decorative dividers are exempt. Verify with the Contrast Checker.
- 4.1.2 Name, Role, Value (Level A): When using a custom element (e.g.,
<div>) instead of<hr>, explicitly setrole="separator". For vertical dividers withrole="separator", setaria-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.
Usage Guidelines
Usage Guidelines
Do:
- Use
<hr>as the base element whenever possible for built-in semantics - Pair dividers with appropriate whitespace — the divider alone is not a spacing mechanism; use the Spacing Scale for margins
- Use inset dividers in lists where items share a leading element (avatar, icon) to maintain visual continuity of the leading column
- Apply
role="presentation"to decorative dividers that do not communicate structure - Use the lowest-contrast divider color that still reads clearly against the background — overly dark dividers in light themes create visual clutter
Don't:
- Use dividers as a substitute for proper spacing — if the content is clearly separated by whitespace and hierarchy, the divider is redundant
- Place dividers between every single element in a dense layout — this creates a "cage" effect that makes the UI feel rigid
- Use thick, high-contrast dividers between tightly related items — it over-separates content that should feel cohesive
- Forget to conditionally omit the last divider in a list — a trailing divider before empty space looks like an error
- Nest dividers inside interactive elements —
<hr>inside a<button>or<a>is invalid HTML
Spacing 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 |
Code Snippets
<!-- 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>
);
}Design Systems
Design System Implementations
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.