Skip to content
Plinth

Backgrounds

Silk Ribbons

Blurred ellipses of saturated light drifting and crossing on screen blend, so overlaps brighten like folds catching the light.

#background#silk#gradient#blend-mode#animation

Install

npx shadcn@latest add https://plinthui.com/r/silk-ribbons.json
Open in v0
// No hooks or handlers, so this stays a server component — it is pure CSS.
const css = `
.silk {
  position: relative;
  min-height: 300px;
  height: 100%;
  overflow: hidden;
  background: radial-gradient(130% 100% at 50% 0%, #170e33 0%, #0b0718 55%, #06040f 100%);
  isolation: isolate;
}

/* Each ribbon is ONE heavily blurred ellipse, not a striped rectangle. A
   repeating gradient inside a skewed box seems like the obvious way to make
   bands, and it is wrong: blurred, the stripes turn to mush, and unblurred
   they read as a mosaic of hard blocks. A single wide ellipse has no internal
   edges to betray it, so blur only ever softens its silhouette — which is
   exactly what fabric catching light looks like. */
.silk__band {
  position: absolute;
  left: -25%;
  width: 150%;
  height: 30%;
  border-radius: 50%;
  filter: blur(34px);
  /* screen so overlaps ADD — where two ribbons cross is the brightest part of
     the image, and that crossing is the whole effect. */
  mix-blend-mode: screen;
  will-change: transform;
}

.silk__band--a {
  top: 4%;
  background: linear-gradient(90deg, transparent, #ff3d81 28%, #ff9f45 58%, transparent);
  transform: rotate(-11deg);
  animation: silk-a 21s ease-in-out infinite alternate;
}

.silk__band--b {
  top: 28%;
  background: linear-gradient(90deg, transparent, #7c3aed 30%, #5b8cff 62%, transparent);
  transform: rotate(7deg);
  animation: silk-b 27s ease-in-out infinite alternate;
}

.silk__band--c {
  top: 52%;
  background: linear-gradient(90deg, transparent, #22d3ee 26%, #4affd0 60%, transparent);
  transform: rotate(-6deg);
  animation: silk-c 33s ease-in-out infinite alternate;
}

.silk__band--d {
  top: 70%;
  height: 24%;
  background: linear-gradient(90deg, transparent, #b45bff 34%, #ff5ab4 64%, transparent);
  transform: rotate(9deg);
  animation: silk-d 39s ease-in-out infinite alternate;
}

/* Translate AND rotate together. Sliding alone reads as a sheet on rails;
   rotating a degree or two as it travels is what makes it look like it is
   turning in space. Durations share no common factor, so the four never line
   up on a beat. */
@keyframes silk-a {
  from { transform: translate3d(-8%, 0, 0) rotate(-11deg); }
  to   { transform: translate3d(10%, 5%, 0) rotate(-4deg); }
}
@keyframes silk-b {
  from { transform: translate3d(9%, 0, 0) rotate(7deg); }
  to   { transform: translate3d(-10%, -4%, 0) rotate(1deg); }
}
@keyframes silk-c {
  from { transform: translate3d(-6%, 2%, 0) rotate(-6deg); }
  to   { transform: translate3d(11%, -5%, 0) rotate(2deg); }
}
@keyframes silk-d {
  from { transform: translate3d(7%, 0, 0) rotate(9deg); }
  to   { transform: translate3d(-9%, 4%, 0) rotate(3deg); }
}

/* One grade over everything, so four independently drifting ribbons sit in the
   same light instead of reading as four separate objects. */
.silk__grade {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(255, 190, 130, 0.1), transparent 45%, rgba(40, 60, 200, 0.16));
  mix-blend-mode: screen;
  pointer-events: none;
}

.silk__vignette {
  position: absolute;
  inset: 0;
  background: radial-gradient(125% 95% at 50% 50%, transparent 52%, rgba(4, 2, 10, 0.8) 100%);
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .silk__band { animation: none; }
}
`;

export function SilkRibbons() {
  return (
    <>
      <style>{css}</style>
      <div className="silk">
        <div className="silk__band silk__band--a" />
        <div className="silk__band silk__band--b" />
        <div className="silk__band silk__band--c" />
        <div className="silk__band silk__band--d" />
        <div className="silk__grade" />
        <div className="silk__vignette" />
      </div>
    </>
  );
}

More in Backgrounds

Similar tags