Skip to content

Divider

A visual separator used to divide sections of content.

  • Separator
  • Rule
  • HR

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

VariantDescriptionCommon Use Case
HorizontalSpans the full width of its container. The default and most common form.Separating sections in a page, form groups, list items
VerticalSpans the full height of its container. Used inside horizontal layouts.Separating items in a toolbar, splitting panel regions, inline content groups

Inset Variants

VariantDescriptionVisual Effect
Full-bleedExtends edge-to-edge within the container.Strong separation, clearly delineates sections
InsetIndented 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)
MiddleIndented equally from both edges.Centered rule, often used in content-heavy layouts

Content Variants

VariantDescriptionExample
PlainA simple line with no content. The semantic default.<hr />
With textA label centered on the divider line."OR", "Section 2", "Continue reading"
With iconAn icon centered on the divider line.A decorative star, arrow, or brand mark
Dashed / DottedAlternative line styles for lighter visual weight.Draft states, placeholder boundaries

Thickness Variants

VariantBorder WidthUse Case
Hairline1pxDefault — subtle separation
Medium2pxEmphasized section breaks
Bold4–8pxDecorative accents, brand-colored section dividers

Generate and preview border styles with the Divider Generator.

Properties

Divider Properties

PropertyTypeDefaultDescription
orientation'horizontal' | 'vertical''horizontal'Axis along which the divider renders
variant'fullWidth' | 'inset' | 'middle''fullWidth'Controls inset spacing
thicknessnumber | string1Border width in pixels
colorstringToken-basedOverride the divider color. Prefer design tokens.
spacingnumber | string0Margin above and below (horizontal) or left and right (vertical)
childrenReactNode—Content rendered centered on the divider (text or icon)
textAlign'left' | 'center' | 'right''center'Alignment of child content along the divider
componentElementType'hr'Override the rendered HTML element
flexItembooleanfalseWhen true, applies align-self: stretch for use inside flex containers
rolestring'separator'ARIA role override. Set to 'presentation' for purely decorative dividers.
classNamestring—Custom CSS class
styleCSSProperties—Inline style overrides

CSS Custom Properties

Custom PropertyDescriptionDefault
--divider-colorLine colorvar(--color-border-subtle)
--divider-thicknessLine thickness1px
--divider-spacingMargin on both sides0
--divider-inset-startLeft/top inset0
--divider-inset-endRight/bottom inset0

Token Mappings

Design Token Mappings

TokenRoleTypical Value
color.border.subtleDefault divider color#e5e7eb (light) / #374151 (dark)
color.border.defaultEmphasized divider#d1d5db (light) / #4b5563 (dark)
color.border.strongBold/accent divider#9ca3af (light) / #6b7280 (dark)
color.border.brandBrand-colored dividerBrand primary at reduced opacity
space.1 – space.6Divider spacing (margin)4px – 24px
space.4Default inset value16px
radius.noneDivider border-radius (typically none)0
opacity.dividerDivider opacity for semi-transparent styles0.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:

StateDescriptionVisual Treatment
DefaultStandard renderingThin line in color.border.subtle
DecorativePurely visual, no semantic meaningSet role="presentation" and aria-hidden="true"
With contentDisplays centered label or iconLine 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> or role="separator" so screen readers announce the boundary. When purely decorative, use role="presentation" or aria-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 set role="separator". For vertical dividers with role="separator", set aria-orientation="vertical".

Screen Reader Behavior:

ImplementationAnnouncement
<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:

ContextSpacing Above/BelowDivider Type
Between page sections24–48pxFull-bleed, hairline
Between list items0 (built into list padding)Inset, hairline
Between form groups16–24pxFull-bleed or inset
Inside a card12–16pxInset, hairline
In dropdown menus4–8pxFull-bleed, hairline

Code Snippets

html
<!-- 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>
tsx
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.

LayoutSeparatorRuleHR