A clean divider can save a layout. It separates topics, paces long pages, and gives readers a moment to reset their eyes. By the end of this walkthrough you will build a flexible, themeable page divider with pure CSS, including solid, dashed, and labeled variants, plus an angled option that works without images or SVGs.
Why Simple CSS Page Dividers Matter
A divider is a small piece of UI that appears many times across a site. Reaching for an image or SVG adds weight and a new asset pipeline for something that a few lines of CSS can solve. A CSS divider scales with the container, adapts to dark or light themes with custom properties, and reduces maintenance by living in your stylesheet. You also gain powerful variations using pseudo-elements and gradients with no extra HTML. If you want an organic, decorative option for hero breaks or marketing pages, see the CSS wave page divider pattern in the shapes library.
Prerequisites
Nothing here is exotic. You only need comfort with basic HTML and a few core CSS features. The code targets evergreen browsers and avoids vendor-specific tricks.
- Basic HTML
- CSS custom properties
- CSS pseudo-elements (::before / ::after)
Step 1: The HTML Structure
The markup stays minimal. A single element with a class of divider renders the line. Modifiers like divider–dashed or divider–label produce variations. The labeled version includes a child span for the text. Each divider sits between content sections and can carry an accessible role when useful.
<!-- HTML -->
<main class="container">
<section>
<h1>Simple CSS Page Divider Demo</h1>
<p>This page shows solid, dashed, labeled, and angled dividers built with pure CSS.</p>
</section>
<div class="divider" role="separator" aria-hidden="true"></div>
<section>
<h2>Section Two</h2>
<p>Solid dividers work well for content-heavy layouts and documentation.</p>
</section>
<div class="divider divider--dashed" role="separator" aria-hidden="true"></div>
<section>
<h2>Section Three</h2>
<p>Dashed lines suggest a loose break without a strong stop.</p>
</section>
<div class="divider divider--label" role="separator" aria-label="Next section">
<span class="divider__label">Next up</span>
</div>
<section>
<h2>Section Four</h2>
<p>A labeled divider can introduce the next topic or a short callout.</p>
</section>
<div class="divider divider--angle" role="separator" aria-hidden="true"></div>
<section>
<h2>Section Five</h2>
<p>An angled notch is a subtle way to point the eye downward.</p>
</section>
</main>
Step 2: The Basic CSS & Styling
Start with theming variables, typography, and layout spacing. The divider uses custom properties for thickness, color, and spacing, so you can theme it from a single place or override it per section. The base .divider draws a solid line; variants override background to create other styles.
/* CSS */
:root {
--bg: #0b1220;
--surface-1: #0f172a;
--text-1: #e2e8f0;
--text-2: #94a3b8;
--accent: #22d3ee;
--divider-color: #334155; /* Line color */
--divider-thickness: 2px; /* Line thickness */
--divider-space: 2.25rem; /* Vertical spacing above/below */
--divider-dash: 10px; /* Dash length */
--divider-gap: 10px; /* Space between dashes */
}
*,
*::before,
*::after { box-sizing: border-box; }
html, body {
height: 100%;
}
body {
margin: 0;
font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
line-height: 1.6;
color: var(--text-1);
background: radial-gradient(1200px 800px at 20% -10%, #0e1b33 0%, var(--bg) 60%) no-repeat,
var(--bg);
}
.container {
max-width: 68ch;
margin-inline: auto;
padding: 3rem 1rem 6rem;
}
h1, h2 {
line-height: 1.2;
margin: 0 0 0.5em;
}
p { margin: 0 0 1em; }
/* Base divider (solid) */
.divider {
height: var(--divider-thickness);
background: var(--divider-color);
margin-block: var(--divider-space);
border: 0;
}
Advanced Tip: Expose line color and thickness as custom properties on a parent container, then theme per section. For example, a marketing block can set –divider-color: var(–accent) to brand the divider without new selectors.
Step 3: Building the Solid and Dashed Dividers
The dashed variant leverages a repeating-linear-gradient. This removes extra markup and avoids border dashes that collapse at high zoom levels. Both versions share the same height and spacing, so you can swap classes freely.
/* CSS */
/* Solid divider already defined by .divider */
.divider--dashed {
/* The element height stays the same, we only change the paint */
background:
repeating-linear-gradient(
to right,
var(--divider-color) 0 var(--divider-dash),
transparent var(--divider-dash) calc(var(--divider-dash) + var(--divider-gap))
);
}
How This Works (Code Breakdown)
The base .divider sets height using –divider-thickness and paints a single color background. A block-level element with a background color acts like a horizontal rule, but it is more flexible than an hr tag when you want variants and animation.
The .divider–dashed rule replaces that solid fill with a repeating-linear-gradient. The gradient runs to right, which means the pattern repeats horizontally. The first color stop paints var(–divider-color) from 0 to var(–divider-dash). The next stop switches to transparent from var(–divider-dash) to var(–divider-dash + gap). That two-stop sequence repeats for the full width, creating dashes that scale with the container. Changing –divider-dash and –divider-gap will set the rhythm for a tighter or looser feel.
Spacing is controlled with margin-block so the divider breathes evenly above and below the line. If you prefer logical properties, margin-block works across writing modes. If you theme sections, place –divider-color on a section element and the line will inherit from there.
If you want a more decorative break with curvature, you can swap this line for a wave. The shapes library has an end-to-end pattern here: CSS wave page divider.
Step 4: Building the Labeled Divider
The labeled divider centers a short piece of text with lines on both sides. It uses a single wrapper with flexbox and two pseudo-elements for the arms. No extra divs are required.
/* CSS */
.divider--label {
/* Reset height so content defines the block size */
height: auto;
margin-block: var(--divider-space);
display: flex;
align-items: center;
gap: 0.75rem;
color: var(--divider-color); /* Pseudo-elements inherit this */
}
/* Left and right arms */
.divider--label::before,
.divider--label::after {
content: "";
height: var(--divider-thickness);
background: currentColor;
flex: 1 1 auto;
}
/* The text chip */
.divider__label {
color: var(--text-2);
font-weight: 600;
letter-spacing: 0.02em;
padding: 0 0.5rem;
line-height: 1.2;
white-space: nowrap;
background: var(--surface-1); /* Keeps the label crisp on patterned backgrounds */
border-radius: 0.25rem;
box-shadow: 0 0 0 1px rgba(148, 163, 184, 0.15) inset;
}
How This Works (Code Breakdown)
The .divider–label wrapper switches to flex so we can place the label text between two stretchable arms. Setting color on the wrapper makes currentColor available to the pseudo-elements, which keeps the API simple. The ::before and ::after draw the lines: each gets a fixed height equal to –divider-thickness and a flexible width through flex: 1 1 auto. They expand to fill the row equally, while the text remains centered.
The .divider__label uses a slight weight and letter spacing for legibility. A subtle background and inner ring prevent the lines from visually colliding with the text when placed over gradients or images. If your background is a flat color, you can remove the background and box-shadow to keep it austere.
For an angled or pointed divider, you can add a small triangular notch that directs attention downward. This uses the classic CSS triangle technique that builds shapes out of borders. The shapes library has a full primer on triangles; see triangle down with CSS for a refresher.
Advanced Techniques: Angled Notch and Motion
Here are two optional upgrades. The first creates an angled notch divider that points to the next section using a down-facing triangle. The second animates dashed dividers by shifting the gradient to create a gentle conveyor effect.
/* CSS */
/* Angled notch divider */
.divider--angle {
position: relative;
background: var(--divider-color);
height: var(--divider-thickness);
margin-block: var(--divider-space);
}
/* The notch hangs under the line */
.divider--angle::after {
content: "";
position: absolute;
left: 50%;
top: 100%;
transform: translateX(-50%);
/* Triangle built from borders */
width: 0;
height: 0;
border-left: 12px solid transparent;
border-right: 12px solid transparent;
border-top: 12px solid var(--divider-color);
}
/* Hover accent (optional) */
.divider:hover,
.divider--label:hover,
.divider--angle:hover {
--divider-color: var(--accent);
}
/* Animate dashed flow when motion is allowed */
@media (prefers-reduced-motion: no-preference) {
.divider--dashed {
background-size: calc(var(--divider-dash) + var(--divider-gap)) 100%;
animation: dash-flow 8s linear infinite;
}
@keyframes dash-flow {
to { background-position: 100% 0; }
}
}
The notch divider uses a 0x0 box with borders to form a triangle, a technique that is fast and battle-tested. The line is the parent background; the notch is the ::after triangle with a border-top the same color as the line. The transform centers it under the rule. On hover, the –divider-color changes for all variants. For motion, rather than animating height or width, the dashed divider moves the gradient through background-position, which is smooth and avoids layout work.
Accessibility & Performance
Dividers are decorative most of the time, but they can also carry meaning. Treat each variant with intention so screen readers and motion-sensitive users get a good experience.
Accessibility
When the divider is purely visual, add aria-hidden=”true” or omit roles entirely. The labeled divider can advertise the next section. In that case, keep role=”separator” with an aria-label so assistive tech announces “separator, Next up.” If the label conveys a heading-level concept, prefer a proper heading instead of a divider.
Respect motion preferences. The dashed animation sits behind a prefers-reduced-motion query, so users who reduce motion will see a static line. For color, verify the label text and any colored lines meet contrast targets against the background. You can bump –divider-thickness or switch to a higher-contrast –divider-color inside a high-contrast theme wrapper to keep the line visible.
Performance
Everything here is paint-only. A solid background or a repeating-linear-gradient is cheap to render. Pseudo-elements add no DOM nodes and carry the same cost as a background. The notch triangle uses border rendering, which is lightweight. The dashed flow animation changes only background-position, which runs on the compositor when possible and avoids layout recalculation. Avoid animating height, box-shadow, or filter for this component, since those trigger heavier work.
Ship dividers that stay out of the way
You built a solid, dashed, labeled, and angled divider using nothing but CSS. The API is small, themeable, and easy to extend across a design system. With these patterns you can separate content cleanly and keep your CSS in charge of presentation. When you want curves or a hero flourish, reach for the shapes library’s wave pattern next.