26 CSS Dividers08 / 26
Pure CSS Ornamental Divider with Pseudo-Elements
Five ornamental rules, each built from ONE element — a bare <hr> — with the glyph injected by content: attr(data-glyph) and the flanking rules painted as backgrounds. No wrapper divs, no spans, no icon font. Because the ornament is a data attribute, one CSS rule serves diamonds, fleurons, asterisks and chevrons, and the markup stays a semantically correct thematic break.
Published
The code
<section class="div-08" aria-label="Ornamental pseudo-element divider demo">
<div class="div-08__stage">
<p class="div-08__eyebrow">one <hr> · zero extra markup</p>
<h2 class="div-08__title">Ornaments from a data attribute</h2>
<div class="div-08__set">
<hr class="div-08__orn" data-glyph="❖">
<hr class="div-08__orn" data-glyph="❦">
<hr class="div-08__orn div-08__orn--cluster" data-glyph="✦ ✦ ✦">
<hr class="div-08__orn div-08__orn--taper" data-glyph="§">
<hr class="div-08__orn div-08__orn--heavy" data-glyph="◆">
</div>
<p class="div-08__chip">content:attr(data-glyph) · punched-through background · role="separator" kept</p>
</div>
</section><section class="div-08" aria-label="Ornamental pseudo-element divider demo">
<div class="div-08__stage">
<p class="div-08__eyebrow">one <hr> · zero extra markup</p>
<h2 class="div-08__title">Ornaments from a data attribute</h2>
<div class="div-08__set">
<hr class="div-08__orn" data-glyph="❖">
<hr class="div-08__orn" data-glyph="❦">
<hr class="div-08__orn div-08__orn--cluster" data-glyph="✦ ✦ ✦">
<hr class="div-08__orn div-08__orn--taper" data-glyph="§">
<hr class="div-08__orn div-08__orn--heavy" data-glyph="◆">
</div>
<p class="div-08__chip">content:attr(data-glyph) · punched-through background · role="separator" kept</p>
</div>
</section>.div-08 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
background: var(--div-08-bg);
--div-08-bg: oklch(0.975 0.012 92);
--div-08-ink: oklch(0.26 0.02 60);
--div-08-mut: oklch(0.56 0.02 60);
--div-08-line: oklch(0.26 0.02 60/.28);
--div-08-acc: oklch(0.55 0.12 40);
font-family: Georgia,'Iowan Old Style','Times New Roman',serif;
color: var(--div-08-ink);
}
.div-08 *,
.div-08 *::before,
.div-08 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.div-08__stage {
min-height: 100svh;
display: grid;
place-content: center;
justify-items: center;
gap: clamp(20px,4vw,34px);
padding: clamp(28px,6vw,72px) clamp(20px,5vw,56px);
background: radial-gradient(110% 80% at 50% 0%,oklch(0.995 0.008 92),transparent);
}
.div-08__eyebrow {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11.5px;
letter-spacing: .16em;
text-transform: uppercase;
color: var(--div-08-acc);
}
.div-08__title {
font-size: clamp(28px,5vw,50px);
line-height: 1.06;
letter-spacing: -.02em;
font-weight: 400;
text-align: center;
text-wrap: balance;
}
.div-08__set {
width: min(90vw,560px);
display: grid;
gap: clamp(26px,5vw,44px);
}
.div-08__orn {
position: relative;
border: 0;
height: 1px;
background: linear-gradient(90deg,transparent,var(--div-08-line) 20%,var(--div-08-line) 80%,transparent);
}
.div-08__orn::before {
content: attr(data-glyph);
position: absolute;
left: 50%;
top: 50%;
translate: -50% -50%;
padding: 0 .7em;
background: var(--div-08-bg);
color: var(--div-08-acc);
font-family: 'Segoe UI Symbol','Apple Symbols','Noto Sans Symbols 2','Segoe UI Historic',Georgia,serif;
font-size: 22px;
line-height: 1;
transition: rotate .5s cubic-bezier(.65,0,.35,1),color .3s ease;
}
.div-08__orn:hover::before {
rotate: 180deg;
color: oklch(0.45 0.15 30);
}
.div-08__orn--cluster::before {
letter-spacing: .34em;
font-size: 14px;
padding: 0 .9em;
}
.div-08__orn--taper {
height: 3px;
background: linear-gradient(90deg,transparent,var(--div-08-line) 30%,transparent 48%,transparent 52%,var(--div-08-line) 70%,transparent);
-webkit-mask: linear-gradient(90deg,#0000,#000 8%,#000 92%,#0000);
mask: linear-gradient(90deg,#0000,#000 8%,#000 92%,#0000);
}
.div-08__orn--taper::before {
font-size: 17px;
font-style: italic;
}
.div-08__orn--heavy {
height: 4px;
border-radius: 99px;
background: linear-gradient(90deg,transparent,var(--div-08-acc) 26%,var(--div-08-acc) 74%,transparent);
opacity: .75;
}
.div-08__orn--heavy::before {
font-size: 13px;
padding: 0 1em;
}
.div-08__chip {
margin-top: 8px;
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
color: var(--div-08-mut);
padding: 8px 14px;
border: 1px solid var(--div-08-line);
border-radius: 99px;
text-align: center;
max-width: 92vw;
}
@media (prefers-reduced-motion: reduce) {
.div-08__orn::before {
transition: none;
}
}.div-08 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
background: var(--div-08-bg);
--div-08-bg: oklch(0.975 0.012 92);
--div-08-ink: oklch(0.26 0.02 60);
--div-08-mut: oklch(0.56 0.02 60);
--div-08-line: oklch(0.26 0.02 60/.28);
--div-08-acc: oklch(0.55 0.12 40);
font-family: Georgia,'Iowan Old Style','Times New Roman',serif;
color: var(--div-08-ink);
}
.div-08 *,
.div-08 *::before,
.div-08 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.div-08__stage {
min-height: 100svh;
display: grid;
place-content: center;
justify-items: center;
gap: clamp(20px,4vw,34px);
padding: clamp(28px,6vw,72px) clamp(20px,5vw,56px);
background: radial-gradient(110% 80% at 50% 0%,oklch(0.995 0.008 92),transparent);
}
.div-08__eyebrow {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11.5px;
letter-spacing: .16em;
text-transform: uppercase;
color: var(--div-08-acc);
}
.div-08__title {
font-size: clamp(28px,5vw,50px);
line-height: 1.06;
letter-spacing: -.02em;
font-weight: 400;
text-align: center;
text-wrap: balance;
}
.div-08__set {
width: min(90vw,560px);
display: grid;
gap: clamp(26px,5vw,44px);
}
.div-08__orn {
position: relative;
border: 0;
height: 1px;
background: linear-gradient(90deg,transparent,var(--div-08-line) 20%,var(--div-08-line) 80%,transparent);
}
.div-08__orn::before {
content: attr(data-glyph);
position: absolute;
left: 50%;
top: 50%;
translate: -50% -50%;
padding: 0 .7em;
background: var(--div-08-bg);
color: var(--div-08-acc);
font-family: 'Segoe UI Symbol','Apple Symbols','Noto Sans Symbols 2','Segoe UI Historic',Georgia,serif;
font-size: 22px;
line-height: 1;
transition: rotate .5s cubic-bezier(.65,0,.35,1),color .3s ease;
}
.div-08__orn:hover::before {
rotate: 180deg;
color: oklch(0.45 0.15 30);
}
.div-08__orn--cluster::before {
letter-spacing: .34em;
font-size: 14px;
padding: 0 .9em;
}
.div-08__orn--taper {
height: 3px;
background: linear-gradient(90deg,transparent,var(--div-08-line) 30%,transparent 48%,transparent 52%,var(--div-08-line) 70%,transparent);
-webkit-mask: linear-gradient(90deg,#0000,#000 8%,#000 92%,#0000);
mask: linear-gradient(90deg,#0000,#000 8%,#000 92%,#0000);
}
.div-08__orn--taper::before {
font-size: 17px;
font-style: italic;
}
.div-08__orn--heavy {
height: 4px;
border-radius: 99px;
background: linear-gradient(90deg,transparent,var(--div-08-acc) 26%,var(--div-08-acc) 74%,transparent);
opacity: .75;
}
.div-08__orn--heavy::before {
font-size: 13px;
padding: 0 1em;
}
.div-08__chip {
margin-top: 8px;
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
color: var(--div-08-mut);
padding: 8px 14px;
border: 1px solid var(--div-08-line);
border-radius: 99px;
text-align: center;
max-width: 92vw;
}
@media (prefers-reduced-motion: reduce) {
.div-08__orn::before {
transition: none;
}
}Here's a working CSS Divider from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Pure CSS Ornamental Divider with Pseudo-Elements
Source: https://codefronts.com/snippets/css-dividers/pure-css-ornamental-divider-with-pseudo-elements/
Five ornamental rules, each built from ONE element — a bare <hr> — with the glyph injected by content: attr(data-glyph) and the flanking rules painted as backgrounds. No wrapper divs, no spans, no icon font. Because the ornament is a data attribute, one CSS rule serves diamonds, fleurons, asterisks and chevrons, and the markup stays a semantically correct thematic break.
## HTML
```html
<section class="div-08" aria-label="Ornamental pseudo-element divider demo">
<div class="div-08__stage">
<p class="div-08__eyebrow">one <hr> · zero extra markup</p>
<h2 class="div-08__title">Ornaments from a data attribute</h2>
<div class="div-08__set">
<hr class="div-08__orn" data-glyph="❖">
<hr class="div-08__orn" data-glyph="❦">
<hr class="div-08__orn div-08__orn--cluster" data-glyph="✦ ✦ ✦">
<hr class="div-08__orn div-08__orn--taper" data-glyph="§">
<hr class="div-08__orn div-08__orn--heavy" data-glyph="◆">
</div>
<p class="div-08__chip">content:attr(data-glyph) · punched-through background · role="separator" kept</p>
</div>
</section>
```
## CSS
```css
.div-08 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
background: var(--div-08-bg);
--div-08-bg: oklch(0.975 0.012 92);
--div-08-ink: oklch(0.26 0.02 60);
--div-08-mut: oklch(0.56 0.02 60);
--div-08-line: oklch(0.26 0.02 60/.28);
--div-08-acc: oklch(0.55 0.12 40);
font-family: Georgia,'Iowan Old Style','Times New Roman',serif;
color: var(--div-08-ink);
}
.div-08 *,
.div-08 *::before,
.div-08 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.div-08__stage {
min-height: 100svh;
display: grid;
place-content: center;
justify-items: center;
gap: clamp(20px,4vw,34px);
padding: clamp(28px,6vw,72px) clamp(20px,5vw,56px);
background: radial-gradient(110% 80% at 50% 0%,oklch(0.995 0.008 92),transparent);
}
.div-08__eyebrow {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11.5px;
letter-spacing: .16em;
text-transform: uppercase;
color: var(--div-08-acc);
}
.div-08__title {
font-size: clamp(28px,5vw,50px);
line-height: 1.06;
letter-spacing: -.02em;
font-weight: 400;
text-align: center;
text-wrap: balance;
}
.div-08__set {
width: min(90vw,560px);
display: grid;
gap: clamp(26px,5vw,44px);
}
.div-08__orn {
position: relative;
border: 0;
height: 1px;
background: linear-gradient(90deg,transparent,var(--div-08-line) 20%,var(--div-08-line) 80%,transparent);
}
.div-08__orn::before {
content: attr(data-glyph);
position: absolute;
left: 50%;
top: 50%;
translate: -50% -50%;
padding: 0 .7em;
background: var(--div-08-bg);
color: var(--div-08-acc);
font-family: 'Segoe UI Symbol','Apple Symbols','Noto Sans Symbols 2','Segoe UI Historic',Georgia,serif;
font-size: 22px;
line-height: 1;
transition: rotate .5s cubic-bezier(.65,0,.35,1),color .3s ease;
}
.div-08__orn:hover::before {
rotate: 180deg;
color: oklch(0.45 0.15 30);
}
.div-08__orn--cluster::before {
letter-spacing: .34em;
font-size: 14px;
padding: 0 .9em;
}
.div-08__orn--taper {
height: 3px;
background: linear-gradient(90deg,transparent,var(--div-08-line) 30%,transparent 48%,transparent 52%,var(--div-08-line) 70%,transparent);
-webkit-mask: linear-gradient(90deg,#0000,#000 8%,#000 92%,#0000);
mask: linear-gradient(90deg,#0000,#000 8%,#000 92%,#0000);
}
.div-08__orn--taper::before {
font-size: 17px;
font-style: italic;
}
.div-08__orn--heavy {
height: 4px;
border-radius: 99px;
background: linear-gradient(90deg,transparent,var(--div-08-acc) 26%,var(--div-08-acc) 74%,transparent);
opacity: .75;
}
.div-08__orn--heavy::before {
font-size: 13px;
padding: 0 1em;
}
.div-08__chip {
margin-top: 8px;
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
color: var(--div-08-mut);
padding: 8px 14px;
border: 1px solid var(--div-08-line);
border-radius: 99px;
text-align: center;
max-width: 92vw;
}
@media (prefers-reduced-motion: reduce) {
.div-08__orn::before {
transition: none;
}
}
```Here's a working CSS Divider from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Pure CSS Ornamental Divider with Pseudo-Elements
Source: https://codefronts.com/snippets/css-dividers/pure-css-ornamental-divider-with-pseudo-elements/
Five ornamental rules, each built from ONE element — a bare <hr> — with the glyph injected by content: attr(data-glyph) and the flanking rules painted as backgrounds. No wrapper divs, no spans, no icon font. Because the ornament is a data attribute, one CSS rule serves diamonds, fleurons, asterisks and chevrons, and the markup stays a semantically correct thematic break.
## HTML
```html
<section class="div-08" aria-label="Ornamental pseudo-element divider demo">
<div class="div-08__stage">
<p class="div-08__eyebrow">one <hr> · zero extra markup</p>
<h2 class="div-08__title">Ornaments from a data attribute</h2>
<div class="div-08__set">
<hr class="div-08__orn" data-glyph="❖">
<hr class="div-08__orn" data-glyph="❦">
<hr class="div-08__orn div-08__orn--cluster" data-glyph="✦ ✦ ✦">
<hr class="div-08__orn div-08__orn--taper" data-glyph="§">
<hr class="div-08__orn div-08__orn--heavy" data-glyph="◆">
</div>
<p class="div-08__chip">content:attr(data-glyph) · punched-through background · role="separator" kept</p>
</div>
</section>
```
## CSS
```css
.div-08 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
display: block;
background: var(--div-08-bg);
--div-08-bg: oklch(0.975 0.012 92);
--div-08-ink: oklch(0.26 0.02 60);
--div-08-mut: oklch(0.56 0.02 60);
--div-08-line: oklch(0.26 0.02 60/.28);
--div-08-acc: oklch(0.55 0.12 40);
font-family: Georgia,'Iowan Old Style','Times New Roman',serif;
color: var(--div-08-ink);
}
.div-08 *,
.div-08 *::before,
.div-08 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.div-08__stage {
min-height: 100svh;
display: grid;
place-content: center;
justify-items: center;
gap: clamp(20px,4vw,34px);
padding: clamp(28px,6vw,72px) clamp(20px,5vw,56px);
background: radial-gradient(110% 80% at 50% 0%,oklch(0.995 0.008 92),transparent);
}
.div-08__eyebrow {
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11.5px;
letter-spacing: .16em;
text-transform: uppercase;
color: var(--div-08-acc);
}
.div-08__title {
font-size: clamp(28px,5vw,50px);
line-height: 1.06;
letter-spacing: -.02em;
font-weight: 400;
text-align: center;
text-wrap: balance;
}
.div-08__set {
width: min(90vw,560px);
display: grid;
gap: clamp(26px,5vw,44px);
}
.div-08__orn {
position: relative;
border: 0;
height: 1px;
background: linear-gradient(90deg,transparent,var(--div-08-line) 20%,var(--div-08-line) 80%,transparent);
}
.div-08__orn::before {
content: attr(data-glyph);
position: absolute;
left: 50%;
top: 50%;
translate: -50% -50%;
padding: 0 .7em;
background: var(--div-08-bg);
color: var(--div-08-acc);
font-family: 'Segoe UI Symbol','Apple Symbols','Noto Sans Symbols 2','Segoe UI Historic',Georgia,serif;
font-size: 22px;
line-height: 1;
transition: rotate .5s cubic-bezier(.65,0,.35,1),color .3s ease;
}
.div-08__orn:hover::before {
rotate: 180deg;
color: oklch(0.45 0.15 30);
}
.div-08__orn--cluster::before {
letter-spacing: .34em;
font-size: 14px;
padding: 0 .9em;
}
.div-08__orn--taper {
height: 3px;
background: linear-gradient(90deg,transparent,var(--div-08-line) 30%,transparent 48%,transparent 52%,var(--div-08-line) 70%,transparent);
-webkit-mask: linear-gradient(90deg,#0000,#000 8%,#000 92%,#0000);
mask: linear-gradient(90deg,#0000,#000 8%,#000 92%,#0000);
}
.div-08__orn--taper::before {
font-size: 17px;
font-style: italic;
}
.div-08__orn--heavy {
height: 4px;
border-radius: 99px;
background: linear-gradient(90deg,transparent,var(--div-08-acc) 26%,var(--div-08-acc) 74%,transparent);
opacity: .75;
}
.div-08__orn--heavy::before {
font-size: 13px;
padding: 0 1em;
}
.div-08__chip {
margin-top: 8px;
font-family: ui-monospace,Menlo,Consolas,monospace;
font-size: 11px;
color: var(--div-08-mut);
padding: 8px 14px;
border: 1px solid var(--div-08-line);
border-radius: 99px;
text-align: center;
max-width: 92vw;
}
@media (prefers-reduced-motion: reduce) {
.div-08__orn::before {
transition: none;
}
}
```How this works
The trick is that an <hr> can carry pseudo-elements, so a single tag becomes rule + ornament + rule:
hr[data-glyph]{ position:relative; border:0; height:1px;
background:linear-gradient(90deg,transparent,var(--line) 22%,
var(--line) 78%,transparent); }
hr[data-glyph]::before{
content:attr(data-glyph);
position:absolute; left:50%; top:50%;
translate:-50% -50%;
padding:0 .7em; background:var(--bg); color:var(--acc); }The ornament isn't sitting on the line — it is punched through it. The pseudo-element paints the page background across the middle of the rule and the glyph draws on top, which is why the rule never peeks out from behind a glyph with awkward sidebearings. attr() means the ornament is authored in HTML where it belongs, so a single class handles every variant.
The tapered variant swaps the flat gradient for two mirrored rules that thin toward the centre, using a second background layer with mask. The double-diamond variant adds ::after with three spaced glyphs and letter-spacing, which is the cheapest way to build a fleuron cluster.
Every ornament here is decoration on top of a real thematic break, so the <hr> keeps its role="separator" and pseudo-element content stays out of the accessibility tree — the correct division of labour.
Make it yours
- Any glyph:
data-glyph="❦"(floral heart),"✦","§","⁂","◆◆◆". Addletter-spacing:.35emfor clusters. - Inline SVG instead of a glyph: keep the same structure but set
content:''plusbackground:url('data:image/svg+xml,…') center/contain no-repeatand a width/height. - Rotate on hover:
hr:hover::before{ rotate:180deg }with a transition — a delightful, nearly free micro-interaction on an editorial page. - Coloured rule, neutral glyph (or the reverse): the rule reads
--lineand the ornament reads--acc; they are independent tokens on purpose.
Gotchas — read before shipping
- The pseudo-element's
backgroundmust match the real page background exactly, or you'll see a pale rectangle behind the glyph. Read it from the same custom property, never a hard-coded hex. content:attr()only works on the content property (attr() in other properties is still not shipped anywhere), so this pattern is fine butwidth:attr(data-w px)is not.- Glyph coverage varies: ❖ and ⁂ are missing from some Android system fonts and fall back to tofu. Test, or use an SVG data URI for anything exotic.
- Unicode escapes in CSS need the hex form —
content:'\2766', not the literal character — if your stylesheet isn't served as UTF-8.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 114+ | 17.5+ | 121+ | 114+ |
Floor set by oklch(), text-wrap as this demo is written. The core technique itself goes back further — Chrome 111+.
content:attr() on ::before and pseudo-elements on <hr> work back to IE9. The floor is only the oklch() tokens; the modern `translate` property (Chrome 104 / Safari 14.1 / Firefox 72) can be swapped for transform:translate().