Borders, gradients, pseudo-elements and clipped shapes can all split a page into sections without a single image file. Plenty of pages add one out of habit, though, when plain spacing would have done the job.

MDN Web Docs (2026) describes the hr element as a thematic break, not a decorative line, and points purely visual rules toward CSS borders. A divider earns its place where it marks a boundary that spacing alone fails to show.

Which Method Should You Use to Build a CSS Divider?

It depends on the job. A plain break between two blocks takes an hr or a border. Anything curved needs SVG, and a label in the middle calls for a pseudo-element. The table puts them side by side.

MethodBest forMain limitExtra markup
hr or borderPlain line between content blocksSolid or dashed styles, no fadeNone or one div
Pseudo-elementLines beside text or an iconNeeds a flex wrapperOne wrapper
GradientFading, striped, or scalloped linesAwkward for large curvesNone or one div
clip-pathDiagonals and trianglesClips borders and children tooNone
Inline SVGWaves and smooth curvesPath data is tedious to write by handOne svg element

Most pages only ever need the first row. A styled hr covers blog posts and docs, and settings screens usually get by with it too. The other methods pay for their extra markup on landing pages and section edges.

A divider also has to strengthen the visual hierarchy of the page, and sometimes plain spacing does that job better than a line.

Curves are the one case where a border cannot help at all. Those need SVG paths, which stay sharp at any container width.

How to Style an hr Element as a Divider

Wipe the default border first, then draw your own with border-top and set the width and margin around it.

The rendering section of the HTML Living Standard (WHATWG, revision of 13 October 2026) gives the hr a 1px inset border and an auto inline margin, plus a gray color. MDN adds that the element is defined by meaning now, which is why purely visual lines belong to CSS borders.

  1. Remove the default inset border with border: none.
  2. Draw the line with border-top: 2px solid #ccc, which sets thickness, style, and color in one declaration.
  3. Set the length with width: 60% and center it with margin-inline: auto.
  4. Add vertical spacing with margin-block: 2rem.
  5. Soften the line with opacity: 0.6 if it competes with the text.

That margin does more work than the line itself. The white space around a divider is what makes it read as a break, so I settle on 1.5rem to 3rem before touching line weight.

Why color alone does not work

This one catches almost everyone the first time. Setting only a color on an hr changes the line, but the inset style still paints a shaded edge on all four sides, 1px each, so the rule stays 2px tall.

Declare border-style or border-top yourself and the shading goes away. The old color and noshade attributes work by switching the style to solid, which tells you what the real fix is.

Is responsive design still a top priority?

Explore the latest responsive design statistics: adoption rates, performance impact, user behavior, and trends shaping modern websites.

See the Numbers →

Line styles beyond solid

Signicat’s Catnip design system renders a native hr and maps its style setting straight to border-style: solid, dashed, dotted, or double. Its size setting maps to 1px, 2px, and 4px of thickness.

Dashed works for secondary breaks, and dotted is about the lightest touch you can get. Double reads as a formal, print-style rule. For that one, copy MDN’s own hr example, which uses a 3px double border.

How to Make a Divider With Text or an Icon in the Middle

See the Pen
Text Divider
by Lynden Oliver (@lyndenoliver)
on CodePen.

An hr cannot contain text, so build a labeled divider from a flex row with a line on each side of the label.

Set the wrapper to display flex with align-items center and a gap, then give ::before and ::after an empty content string, flex: 1, and a border-top. The two lines share whatever width the label leaves over.

Tabler’s divider component shows how far the pattern stretches. Its hr-text class centers the label by default, and hr-text-start and hr-text-end move it to either end (Tabler documentation).

For an icon, swap the text for a span that holds the icon and keep the same two lines.

The fieldset and legend alternative

A fieldset with only a top border and a centered legend draws the same look with no pseudo-elements (Kindacode’s Tailwind example). The HTML Standard skips painting the fieldset border behind the legend, so the line breaks around the label on its own.

The cost is meaning. A fieldset groups form controls and its legend names that group, so using the pair for decoration sends the wrong signal.

The flex row works inside any container and keeps both lines level at every width, and moving the label takes one alignment change. It does need a wrapper element and a class on every divider. The fieldset route is a single border with no generated content, and the browser handles label placement. Its downsides are the wrong semantics for decoration and a legend position tied to the fieldset border.

One accessibility catch applies to both. MDN notes that browsers treat everything inside a separator role as presentational, so a heading placed inside role=”separator” loses its heading semantics (MDN Web Docs).

Keep the label as plain text and leave the role off the labeled wrapper.

How to Create Gradient, Fading, and Patterned Dividers

See the Pen
Fading Divider
by Steven Mask (@mnicpt)
on CodePen.

A border can’t fade out at the ends. A gradient can, and it can also repeat in custom stripes or shift color along the length.

For the fade, give a thin element a background of linear-gradient from transparent to a color and back to transparent. Webstudio’s separator docs set it at 2px tall, 50% wide, with 32px of auto-centered margin.

Tuning color stops by eye gets tedious, so a CSS gradient generator speeds up the fade and stripe variants.

For custom dashes, repeating-linear-gradient sets dash and gap lengths exactly. A radial-gradient tiled across the width draws a scalloped edge, and border-image accepts a gradient as its source if you want a gradient border.

Double lines come from a different trick. Put a box-shadow with zero blur and a 4px vertical offset on a 1px element, and the shadow paints the second line, which a box shadow generator lets you preview before you commit.

border-image has a catch, though. MDN states that border-radius has no effect on a border image, so a rounded gradient border needs two stacked background-image layers instead.

Some browsers also skip the image when border-style is none or the border width is 0, which is why MDN advises declaring a fallback border-style. The property itself has been Baseline since July 2015.

How to Build Wave, Curve, and Diagonal Section Dividers

Inline SVG handles waves and curves. Diagonals and triangles come from clip-path with polygon().

SVG wave dividers

See the Pen
SVG section dividers, embedded with Data-URI & colored with SASS
by rinaw (@rinaw)
on CodePen.

Place the SVG at the bottom edge of the section and give it a viewBox with no fixed width or height. Set it to width 100% and the shape stretches to fill any container (CodeShack).

Fill the path with the next section’s background color, ideally through a custom property such as var(–color-bg). A wave that belongs on a top edge just needs transform: rotate(180deg), and display: block on the svg removes the small gap inline elements leave below them.

Hand-writing cubic bezier path data is tedious, because changing the wave height means recalculating every control point (parix.ai). An SVG waves generator outputs a copyable viewBox path and skips that step.

clip-path diagonals and triangles

See the Pen
Diagonal Divider
by JD Tinney (@Yappenzo)
on CodePen.

The clip-path property has been Baseline widely available since January 2020 (MDN Web Docs), so a polygon() diagonal needs no fallback in current browsers.

A triangle edge is clip-path: polygon(50% 0, 100% 100%, 0 100%), the same shape MDN uses in its own example. Stick to percentages, because MDN’s shape() versus path() comparison shows a path() clip stays fixed in pixels while the element grows.

Watch what gets cut. The clip applies to the element’s border and text as well as its background, so any content near the cut edge disappears with it. Any value other than none also creates a stacking context, the same way opacity does, which can change z-index layering between neighboring sections.

Dragging points in a CSS clip-path generator is faster than guessing polygon coordinates, and you can paste the finished values straight into the stylesheet.

How to Add Vertical Dividers in Flex, Grid, and Column Layouts

See the Pen
Untitled
by Bobby (@protonbobby)
on CodePen.

An hr only draws horizontal lines, so a vertical divider is usually a border on one item. A thin stretched element or a column rule also works.

Prefer border-inline-start over border-left. Tailwind’s documentation notes that the logical property maps to the left or right edge based on text direction, so the line follows the layout in right-to-left languages.

Flexbox and grid

In a flex row, give every item after the first a border-inline-start plus matching padding, or drop a thin element between items.

A standalone line needs height to stretch into. Chi, the design system from Lumen, states that its vertical divider requires a parent container with a specified height.

A grid with one row can use border-inline-start on :not(:first-child). Wrapping rows are the problem, because that rule leaves a stray line at the start of every new row, so switch to gap-based rules.

Multi-column and list items

The column-rule property has been Baseline widely available since March 2017 for multi-column layouts (MDN Web Docs).

MDN’s page also shows a flex list with gap: 12px and column-rule: 10px groove maroon, which belongs to the newer CSS Gaps module. The same page’s formal definition still says the property applies to multicol elements, so check the compatibility table before you use it on flex or grid (MDN, revised 5 October 2026).

Keep the rule narrower than the gap, as in that example, where the 12px gap leaves room for the 10px rule.

For navigation links and list items, a sibling selector such as li + li adds the line to every item except the first.

Tailwind and Bootstrap utilities

Both big CSS frameworks ship shortcuts for this job.

The utility-first Tailwind framework adds borders between children with divide-x and divide-y. Reversed order catches people out. When flex-row-reverse flips the items, add divide-x-reverse, or the border lands on the wrong side of each one (Tailwind documentation, v4.3).

Bootstrap goes the component route. Its .vr helper arrived in v5.1, measures 1px wide with a min-height of 1em, and stretches to the container height inside flex layouts (Bootstrap 5.3 documentation).

Should a CSS Divider Be Hidden From Screen Readers?

Hide it when the line is purely visual, and leave it exposed when it marks a real change of topic.

Published guidance disagrees, and the disagreement is about intent, not browser behavior.

SourcePosition on hrReasoning
MDN Web DocsImplicit separator role, with presentation or none permittedNative semantics stay unless overridden
Pennsylvania Keystone design systemAdd aria-hidden=”true”Treats the line as visual only
Wonderflow design systemAdd aria-hidden when the line is not content-relatedSame visual-only reading

Keep the hr and its default role when the line marks a real break. For a visual-only line, draw it with a border or pseudo-element and add no node at all. Labeled dividers need nothing special, since the label stays readable text and the lines are pseudo-elements.

ARIA offers two ways to silence a leftover hr. According to MDN, aria-hidden="true" removes an element and all its children from the accessibility tree without hiding anything visually, while role="presentation" or none strips the semantics and keeps the content exposed.

For a childless hr the two end the same way. They differ on a wrapper, which is where mistakes happen.

MDN warns that aria-hidden is inherited, so it should not sit on a focusable element or on an ancestor of one. A toolbar wrapper that holds a divider and buttons would hide the buttons too.

If CSS already hides the divider with display: none, visibility: hidden, or the hidden attribute, skip the attribute. MDN says the element is out of the accessibility tree in all of those cases.

How to Make Dividers Responsive and Theme-Aware

See the Pen
Responsive SVG Arrow as Section divider
by Domonkos Horvath (@domhorvath)
on CodePen.

Keep the sizing in relative units and the color in currentColor or a custom property. Media queries only come in when orientation or visibility has to change.

Percentage and rem widths scale with the container, so a 60% centered line stays proportional on a phone and on a wide monitor.

Orientation is trickier. A vertical divider between flex items turns into a stray sliver once the row wraps. Swap it for a horizontal rule with media queries, or hide the vertical one below the breakpoint.

Writing the stacked, horizontal version first and adding the vertical rule from a breakpoint up is the mobile-first order. It keeps the small-screen CSS short.

On color, currentColor lets the line follow the text color, and MDN lists currentcolor as the default for column-rule.

A common pattern goes one step further and exposes the color and spacing as custom properties, such as --divider-color and --divider-spacing, so one edit restyles every divider.

The prefers-color-scheme media feature has been Baseline widely available since January 2020 and reports light when the user has expressed no preference (MDN Web Docs). Override only the color token inside the dark query and every divider updates.

SVG dividers need one extra step. MDN says a prefers-color-scheme rule inside an SVG is meant for SVGs used as embedded files, through an img tag for example, rather than SVGs inlined in HTML, so inline waves should take their fill from a custom property.

Motion deserves a thought as well. MDN lists scaling and panning large objects as vestibular motion triggers, which covers animated waves that drift across the screen. The prefers-reduced-motion feature has been Baseline since January 2020, so stop or tone down the animation inside @media (prefers-reduced-motion: reduce).

When a CSS Divider Is the Wrong Choice

Skip the divider when spacing or the layout already separates the content, container edges included.

Volvo Cars’ design system documentation (@volvo-cars/css v3.1.0) lists the cases plainly, and the table pairs each one with a better move.

SituationWhy a line failsBetter move
More margin would separate the blocksThe line only adds noiseIncrease spacing
Inside a card, tile, or modalThe container already separatesLeave it out
Purely decorative accentA divider signals structure, not styleUse a heading style or background
Covering a layout or alignment bugThe line hides the faultFix the layout

The same documentation names where a divider does earn its place. Dense tables and settings panels, mostly, and groups of menu items where spacing alone does not show the boundary. Its example is a “Danger zone” block set apart from standard settings.

Layouts built on minimalist design principles lean on space and type for grouping, and add a line only when those fail.

Shape dividers need extra restraint. A wave or diagonal between every pair of sections looks like noise, not polish, so I’d use one at the hero edge and let plain spacing handle the rest.

The quickest test is to remove the line and look at the page. If the grouping is still clear, leave it out.

CSS Dividers Examples To Check Out

Benjamin’s Horizontal Delight

See the Pen
Cool Horizontal Divider Headings
by Benjamin Knight (@benknight)
on CodePen.

Fresh Section Breaks by joshuar

See the Pen
Section Breaks
by joshuar (@joshuar)
on CodePen.

Catchy SVG Page Separator by Alexandr Kazakov

See the Pen
SVG page separator
by Alexandr Kazakov (@alexandr-kazakov)
on CodePen.

Color Burst with Slanted Sections by Adam Quinlan

See the Pen
Slanted Sections
by Adam Quinlan (@quinlo)
on CodePen.

Rinatoptimus’s Circle Game

See the Pen
Divider with a circle
by rinatoptimus (@rinatoptimus)
on CodePen.

HummixX’s Stylish Touch

See the Pen
Simple stylish divider with a little help of span.
by HummixX (@HummixX)
on CodePen.

Get Creative with Email Designs

See the Pen
Email Design Conference CSS Divider
by Eric Klemen (@emkmail2)
on CodePen.

Ema’s Boundary Breaker

See the Pen
Row Separator
by Ema (@emared)
on CodePen.

Gregor’s Classic Horizontal

See the Pen
horizontal divider css
by Gregor (@fenixmedia)
on CodePen.

Daniel Ilse’s Pseudo Genius

See the Pen
CSS Divider using :pseudo elements
by Daniel Ilse (@dantilse)
on CodePen.

Starry Night Divider

See the Pen
Pure CSS Horizontal Divider With Star Icon
by Isabel C (@isabelc)
on CodePen.

Getting Ziggy with Brandon

See the Pen
crooked section dividers
by Brandon Kennedy (@brandonkennedy)
on CodePen.

Elegance in Lines

See the Pen
Fancy Horizontal Rules
by szpakoli (@szpakoli)
on CodePen.

Ride the CSS Wave

See the Pen
Waves Content Divider Using CSS
by CMDW (@cmdw)
on CodePen.

Milan’s Playground

See the Pen
Divider Experiments #1
by Milan (@Oddgson)
on CodePen.

Pixel Perfection

See the Pen
Pixel Dividers
by C4rin3 (@c4rin3)
on CodePen.

Keep it Sleek with Pinline Headers

See the Pen
Header Pinlines
by Robert Douglas (@redouglas)
on CodePen.

Skewed, Yet Perfect

See the Pen
Responsive Skewed Page Dividers (using CSS gradients)
by chilli con code (@chilliconcode)
on CodePen.

Go Zigzag with Ninjaweb

See the Pen
Zigzag Divider
by ninja (@Ninjaweb)
on CodePen.

Colors that Pop

See the Pen
Frame Patterns
by Bram de Haan (@atelierbram)
on CodePen.

Divide and Rule with Christian

See the Pen
Resonsive Divider with Content
by Christian Gorke (@WolfInStep)
on CodePen.

Scalloping Like a Pro

See the Pen
Responsive Scalloped Page Dividers (using CSS gradients)
by chilli con code (@chilliconcode)
on CodePen.

Step Outside the Box

See the Pen
Non-rectangular Sections | CSS clip-path
by Cagri Kizilkan (@cgrkzlkn)
on CodePen.

Daniel Padin’s Sleek Collection

See the Pen
Section Separators
by Daniel Padin (@webd3v)
on CodePen.

One Element Wonder

See the Pen
Fancy title divider with one element
by Temani Afif (@t_afif)
on CodePen.

Dashing in Rainbow

See the Pen
Rainbowy Dashed Divider
by Simon Goellner (@simeydotme)
on CodePen.

Gone Fishing with Norbert

See the Pen
Fish logo in line
by Norbert Papp (@partisan1991)
on CodePen.

The Forest Feels by Olly Hodgson

See the Pen
SVG forested separator bar
by Olly Hodgson (@thinkdrastic)
on CodePen.

Medium Vibes with a Touch of Kindness

See the Pen
Accessible Medium Style Dividers
by Zoë Bijl (@Moiety)
on CodePen.

Freecodecamp’s Finest

See the Pen
FreeCodeCamp style dividing line
by Jeffrey Thomas (@2percentMilkFromCows)
on CodePen.

Triangulating with Mark

See the Pen
Triangle section Divider
by Mark Sottek (@mark_sottek)
on CodePen.

Less is More with José Luis Antúnez

See the Pen
Animated <hr>
by José Luis Antúnez (@jlantunez)
on CodePen.

Palette Play with Greg Douglas

See the Pen
Different Divider Examples
by Greg Douglas (@xgad)
on CodePen.

The Diagonal Drama

See the Pen
Diagonal Split Screen
by Chris Smith (@chris22smith)
on CodePen.

Caree’s Subtle Shadows

See the Pen
Box-Shadows as Page Dividers
by Caree Youngman (@careecodes)
on CodePen.

CSS Dividers FAQ

Does the hr element need a closing tag?

No. A void element takes a start tag and must not have an end tag, according to MDN Web Docs, and hr is one. Write <hr> or <hr />; both are valid.

Can an hr go inside a select menu or a list?

Yes to both. MDN shows an hr between options inside a select element. Inside a list, wrap it in an li with role=”presentation” so the markup stays valid.

Can you animate a divider?

See the Pen
Pacman divider
by C4rin3 (@c4rin3)
on CodePen.

Yes, with limits. MDN lists clip-path as animatable for basic shapes such as polygon(), but not for url() references. The column-rule-width property animates as a length, while border-image-source is discrete, so a gradient border flips instead of fading.

Should a divider be an image file or CSS?

CSS or inline SVG, in nearly every case. An image file adds a network request and cannot follow the page’s text color. A border can inherit currentColor, and so can an SVG fill.

Is an hr the same as a div with a border?

No. An hr carries an implicit separator role and the browser defaults for gray color, a 1px inset border and an auto inline margin (HTML Living Standard, 2026), since it is a thematic break. A div with a border has neither the meaning nor the defaults.

Building a Reusable Divider Class Set

A reusable set starts with one base rule and a token each for color, thickness and spacing. Each variant then overrides a single token instead of redefining the whole divider.

Build order matters, because each step depends on the one before it. Start with the hr reset using border-top, driven by the color token. Vertical, labeled and gradient variants come next on the same tokens, and shape dividers go last, with the fill read from the color token.

Tokens first means one prefers-color-scheme override recolors every divider at once, inline SVG waves included.

The trade-off is a longer stylesheet and one more naming layer to maintain. The base hr needs no motion rule or extra markup, while every shape divider adds both, so each one has to earn its place.

Bogdan Sandu
Latest posts by Bogdan Sandu (see all)