26 CSS Dividers07 / 26

Light JSMIT licensed

Blob Organic SVG Divider CSS

Fluid, non-geometric shapes for portfolio and product pages — plus the thing most blob tutorials skip: where the curve numbers come from. A 22-line generator walks a circle, jitters each radius, and emits a smooth cubic path, so "Reshuffle" gives you an infinite supply of on-brand blobs. A second, purely-CSS blob morphs forever using animated border-radius, for when you want organic motion with zero script.

Published

Live Demo
Try it

The code

<section class="div-07" aria-label="Organic blob SVG divider demo">
  <div class="div-07__top">
    <div class="div-07__blob" aria-hidden="true"></div>
    <p class="div-07__eyebrow">generated path · morphing radius</p>
    <h2 class="div-07__title">Shapes that never repeat</h2>
    <div class="div-07__wrap" aria-hidden="true">
      <svg class="div-07__svg" id="div-07-svg" viewBox="0 0 1200 160" preserveAspectRatio="none" focusable="false">
        <path id="div-07-path" d="M0,90 C200,140 380,40 620,78 C860,116 1010,150 1200,96 L1200,160 L0,160 Z"></path>
      </svg>
    </div>
  </div>
  <div class="div-07__bottom">
    <p class="div-07__lede">Each reshuffle walks a circle, jitters every radius, then smooths the vertices into cubic segments.</p>
    <button class="div-07__btn" id="div-07-shuffle" type="button">Reshuffle the blob</button>
    <p class="div-07__chip">Catmull-Rom smoothing · 8-value border-radius morph</p>
  </div>
</section>
.div-07 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  background: var(--div-07-bg);
  --div-07-bg: oklch(0.96 0.03 190);
  --div-07-top: oklch(0.35 0.11 300);
  --div-07-ink: oklch(0.98 0.01 300);
  --div-07-dark: oklch(0.28 0.04 290);
  --div-07-mut: oklch(0.53 0.03 290);
  --div-07-acc: oklch(0.74 0.17 340);
  font-family: 'Segoe UI',system-ui,-apple-system,sans-serif;
  color: var(--div-07-dark);
}

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

.div-07__top {
  position: relative;
  min-height: 56svh;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 12px;
  text-align: center;
  color: var(--div-07-ink);
  background: linear-gradient(150deg,var(--div-07-top),oklch(0.28 0.09 265));
  padding: clamp(34px,7vw,80px) clamp(20px,5vw,56px) clamp(80px,13vw,150px);
  overflow: hidden;
}

.div-07__blob {
  position: absolute;
  width: min(40vw,300px);
  aspect-ratio: 1;
  top: -16%;
  right: -11%;
  background: radial-gradient(circle at 35% 30%,var(--div-07-acc),oklch(0.55 0.19 320/.35));
  filter: blur(3px);
  opacity: .4;
  border-radius: 42% 58% 63% 37%/41% 44% 56% 59%;
  animation: div-07-morph 14s ease-in-out infinite,div-07-spin 32s linear infinite;
}

@keyframes div-07-morph {
  0%,
    100% {
    border-radius: 42% 58% 63% 37%/41% 44% 56% 59%;
  }

  33% {
    border-radius: 63% 37% 41% 59%/57% 63% 37% 43%;
  }

  66% {
    border-radius: 36% 64% 55% 45%/62% 38% 62% 38%;
  }
}

@keyframes div-07-spin {
  to {
    transform: rotate(360deg);
  }
}

.div-07__eyebrow {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11.5px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--div-07-acc);
}

.div-07__title {
  font-size: clamp(28px,5vw,52px);
  line-height: 1.05;
  letter-spacing: -.03em;
  font-weight: 700;
  text-wrap: balance;
}

.div-07__wrap {
  position: absolute;
  inset: auto 0 -1px;
  height: clamp(72px,11vw,160px);
  line-height: 0;
  pointer-events: none;
}

.div-07__svg {
  width: 100%;
  height: 100%;
  display: block;
}

.div-07__svg path {
  fill: var(--div-07-bg);
  transition: d .8s cubic-bezier(.65,0,.35,1);
}

.div-07__bottom {
  min-height: 44svh;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 18px;
  text-align: center;
  padding: clamp(28px,5vw,60px) clamp(20px,5vw,56px);
}

.div-07__lede {
  max-width: 52ch;
  font-size: clamp(15px,1.9vw,19px);
  line-height: 1.62;
  text-wrap: pretty;
}

.div-07__btn {
  min-height: 46px;
  padding: 0 24px;
  border: 0;
  border-radius: 99px;
  background: linear-gradient(120deg,var(--div-07-acc),oklch(0.62 0.19 300));
  color: oklch(0.99 0.01 340);
  font-family: inherit;
  font-size: 14.5px;
  font-weight: 650;
  cursor: pointer;
  box-shadow: 0 8px 22px oklch(0.62 0.19 320/.32);
  transition: transform .18s ease,box-shadow .18s ease;
}

.div-07__btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 12px 28px oklch(0.62 0.19 320/.42);
}

.div-07__btn:active {
  transform: translateY(0);
}

.div-07__btn:focus-visible {
  outline: 2px solid var(--div-07-top);
  outline-offset: 3px;
}

.div-07__chip {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11.5px;
  color: var(--div-07-mut);
  padding: 8px 14px;
  border: 1px solid oklch(0.28 0.04 290/.16);
  border-radius: 99px;
}

@media (prefers-reduced-motion: reduce) {
  .div-07__blob {
    animation: none;
  }

  .div-07__svg path {
    transition: none;
  }
}
(() => {
  const svg = document.getElementById('div-07-svg');
  const path = document.getElementById('div-07-path');
  const btn = document.getElementById('div-07-shuffle');
  if (!svg || !path || !btn || btn.dataset.ppWired) return;
  btn.dataset.ppWired = '1';
  const W = 1200, H = 160, N = 7, WOBBLE = 0.42;
  const build = () => {
    const ys = Array.from({ length: N + 1 }, () => 30 + Math.random() * WOBBLE * 150);
    ys[N] = ys[0];
    const step = W / N;
    let d = 'M0,' + ys[0].toFixed(1);
    for (let i = 0; i < N; i++) {
      const x0 = i * step, x1 = (i + 1) * step, cx = step * 0.42;
      d += ' C' + (x0 + cx).toFixed(1) + ',' + ys[i].toFixed(1) +
           ' ' + (x1 - cx).toFixed(1) + ',' + ys[i + 1].toFixed(1) +
           ' ' + x1.toFixed(1) + ',' + ys[i + 1].toFixed(1);
    }
    path.setAttribute('d', d + ' L' + W + ',' + H + ' L0,' + H + ' Z');
  };
  btn.addEventListener('click', build);
  build();
})();
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: Blob Organic SVG Divider CSS
Source: https://codefronts.com/snippets/css-dividers/blob-organic-svg-divider-css/

Fluid, non-geometric shapes for portfolio and product pages — plus the thing most blob tutorials skip: where the curve numbers come from. A 22-line generator walks a circle, jitters each radius, and emits a smooth cubic path, so "Reshuffle" gives you an infinite supply of on-brand blobs. A second, purely-CSS blob morphs forever using animated border-radius, for when you want organic motion with zero script.
## HTML
```html
<section class="div-07" aria-label="Organic blob SVG divider demo">
  <div class="div-07__top">
    <div class="div-07__blob" aria-hidden="true"></div>
    <p class="div-07__eyebrow">generated path · morphing radius</p>
    <h2 class="div-07__title">Shapes that never repeat</h2>
    <div class="div-07__wrap" aria-hidden="true">
      <svg class="div-07__svg" id="div-07-svg" viewBox="0 0 1200 160" preserveAspectRatio="none" focusable="false">
        <path id="div-07-path" d="M0,90 C200,140 380,40 620,78 C860,116 1010,150 1200,96 L1200,160 L0,160 Z"></path>
      </svg>
    </div>
  </div>
  <div class="div-07__bottom">
    <p class="div-07__lede">Each reshuffle walks a circle, jitters every radius, then smooths the vertices into cubic segments.</p>
    <button class="div-07__btn" id="div-07-shuffle" type="button">Reshuffle the blob</button>
    <p class="div-07__chip">Catmull-Rom smoothing · 8-value border-radius morph</p>
  </div>
</section>
```
## CSS
```css
.div-07 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  background: var(--div-07-bg);
  --div-07-bg: oklch(0.96 0.03 190);
  --div-07-top: oklch(0.35 0.11 300);
  --div-07-ink: oklch(0.98 0.01 300);
  --div-07-dark: oklch(0.28 0.04 290);
  --div-07-mut: oklch(0.53 0.03 290);
  --div-07-acc: oklch(0.74 0.17 340);
  font-family: 'Segoe UI',system-ui,-apple-system,sans-serif;
  color: var(--div-07-dark);
}

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

.div-07__top {
  position: relative;
  min-height: 56svh;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 12px;
  text-align: center;
  color: var(--div-07-ink);
  background: linear-gradient(150deg,var(--div-07-top),oklch(0.28 0.09 265));
  padding: clamp(34px,7vw,80px) clamp(20px,5vw,56px) clamp(80px,13vw,150px);
  overflow: hidden;
}

.div-07__blob {
  position: absolute;
  width: min(40vw,300px);
  aspect-ratio: 1;
  top: -16%;
  right: -11%;
  background: radial-gradient(circle at 35% 30%,var(--div-07-acc),oklch(0.55 0.19 320/.35));
  filter: blur(3px);
  opacity: .4;
  border-radius: 42% 58% 63% 37%/41% 44% 56% 59%;
  animation: div-07-morph 14s ease-in-out infinite,div-07-spin 32s linear infinite;
}

@keyframes div-07-morph {
  0%,
    100% {
    border-radius: 42% 58% 63% 37%/41% 44% 56% 59%;
  }

  33% {
    border-radius: 63% 37% 41% 59%/57% 63% 37% 43%;
  }

  66% {
    border-radius: 36% 64% 55% 45%/62% 38% 62% 38%;
  }
}

@keyframes div-07-spin {
  to {
    transform: rotate(360deg);
  }
}

.div-07__eyebrow {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11.5px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--div-07-acc);
}

.div-07__title {
  font-size: clamp(28px,5vw,52px);
  line-height: 1.05;
  letter-spacing: -.03em;
  font-weight: 700;
  text-wrap: balance;
}

.div-07__wrap {
  position: absolute;
  inset: auto 0 -1px;
  height: clamp(72px,11vw,160px);
  line-height: 0;
  pointer-events: none;
}

.div-07__svg {
  width: 100%;
  height: 100%;
  display: block;
}

.div-07__svg path {
  fill: var(--div-07-bg);
  transition: d .8s cubic-bezier(.65,0,.35,1);
}

.div-07__bottom {
  min-height: 44svh;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 18px;
  text-align: center;
  padding: clamp(28px,5vw,60px) clamp(20px,5vw,56px);
}

.div-07__lede {
  max-width: 52ch;
  font-size: clamp(15px,1.9vw,19px);
  line-height: 1.62;
  text-wrap: pretty;
}

.div-07__btn {
  min-height: 46px;
  padding: 0 24px;
  border: 0;
  border-radius: 99px;
  background: linear-gradient(120deg,var(--div-07-acc),oklch(0.62 0.19 300));
  color: oklch(0.99 0.01 340);
  font-family: inherit;
  font-size: 14.5px;
  font-weight: 650;
  cursor: pointer;
  box-shadow: 0 8px 22px oklch(0.62 0.19 320/.32);
  transition: transform .18s ease,box-shadow .18s ease;
}

.div-07__btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 12px 28px oklch(0.62 0.19 320/.42);
}

.div-07__btn:active {
  transform: translateY(0);
}

.div-07__btn:focus-visible {
  outline: 2px solid var(--div-07-top);
  outline-offset: 3px;
}

.div-07__chip {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11.5px;
  color: var(--div-07-mut);
  padding: 8px 14px;
  border: 1px solid oklch(0.28 0.04 290/.16);
  border-radius: 99px;
}

@media (prefers-reduced-motion: reduce) {
  .div-07__blob {
    animation: none;
  }

  .div-07__svg path {
    transition: none;
  }
}
```

## JavaScript
```js
(() => {
  const svg = document.getElementById('div-07-svg');
  const path = document.getElementById('div-07-path');
  const btn = document.getElementById('div-07-shuffle');
  if (!svg || !path || !btn || btn.dataset.ppWired) return;
  btn.dataset.ppWired = '1';
  const W = 1200, H = 160, N = 7, WOBBLE = 0.42;
  const build = () => {
    const ys = Array.from({ length: N + 1 }, () => 30 + Math.random() * WOBBLE * 150);
    ys[N] = ys[0];
    const step = W / N;
    let d = 'M0,' + ys[0].toFixed(1);
    for (let i = 0; i < N; i++) {
      const x0 = i * step, x1 = (i + 1) * step, cx = step * 0.42;
      d += ' C' + (x0 + cx).toFixed(1) + ',' + ys[i].toFixed(1) +
           ' ' + (x1 - cx).toFixed(1) + ',' + ys[i + 1].toFixed(1) +
           ' ' + x1.toFixed(1) + ',' + ys[i + 1].toFixed(1);
    }
    path.setAttribute('d', d + ' L' + W + ',' + H + ' L0,' + H + ' Z');
  };
  btn.addEventListener('click', build);
  build();
})();
```

How this works

A blob is a closed loop of points around a centre with randomised radii, joined by curves rather than lines. The generator does exactly that:

const pts = Array.from({length:n}, (_, i) => {
  const a = (i / n) * Math.PI * 2;
  const r = base * (1 + (Math.random() - .5) * wobble);
  return [cx + Math.cos(a) * r, cy + Math.sin(a) * r];
});

Joining them with a Catmull-Rom-style smoothing (each segment's control points derived from the neighbouring vertices) is what turns a spiky star into something that looks poured. Fewer points plus more wobble reads as a lava lamp; more points plus less wobble reads as a pebble.

The CSS-only blob is the other half of the technique, and it is one property:

@keyframes morph{
  0%,100%{ border-radius:42% 58% 63% 37% / 41% 44% 56% 59% }
  50%    { border-radius:63% 37% 41% 59% / 57% 63% 37% 43% }
}

Eight-value border-radius gives independent control of every corner's horizontal and vertical radius, and all eight interpolate — so the shape genuinely morphs rather than cross-fading. Pair a slow morph with a slow rotate at a different duration and the loop never visibly repeats.

Make it yours

  • Wobble: 0.12 gives a barely-organic pebble; 0.45 gives a splat. Above 0.6 the smoothing can self-intersect — clamp it.
  • Point count: 5–8 points is the sweet spot for section dividers. 3 points makes a triangle-ish shape; 20 points converges back to a circle.
  • Colour depth: stack two blobs with the back one scaled 1.08, blurred 20px and at 35% opacity for a soft-shadow silhouette that follows the shape.
  • Blob as an image mask: put the generated path in an SVG <clipPath clipPathUnits="objectBoundingBox"> and reference it with clip-path:url(#id) to pour a photo into the blob.

Gotchas — read before shipping

  • Never animate the SVG d attribute with a CSS transition — it isn't interpolatable in CSS. Either regenerate the path (as here), use SMIL, or morph with border-radius instead.
  • border-radius morphs only look smooth if every keyframe uses the SAME eight-value form; mixing a four-value and an eight-value keyframe makes the browser snap.
  • Truly random blobs go off-brand fast. Seed the randomness or constrain the wobble range so every reshuffle still looks like your product.
  • A decorative blob must be aria-hidden and pointer-events:none — SVG in the accessibility tree announces as 'image' or, worse, exposes its title.

Browser support

ChromeSafariFirefoxEdge
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+.

Inline SVG paths and eight-value border-radius animation are universal. The generator uses only ES2015 (arrow functions, template literals) and needs no polyfill in any evergreen browser.

Techniques used in this demo

Search CodeFronts

Loading…