Back to the library

Partition panels settle

Variant: Paired offset

The four panels open as two mirrored pairs: the inner two swing first, the outer two follow 110ms later, so the partition parts from the middle outward. This is the option to use when the centre of the region carries the primary content and the flanks are secondary. The offset is one long stagger step rather than four short ones, which keeps the reveal symmetric while still arriving in a clear order.

entrancerevealpaneloffsetsymmetry

The source

A complete HTML document. Paste it into an empty .html file and it plays — no stylesheet, script, font, or image to fetch.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Partition panels settle — paired offset</title>
<style>
:root {
  /* Neutrals — fixed, never remixed. */
  --ivory: #faf9f5;
  --paper: #ffffff;
  --oat: #e3dacc;
  --slate: #141413;

  /* Knobs — the remix API. A knob exists iff declared here and used below. */
  --hue: 38.8;
  --lift: 0;
  --chroma: 1;
  --round: 1;
  --speed: 1;

  /* Accents — derived from the knobs with the family's offsets baked in,
     so one hue roll moves them together. Defaults reproduce the original hex
     (#d97757 / #b85c3e / #788c5d / #b04a3f). */
  --clay: oklch(calc(0.6724 + var(--lift)) calc(0.1308 * var(--chroma)) var(--hue));
  --clay-d: oklch(calc(0.5797 + var(--lift)) calc(0.127 * var(--chroma)) var(--hue));
  --olive: oklch(calc(0.6118 + var(--lift)) calc(0.0713 * var(--chroma)) calc(var(--hue) + 88.3));
  --rust: oklch(calc(0.5408 + var(--lift)) calc(0.1357 * var(--chroma)) calc(var(--hue) - 10.3));

  /* Clock — every duration in the document runs off --t. */
  --cycle: 3.4s; /* the authored master loop */
  --t: calc(var(--cycle) / var(--speed));

  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);

  --_step: calc(0.11s / var(--speed)); /* inner pair leads the outer pair */
}

* { box-sizing: border-box; }

html, body { margin: 0; }

body {
  min-height: 100vh;
  display: grid;
  place-items: center;
  background: var(--ivory);
  font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  color: var(--slate);
}

.stage {
  width: min(420px, 92vw);
  padding: clamp(14px, 4vw, 22px);
  background: var(--paper);
  border: 1px solid var(--oat);
  border-radius: calc(14px * var(--round));
}

.caption {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: clamp(11px, 3vw, 12.5px);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: color-mix(in oklab, var(--slate) 55%, var(--ivory));
  margin-bottom: clamp(10px, 3vw, 14px);
}

.caption .dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--clay);
  animation: pip var(--t) linear infinite;
}

.screen {
  position: relative;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(5px, 1.6vw, 8px);
  height: clamp(108px, 30vw, 140px);
  perspective: 620px;
}

.bay {
  position: relative;
  overflow: hidden;
  border-radius: calc(9px * var(--round));
  background: color-mix(in oklab, var(--ivory) 78%, var(--oat));
}

.bay::after {
  content: '';
  position: absolute;
  left: 14%;
  right: 14%;
  bottom: 16%;
  height: 34%;
  border-radius: calc(5px * var(--round));
  background: color-mix(in oklab, var(--olive) 42%, var(--paper));
}

.bay::before {
  content: '';
  position: absolute;
  left: 14%;
  top: 18%;
  width: 42%;
  height: 12%;
  border-radius: 999px;
  background: color-mix(in oklab, var(--slate) 22%, var(--ivory));
}

.panel {
  position: absolute;
  inset: 0;
  border-radius: calc(9px * var(--round));
  background:
    repeating-linear-gradient(
      90deg,
      color-mix(in oklab, var(--clay) 88%, var(--paper)) 0 3px,
      color-mix(in oklab, var(--clay-d) 82%, var(--paper)) 3px 6px
    );
  transform-origin: left center;
  will-change: transform, opacity;
  animation: fold var(--t) var(--ease-out) infinite;
  animation-delay: calc(var(--_i) * var(--_step) - var(--t));
}

/* Inner pair leads (--_i: 0), outer pair trails by one step. */
.bay:nth-child(1) .panel,
.bay:nth-child(4) .panel { --_i: 1; }
.bay:nth-child(2) .panel,
.bay:nth-child(3) .panel { --_i: 0; }

.bay:nth-child(even) .panel { transform-origin: right center; }

@keyframes fold {
  0%           { transform: rotateY(0deg) scale(1); opacity: 1; }
  8.125%, 76%  { transform: rotateY(-72deg) scale(0.97); opacity: 0; }
  84%          { transform: rotateY(0deg) scale(1); opacity: 1; }
  100%         { transform: rotateY(0deg) scale(1); opacity: 1; }
}

@keyframes pip {
  0%, 7%    { opacity: 1; }
  41%       { opacity: 0.3; }
  78%, 100% { opacity: 1; }
}

@media (max-width: 380px) {
  .stage { padding: 12px; }
  .caption { margin-bottom: 9px; }
}

@media (prefers-reduced-motion: reduce) {
  .panel {
    animation: fade calc(var(--cycle) * 2 / var(--speed)) var(--ease-out) infinite;
    animation-delay: calc(var(--_i) * var(--_step) * 2);
    transform: none;
  }
  .caption .dot {
    animation: pip calc(var(--cycle) * 2 / var(--speed)) linear infinite;
  }
  @keyframes fade {
    0%        { opacity: 1; }
    11%, 76%  { opacity: 0.12; }
    86%, 100% { opacity: 1; }
  }
}
</style>
</head>
<body>
  <div class="stage">
    <div class="caption"><span class="dot"></span>Workspace ready</div>
    <div class="screen">
      <div class="bay"><div class="panel"></div></div>
      <div class="bay"><div class="panel"></div></div>
      <div class="bay"><div class="panel"></div></div>
      <div class="bay"><div class="panel"></div></div>
    </div>
  </div>
</body>
</html>

The variant family

Same idea, one axis moved. Compare them side by side before you commit.