26 CSS Dividers25 / 26

Pure CSSMIT licensed

Vertical Flexbox Divider Line CSS

Vertical rules between nav links, header stats and toolbar groups — the divider people hand-code wrong most often. The correct answer is a sibling selector plus align-self:stretch, so the rule is exactly as tall as the tallest item, never a fixed pixel height. Includes the flex-wrap trap that makes stretch rules look broken, and the one-line fix.

Published Updated

Live Demo
Try it

The code

<section class="div-25" aria-label="Vertical flexbox divider demo">
  <div class="div-25__stage">
    <nav class="div-25__nav" aria-label="Demo navigation">
      <a href="#div-25">Overview</a><a href="#div-25">Pricing</a><a href="#div-25">Docs</a><a href="#div-25">Changelog</a>
    </nav>
    <div class="div-25__stats">
      <div class="div-25__stat"><p class="div-25__v">26</p><p class="div-25__k">Dividers</p></div>
      <div class="div-25__stat"><p class="div-25__v">4</p><p class="div-25__k">With JS</p></div>
      <div class="div-25__stat"><p class="div-25__v">0</p><p class="div-25__k">Dependencies</p></div>
    </div>
    <div class="div-25__bar">
      <span class="div-25__tool">Bold</span><span class="div-25__tool">Italic</span>
      <span class="div-25__vr" role="separator" aria-orientation="vertical"></span>
      <span class="div-25__tool">Left</span><span class="div-25__tool">Center</span>
      <span class="div-25__vr" role="separator" aria-orientation="vertical"></span>
      <span class="div-25__tool">Link</span>
    </div>
    <p class="div-25__chip">* + *::before · align-self:stretch · axis flip on wrap</p>
  </div>
</section>
.div-25 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  background: var(--div-25-bg);
  --div-25-bg: oklch(0.97 0.005 250);
  --div-25-card: oklch(1 0 0);
  --div-25-ink: oklch(0.23 0.018 260);
  --div-25-mut: oklch(0.55 0.018 260);
  --div-25-line: oklch(0.23 0.018 260/.18);
  --div-25-acc: oklch(0.58 0.16 262);
  --div-25-gap: 24px;
  font-family: 'Segoe UI',system-ui,-apple-system,sans-serif;
  color: var(--div-25-ink);
}

.div-25 *,
.div-25 *::before,
.div-25 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.div-25__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);
}

.div-25__nav {
  display: flex;
  align-items: stretch;
  gap: var(--div-25-gap);
  background: var(--div-25-card);
  border: 1px solid var(--div-25-line);
  border-radius: 99px;
  padding: 10px 24px;
  box-shadow: 0 8px 26px oklch(0.23 0.018 260/.07);
  flex-wrap: wrap;
  justify-content: center;
}

.div-25__nav a {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  font-size: 14px;
  font-weight: 600;
  color: var(--div-25-mut);
  text-decoration: none;
  transition: color .18s ease;
}

.div-25__nav a:hover,
.div-25__nav a:focus-visible {
  color: var(--div-25-acc);
}

.div-25__nav a:focus-visible {
  outline: 2px solid var(--div-25-acc);
  outline-offset: 4px;
  border-radius: 6px;
}

.div-25__nav a+a::before {
  content: '';
  position: absolute;
  left: calc(var(--div-25-gap) / -2);
  inset-block: 6px;
  width: 1px;
  background: var(--div-25-line);
}

.div-25__stats {
  display: flex;
  align-items: stretch;
  gap: var(--div-25-gap);
  flex-wrap: wrap;
  justify-content: center;
}

.div-25__stat {
  position: relative;
  padding: 6px 10px;
  text-align: center;
  min-width: 104px;
}

.div-25__stat+.div-25__stat::before {
  content: '';
  position: absolute;
  left: calc(var(--div-25-gap) / -2);
  inset-block: 2px;
  width: 1px;
  background: linear-gradient(180deg,transparent,var(--div-25-line) 22%,var(--div-25-line) 78%,transparent);
}

.div-25__v {
  font-size: clamp(28px,5vw,42px);
  font-weight: 720;
  letter-spacing: -.035em;
  line-height: 1;
  color: var(--div-25-acc);
}

.div-25__k {
  margin-top: 6px;
  font-size: 11.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--div-25-mut);
}

.div-25__bar {
  display: flex;
  align-items: stretch;
  gap: 6px;
  background: var(--div-25-card);
  border: 1px solid var(--div-25-line);
  border-radius: 12px;
  padding: 6px;
  box-shadow: 0 6px 20px oklch(0.23 0.018 260/.07);
  flex-wrap: wrap;
  justify-content: center;
}

.div-25__tool {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 0 13px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--div-25-mut);
  cursor: default;
  transition: background .16s ease,color .16s ease;
}

.div-25__tool:hover {
  background: color-mix(in oklch,var(--div-25-acc) 10%,transparent);
  color: var(--div-25-acc);
}

.div-25__vr {
  align-self: stretch;
  width: 1px;
  flex: none;
  background: var(--div-25-line);
  margin-inline: 5px;
}

@media (max-width: 420px) {
  .div-25__stats {
    flex-direction: column;
    align-items: center;
  }

  .div-25__stat+.div-25__stat::before {
    left: 0;
    right: 0;
    width: auto;
    height: 1px;
    inset-block: auto;
    top: calc(var(--div-25-gap) / -2);
    background: linear-gradient(90deg,transparent,var(--div-25-line),transparent);
  }
}

.div-25__chip {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11px;
  color: var(--div-25-mut);
  padding: 8px 14px;
  border: 1px solid var(--div-25-line);
  border-radius: 99px;
  text-align: center;
}

@media (prefers-reduced-motion: reduce) {
  .div-25 * {
    transition-duration: .01ms !important;
  }
}
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
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: Vertical Flexbox Divider Line CSS
Source: https://codefronts.com/snippets/css-dividers/corporate-dividers/

Vertical rules between nav links, header stats and toolbar groups — the divider people hand-code wrong most often. The correct answer is a sibling selector plus align-self:stretch, so the rule is exactly as tall as the tallest item, never a fixed pixel height. Includes the flex-wrap trap that makes stretch rules look broken, and the one-line fix.
## HTML
```html
<section class="div-25" aria-label="Vertical flexbox divider demo">
  <div class="div-25__stage">
    <nav class="div-25__nav" aria-label="Demo navigation">
      <a href="#div-25">Overview</a><a href="#div-25">Pricing</a><a href="#div-25">Docs</a><a href="#div-25">Changelog</a>
    </nav>
    <div class="div-25__stats">
      <div class="div-25__stat"><p class="div-25__v">26</p><p class="div-25__k">Dividers</p></div>
      <div class="div-25__stat"><p class="div-25__v">4</p><p class="div-25__k">With JS</p></div>
      <div class="div-25__stat"><p class="div-25__v">0</p><p class="div-25__k">Dependencies</p></div>
    </div>
    <div class="div-25__bar">
      <span class="div-25__tool">Bold</span><span class="div-25__tool">Italic</span>
      <span class="div-25__vr" role="separator" aria-orientation="vertical"></span>
      <span class="div-25__tool">Left</span><span class="div-25__tool">Center</span>
      <span class="div-25__vr" role="separator" aria-orientation="vertical"></span>
      <span class="div-25__tool">Link</span>
    </div>
    <p class="div-25__chip">* + *::before · align-self:stretch · axis flip on wrap</p>
  </div>
</section>
```
## CSS
```css
.div-25 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  background: var(--div-25-bg);
  --div-25-bg: oklch(0.97 0.005 250);
  --div-25-card: oklch(1 0 0);
  --div-25-ink: oklch(0.23 0.018 260);
  --div-25-mut: oklch(0.55 0.018 260);
  --div-25-line: oklch(0.23 0.018 260/.18);
  --div-25-acc: oklch(0.58 0.16 262);
  --div-25-gap: 24px;
  font-family: 'Segoe UI',system-ui,-apple-system,sans-serif;
  color: var(--div-25-ink);
}

.div-25 *,
.div-25 *::before,
.div-25 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.div-25__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);
}

.div-25__nav {
  display: flex;
  align-items: stretch;
  gap: var(--div-25-gap);
  background: var(--div-25-card);
  border: 1px solid var(--div-25-line);
  border-radius: 99px;
  padding: 10px 24px;
  box-shadow: 0 8px 26px oklch(0.23 0.018 260/.07);
  flex-wrap: wrap;
  justify-content: center;
}

.div-25__nav a {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  font-size: 14px;
  font-weight: 600;
  color: var(--div-25-mut);
  text-decoration: none;
  transition: color .18s ease;
}

.div-25__nav a:hover,
.div-25__nav a:focus-visible {
  color: var(--div-25-acc);
}

.div-25__nav a:focus-visible {
  outline: 2px solid var(--div-25-acc);
  outline-offset: 4px;
  border-radius: 6px;
}

.div-25__nav a+a::before {
  content: '';
  position: absolute;
  left: calc(var(--div-25-gap) / -2);
  inset-block: 6px;
  width: 1px;
  background: var(--div-25-line);
}

.div-25__stats {
  display: flex;
  align-items: stretch;
  gap: var(--div-25-gap);
  flex-wrap: wrap;
  justify-content: center;
}

.div-25__stat {
  position: relative;
  padding: 6px 10px;
  text-align: center;
  min-width: 104px;
}

.div-25__stat+.div-25__stat::before {
  content: '';
  position: absolute;
  left: calc(var(--div-25-gap) / -2);
  inset-block: 2px;
  width: 1px;
  background: linear-gradient(180deg,transparent,var(--div-25-line) 22%,var(--div-25-line) 78%,transparent);
}

.div-25__v {
  font-size: clamp(28px,5vw,42px);
  font-weight: 720;
  letter-spacing: -.035em;
  line-height: 1;
  color: var(--div-25-acc);
}

.div-25__k {
  margin-top: 6px;
  font-size: 11.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--div-25-mut);
}

.div-25__bar {
  display: flex;
  align-items: stretch;
  gap: 6px;
  background: var(--div-25-card);
  border: 1px solid var(--div-25-line);
  border-radius: 12px;
  padding: 6px;
  box-shadow: 0 6px 20px oklch(0.23 0.018 260/.07);
  flex-wrap: wrap;
  justify-content: center;
}

.div-25__tool {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 0 13px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--div-25-mut);
  cursor: default;
  transition: background .16s ease,color .16s ease;
}

.div-25__tool:hover {
  background: color-mix(in oklch,var(--div-25-acc) 10%,transparent);
  color: var(--div-25-acc);
}

.div-25__vr {
  align-self: stretch;
  width: 1px;
  flex: none;
  background: var(--div-25-line);
  margin-inline: 5px;
}

@media (max-width: 420px) {
  .div-25__stats {
    flex-direction: column;
    align-items: center;
  }

  .div-25__stat+.div-25__stat::before {
    left: 0;
    right: 0;
    width: auto;
    height: 1px;
    inset-block: auto;
    top: calc(var(--div-25-gap) / -2);
    background: linear-gradient(90deg,transparent,var(--div-25-line),transparent);
  }
}

.div-25__chip {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11px;
  color: var(--div-25-mut);
  padding: 8px 14px;
  border: 1px solid var(--div-25-line);
  border-radius: 99px;
  text-align: center;
}

@media (prefers-reduced-motion: reduce) {
  .div-25 * {
    transition-duration: .01ms !important;
  }
}
```

How this works

Two rules do the whole job:

.group{ display:flex; align-items:stretch; gap:20px }
.group > * + *::before{ content:''; position:absolute;
  left:calc(var(--gap) / -2); inset-block:4px; width:1px;
  background:var(--line); }

Using * + * ("every child that follows another child") means the rule exists between items only — no leading rule, no trailing rule, and no :last-child exception to forget. Positioning it at half the negative gap centres it optically in the gutter no matter what the gap is.

When the divider is a real element rather than a pseudo-element, align-self:stretch is the key:

.vr{ align-self:stretch; width:1px; background:var(--line) }

A stretched flex item takes the cross-size of the line, so the rule automatically matches the tallest sibling — no height:24px guess that breaks when someone adds a second line of text.

The trap: with flex-wrap:wrap, a stretched divider that wraps to a new line becomes a stray tick. The fix is a container query or a simple @media that flips the group to flex-direction:column and turns the rules horizontal — the same markup, one property changed, which is why building these as ::before pseudo-elements pays off.

Make it yours

  • Faded rules: background:linear-gradient(var(--line),transparent) gives a rule that dissolves downward — nice under a sticky header.
  • Dot separators: swap width/height for 3px and add border-radius:99px, then centre it with align-self:center instead of stretch.
  • Responsive flip: in the column layout, change the pseudo-element to height:1px;width:100%;top:calc(var(--gap)/-2) and the same code serves both axes.
  • Inset the rule: inset-block:6px keeps it clear of ascenders and descenders — a small optical correction that makes toolbars look designed.

Gotchas — read before shipping

  • height:100% on a flex child does nothing useful here. align-self:stretch is the property that matches the tallest sibling.
  • With flex-wrap, a stretch divider on a wrapped item leaves an orphan tick at the start of the new line. Flip the axis at your breakpoint.
  • :not(:last-child) breaks when items are hidden or filtered; * + * is immune because it depends on actual adjacency in the rendered tree.
  • A vertical rule is decoration in a nav but a boundary in a toolbar. If it separates control groups, ensure it clears 3:1 contrast — otherwise a hairline at 10% opacity is fine.

Browser support

ChromeSafariFirefoxEdge
111+16.4+113+111+

Flexbox, align-self:stretch and the adjacent-sibling selector work back to IE11 (with prefixes). inset-block needs Chrome 87 / Safari 14.1 / Firefox 63; use top/bottom to go further back.

Search CodeFronts

Loading…