# Blob From Three Crossfading SVG Outlines

A shadcn/ui block from the [@weinc](https://we.inc/components?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/weinc/blob-from-three-crossfading-svg-outlines).

- Page, with a live preview: https://shadcn.exchange/r/weinc/blob-from-three-crossfading-svg-outlines
- Screenshot: https://shadcn.exchange/shot/weinc/blob-from-three-crossfading-svg-outlines/light.png
- Registry item: https://we.inc/r/blob-from-three-crossfading-svg-outlines.json
- Listed: 2026-10-28, source last changed 2026-10-28

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/weinc/blob-from-three-crossfading-svg-outlines.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Files

### `components/we-inc/blob-from-three-crossfading-svg-outlines.tsx`

```tsx
import { useEffect, useRef } from "react";

// Colours and timings live at the top of the CSS below; every shape is fixed and only its transform or opacity moves.
const CSS = `
.m2xfd { --m2xfd-bg: #f7f1ea; --m2xfd-f1: #e9c4b8; --m2xfd-f2: #f0d3bf; --m2xfd-f3: #e4b2a6; --m2xfd-ink: #2a2421; --m2xfd-btn: #3f5a3c; }
.m2xfd *, .m2xfd *::before, .m2xfd *::after { box-sizing: border-box; }
.m2xfd { box-sizing: border-box; background: var(--m2xfd-bg); color: var(--m2xfd-ink); padding: clamp(48px, 8vw, 96px) 20px; font-family: "Inter Tight", "Inter", ui-sans-serif, system-ui, sans-serif; overflow: hidden; }
.m2xfd-in { max-width: 1000px; margin: 0 auto; display: grid; grid-template-columns: 1fr 1.2fr; align-items: center; gap: 48px; }
.m2xfd-art { width: min(360px, 74vw); aspect-ratio: 1; margin: 0 auto; }
.m2xfd-svg { width: 100%; height: 100%; overflow: visible; }
.m2xfd-base { fill: var(--m2xfd-f2); opacity: .35; }
.m2xfd-s { transform-box: fill-box; transform-origin: center; opacity: 0; }
.m2xfd-s1 { fill: var(--m2xfd-f1); opacity: 1; }
.m2xfd-s2 { fill: var(--m2xfd-f2); }
.m2xfd-s3 { fill: var(--m2xfd-f3); }
.m2xfd-s { animation: m2xfd-x 12s ease-in-out infinite; }
.m2xfd-s2 { animation-delay: -8s; }
.m2xfd-s3 { animation-delay: -4s; }
@keyframes m2xfd-x {
0% { opacity: 1; transform: rotate(0) scale(1); }
30% { opacity: 1; transform: rotate(8deg) scale(1.02); }
46% { opacity: 0; transform: rotate(14deg) scale(1.03); }
84% { opacity: 0; transform: rotate(-10deg) scale(.98); }
100% { opacity: 1; transform: rotate(0) scale(1); }
}
.m2xfd[data-off] .m2xfd-s { animation-play-state: paused; }
.m2xfd-kicker { margin: 0; color: #8a4a3a; font-size: 13px; font-weight: 600; letter-spacing: .14em; text-transform: uppercase; }
.m2xfd-h { margin: 14px 0 0; font-family: Georgia, "Times New Roman", serif; font-weight: 400; font-size: clamp(28px, 3.8vw, 40px); line-height: 1.15; }
.m2xfd-p { margin: 16px 0 0; max-width: 32rem; color: #5a4e48; font-size: 16px; line-height: 1.65; }
.m2xfd-form { margin-top: 28px; max-width: 30rem; }
.m2xfd-label { display: block; margin-bottom: 8px; font-size: 14px; font-weight: 600; }
.m2xfd-row { display: flex; gap: 8px; }
.m2xfd-input { flex: 1; min-width: 0; height: 48px; padding: 0 14px; border: 1px solid #c9b9ab; border-radius: 12px; background: #fffdf9; color: var(--m2xfd-ink); font: inherit; font-size: 16px; }
.m2xfd-input::placeholder { color: #7d716a; }
.m2xfd-input:focus-visible { outline: 2px solid var(--m2xfd-btn); outline-offset: 1px; border-color: var(--m2xfd-btn); }
.m2xfd-btn { height: 48px; padding: 0 22px; border: 0; border-radius: 12px; background: var(--m2xfd-btn); color: #fff; font: inherit; font-size: 15px; font-weight: 600; cursor: pointer; }
.m2xfd-btn:hover { background: #2f452d; }
.m2xfd-btn:focus-visible { outline: 2px solid var(--m2xfd-btn); outline-offset: 3px; }
.m2xfd-note { margin: 10px 0 0; color: #6a5d56; font-size: 13px; }
@media (max-width: 760px) { .m2xfd-in { grid-template-columns: 1fr; gap: 28px; } .m2xfd-art { width: min(260px, 70vw); } }
@media (max-width: 400px) { .m2xfd-row { flex-direction: column; } .m2xfd-input { flex: none; width: 100%; } }
@media (prefers-reduced-motion: reduce) { .m2xfd-s { animation: none; } }
`;

export default function BlobFromThreeCrossfadingSvgOutlines() {
  const rootRef = useRef<HTMLElement>(null);

  // Pause the keyframes whenever the piece is scrolled out of view.
  useEffect(() => {
    const root = rootRef.current;
    if (!root || typeof IntersectionObserver === "undefined") return;
    const io = new IntersectionObserver(([en]) => {
      if (en.isIntersecting) root.removeAttribute("data-off");
      else root.setAttribute("data-off", "");
    });
    io.observe(root);
    return () => io.disconnect();
  }, []);

  return (
<section ref={rootRef} className="m2xfd" aria-labelledby="m2xfd-h">
<style>{CSS}</style>
  <div className="m2xfd-in">
    <div className="m2xfd-art" aria-hidden="true">
      <svg className="m2xfd-svg" viewBox="0 0 200 200">
        <path className="m2xfd-base" d="M186.9 104.3C187.6 127.0 168.2 164.9 148.9 178.8C129.6 192.7 89.7 192.8 71.0 187.5C52.2 182.2 43.0 169.8 36.5 147.0C29.9 124.2 21.9 69.9 31.7 50.6C41.5 31.3 76.8 32.6 95.6 31.3C114.4 29.9 129.4 30.4 144.6 42.6C159.8 54.8 186.1 81.6 186.9 104.3Z" />
        <path className="m2xfd-s m2xfd-s1" d="M184.5 99.1C182.1 123.2 139.8 158.1 117.1 167.6C94.3 177.1 63.7 174.9 48.1 156.1C32.5 137.2 9.7 76.6 23.6 54.4C37.5 32.3 104.7 15.9 131.6 23.3C158.4 30.8 186.9 75.1 184.5 99.1Z" />
        <path className="m2xfd-s m2xfd-s2" d="M176.3 98.9C178.4 125.5 161.9 171.2 140.1 181.2C118.3 191.2 60.4 180.4 45.7 158.9C31.0 137.3 38.4 74.8 52.1 51.9C65.7 28.9 106.8 13.5 127.5 21.3C148.2 29.1 174.2 72.2 176.3 98.9Z" />
        <path className="m2xfd-s m2xfd-s3" d="M188.8 100.8C186.8 121.4 154.2 151.9 134.7 165.3C115.3 178.7 88.5 187.8 72.1 181.2C55.7 174.6 43.9 142.6 36.2 125.8C28.5 109.0 16.8 96.7 25.9 80.3C35.0 64.0 70.7 34.2 90.9 27.7C111.0 21.3 130.3 29.4 146.6 41.6C163.0 53.8 190.8 80.2 188.8 100.8Z" />
        <g className="m2xfd-stem" fill="none" stroke="#3f5a3c" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
          <path d="M100 160C100 132 98 110 92 84" />
          <path d="M96 120C110 114 120 104 124 90C110 92 100 102 96 120Z" />
          <path d="M94 100C82 96 74 86 72 74C84 76 92 86 94 100Z" />
          <circle cx="91" cy="72" r="11" />
        </g>
      </svg>
    </div>
    <div className="m2xfd-copy">
      <p className="m2xfd-kicker">Mossgate Flowers</p>
      <h2 className="m2xfd-h" id="m2xfd-h">One letter on Fridays about what is in bloom this week.</h2>
      <p className="m2xfd-p">Which stems came in from the growers, how long they last in a vase and a short note on arranging them at home.</p>
      <form className="m2xfd-form" action="#">
        <label className="m2xfd-label" htmlFor="m2xfd-email">Email address</label>
        <div className="m2xfd-row">
          <input className="m2xfd-input" id="m2xfd-email" type="email" name="email" autoComplete="email" placeholder="you@example.com" required />
          <button className="m2xfd-btn" type="submit">Sign up</button>
        </div>
        <p className="m2xfd-note">You can leave the list from any letter.</p>
      </form>
    </div>
  </div>
</section>
  );
}
```
