Home / Snippets / UI Components /

Section separator/divider

Five divider patterns — simple <hr>, gradient fade, text label, icon, and dashed — all built with semantic HTML and CSS.

Simple border


Gradient fade


Text label ("or")

Icon divider

Dashed


Widely Supported
uino-js

Quick implementation

/* 1. Simple hr — reset browser defaults first */
hr {
  border: none;
  border-top: 1px solid oklch(0.32 0.02 260);
  margin-block: 1.5rem;
}

/* 2. Gradient fade — center color matches your accent */
.divider-gradient {
  border: none;
  height: 1px;
  background: linear-gradient(
    to right,
    transparent,
    oklch(0.52 0.22 265),
    transparent
  );
  margin-block: 1.5rem;
}

/* 3. Text divider — flex + pseudo-element lines */
.divider-text {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  color: oklch(0.63 0.02 260);
  font-size: 0.85rem;
  margin-block: 1.5rem;
}
.divider-text::before,
.divider-text::after {
  content: '';
  flex: 1;
  height: 1px;
  background: oklch(0.32 0.02 260);
}

/* 4. Icon divider — same flex pattern, accent icon */
.divider-icon {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-block: 1.5rem;
}
.divider-icon::before,
.divider-icon::after {
  content: '';
  flex: 1;
  height: 1px;
  background: oklch(0.32 0.02 260);
}
.divider-icon__symbol {
  color: oklch(0.72 0.19 265);
  font-size: 1rem;
  line-height: 1;
  flex-shrink: 0;
}

/* 5. Dashed / dotted variations */
.divider-dashed {
  border: none;
  border-top: 2px dashed oklch(0.32 0.02 260);
  margin-block: 1.5rem;
}
.divider-dotted {
  border: none;
  border-top: 2px dotted oklch(0.32 0.02 260);
  margin-block: 1.5rem;
}

Prompt this to your LLM

Includes role, constraints, two framework variants, and edge cases to handle.

You are a senior frontend engineer building reusable section dividers
for a design system.

Goal: A set of CSS divider/separator patterns — simple hr, gradient fade,
text label (e.g. "or"), icon divider, and dashed variant — using semantic
HTML and no JavaScript.

Technical constraints:
- Reset <hr> defaults with border: none before applying custom styles.
- Use oklch() for all color values — no hex or rgba.
- Use CSS custom properties (var(--accent), var(--muted), etc.) for theming.
- Text and icon dividers must use display: flex with ::before and ::after
  pseudo-elements that have flex: 1 and a 1px background — not border-top.
- The gradient divider uses background: linear-gradient(to right, transparent,
  <accent>, transparent) on an element with height: 1px and border: none.
- All <hr> variants are self-closing; decorative div-based dividers must have
  role="separator" and an appropriate aria-label if they contain text.

Framework variant (pick one):
A) Vanilla CSS — standalone classes for each pattern.
B) React — a <Divider> component that accepts a variant prop
   ("simple" | "gradient" | "text" | "icon" | "dashed"), an optional
   label prop (string, used when variant="text"), and an optional
   icon prop (ReactNode, used when variant="icon").

Edge cases to handle:
- Never use <div> as a bare separator without role="separator" — screen
  readers will not announce unlabelled divs as thematic breaks.
- Avoid using margin on <hr> to create visual spacing between sections;
  prefer padding on the surrounding section elements so spacing is
  consistent regardless of whether a divider is present.
- The flex pseudo-element pattern breaks if the parent has
  justify-content: center — keep dividers in a block flow context or
  reset justify-content on the divider itself.
- In dark-mode-only designs, the gradient color stop should be the
  accent-bg token, not the text accent, for sufficient contrast against
  the dark background.

Return CSS only (or a React component if variant B is chosen).

Why semantic <hr> beats a plain <div>

The <hr> element carries a built-in ARIA role of separator, which tells screen readers that a thematic break exists between sections. A bare <div> with border-top is invisible to assistive technology unless you add role="separator" manually — and even then you miss the semantic signal that the content meaningfully shifts. Using <hr> is the right starting point for any simple horizontal rule.

The browser's default <hr> styles are inconsistent across engines, so always begin by zeroing them out: border: none removes the default inset border, then you apply whatever visual style you want via border-top, background, or height. This reset-then-style pattern gives you full control with zero specificity fights.

For text dividers like "or" and icon dividers, <hr> can't hold child content, so a <div role="separator"> is the correct pattern. Add aria-label when the divider carries meaningful text (e.g. aria-label="or") so screen reader users hear the label, not silence.

The gradient fade technique

A gradient divider communicates softness — it implies continuity rather than a hard cut. The trick is to make the element's height exactly 1px, remove any border, and apply background: linear-gradient(to right, transparent, <accent>, transparent). The transparent stops at both ends fade to the page background, creating the illusion that the line appears from nothing and dissolves back into nothing.

Choosing the right color for the center stop matters. In dark-mode designs using oklch(), the accent-background token — something like oklch(0.52 0.22 265) — tends to work better than the text accent because it has enough lightness to be visible against a dark surface without being harsh. The perceptual uniformity of oklch() means the gradient fades look smooth: no muddy intermediate hues that you'd get with legacy hsl() gradients.

The flexbox text-divider pattern

The "or" divider — a label centered between two horizontal lines — is a classic UI pattern for separating login options, form sections, or content blocks. The CSS is a three-part flex row: a ::before pseudo-element, the text content, and an ::after pseudo-element. Both pseudo-elements get content: '', flex: 1, a height: 1px, and a background color. The flex: 1 tells each line to grow equally and fill the remaining space on either side of the label.

This pattern requires the container to be in block flow — if the parent has justify-content: center, the pseudo-elements collapse to zero width. Apply the pattern directly to a full-width element, or override justify-content on the divider itself. For icon dividers, the same structure applies: swap the text node for a small icon element with flex-shrink: 0 so the icon never compresses.

The gap property handles spacing between the icon or text and the lines more reliably than padding on the label, because gap applies uniformly regardless of font rendering differences. Set it to around 0.75rem for a balanced look at body text sizes.