AI CSS Animations

Git Branch and Merge

An accent branch grows off the main line of an isometric commit graph, gains two commits and merges back with a ring pulse, on a dot grid. SVG lines grown with CSS scale.

Also known as: git branch animation, git merge diagram, hairline isometric.

HTML

<div class="hln-git-branch-merge" role="img" aria-label="A git branch growing and merging back">
  <svg viewBox="-110 -60 220 120" aria-hidden="true"><g transform="translate(13 -6) scale(1.6)"><circle class="hln-git-branch-merge-grid" cx="-32" cy="-24" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-40" cy="-20" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-48" cy="-16" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-56" cy="-12" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-64" cy="-8" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-24" cy="-20" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-32" cy="-16" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-40" cy="-12" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-48" cy="-8" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-56" cy="-4" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-16" cy="-16" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-24" cy="-12" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-32" cy="-8" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-40" cy="-4" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-48" cy="0" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-8" cy="-12" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-16" cy="-8" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-24" cy="-4" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-32" cy="0" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-40" cy="4" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="0" cy="-8" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-8" cy="-4" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-16" cy="0" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-24" cy="4" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-32" cy="8" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="8" cy="-4" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="0" cy="0" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-8" cy="4" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-16" cy="8" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-24" cy="12" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="16" cy="0" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="8" cy="4" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="0" cy="8" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-8" cy="12" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-16" cy="16" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="24" cy="4" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="16" cy="8" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="8" cy="12" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="0" cy="16" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="-8" cy="20" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="32" cy="8" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="24" cy="12" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="16" cy="16" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="8" cy="20" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="0" cy="24" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="40" cy="12" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="32" cy="16" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="24" cy="20" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="16" cy="24" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="8" cy="28" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="48" cy="16" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="40" cy="20" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="32" cy="24" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="24" cy="28" r="0.6"/><circle class="hln-git-branch-merge-grid" cx="16" cy="32" r="0.6"/><line x1="-44" y1="-22" x2="44" y2="22"/><line class="hln-git-branch-merge-leg hln-git-branch-merge-g0" style="transform-origin: 100% 0%" x1="-16" y1="-8" x2="-30" y2="-1"/><line class="hln-git-branch-merge-leg hln-git-branch-merge-g1" style="transform-origin: 0% 0%" x1="-30" y1="-1" x2="-19" y2="4.5"/><line class="hln-git-branch-merge-leg hln-git-branch-merge-g2" style="transform-origin: 0% 0%" x1="-19" y1="4.5" x2="-9" y2="9.5"/><line class="hln-git-branch-merge-leg hln-git-branch-merge-g3" style="transform-origin: 0% 0%" x1="-9" y1="9.5" x2="2" y2="15"/><line class="hln-git-branch-merge-leg hln-git-branch-merge-g4" style="transform-origin: 0% 100%" x1="2" y1="15" x2="16" y2="8"/><g><g><path d="M-36.81 -19V-16A4.81 2.4 0 0 0 -27.19 -16V-19Z"/><ellipse cx="-32" cy="-19" rx="4.81" ry="2.4"/></g></g><g><g><path d="M-20.81 -11V-8A4.81 2.4 0 0 0 -11.19 -8V-11Z"/><ellipse cx="-16" cy="-11" rx="4.81" ry="2.4"/></g></g><g><g><path d="M-4.81 -3V0A4.81 2.4 0 0 0 4.81 0V-3Z"/><ellipse cx="0" cy="-3" rx="4.81" ry="2.4"/></g></g><g class="hln-git-branch-merge-pop hln-git-branch-merge-p0"><g><path d="M-23.81 1.5V4.5A4.81 2.4 0 0 0 -14.19 4.5V1.5Z"/><ellipse cx="-19" cy="1.5" rx="4.81" ry="2.4"/></g></g><g class="hln-git-branch-merge-pop hln-git-branch-merge-p2"><g><path d="M11.19 5V8A4.81 2.4 0 0 0 20.81 8V5Z"/><ellipse cx="16" cy="5" rx="4.81" ry="2.4"/></g></g><g class="hln-git-branch-merge-pop hln-git-branch-merge-p1"><g><path d="M-13.81 6.5V9.5A4.81 2.4 0 0 0 -4.19 9.5V6.5Z"/><ellipse cx="-9" cy="6.5" rx="4.81" ry="2.4"/></g></g><g><g><path d="M27.19 13V16A4.81 2.4 0 0 0 36.81 16V13Z"/><ellipse cx="32" cy="13" rx="4.81" ry="2.4"/></g></g><ellipse class="hln-git-branch-merge-ring" cx="16" cy="5" rx="4.81" ry="2.4"/></g></svg>
</div>

CSS

.hln-git-branch-merge {
  --c: var(--hln-color, #ff4f2e);
  font-size: var(--hln-size, 16px);
  position: relative;
  display: grid;
  place-items: center;
  width: 22em;
  height: 12em;
  border-radius: 0.75em;
  background: #f6f5f1;
  overflow: hidden;
  --u: 1px;
}
.hln-git-branch-merge svg {
  width: 100%;
  height: 100%;
  overflow: visible;
  fill: #ffffff;
  stroke: #1c1d21;
  stroke-width: 1.25;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.hln-git-branch-merge svg * {
  vector-effect: non-scaling-stroke;
}
.hln-git-branch-merge .hln-git-branch-merge-grid {
  fill: #9a9ca3;
  stroke: none;
}
.hln-git-branch-merge .hln-git-branch-merge-leg {
  stroke: var(--c);
  transform-box: fill-box;
  scale: 0;
}
.hln-git-branch-merge .hln-git-branch-merge-g0 {
  animation: hln-git-branch-merge-g0 6s infinite;
}
.hln-git-branch-merge .hln-git-branch-merge-g1 {
  animation: hln-git-branch-merge-g1 6s infinite;
}
.hln-git-branch-merge .hln-git-branch-merge-g2 {
  animation: hln-git-branch-merge-g2 6s infinite;
}
.hln-git-branch-merge .hln-git-branch-merge-g3 {
  animation: hln-git-branch-merge-g3 6s infinite;
}
.hln-git-branch-merge .hln-git-branch-merge-g4 {
  animation: hln-git-branch-merge-g4 6s infinite;
}
.hln-git-branch-merge .hln-git-branch-merge-pop {
  transform-box: fill-box;
  transform-origin: 50% 70%;
  scale: 0;
}
.hln-git-branch-merge .hln-git-branch-merge-p0 {
  animation: hln-git-branch-merge-p0 6s infinite;
}
.hln-git-branch-merge .hln-git-branch-merge-p1 {
  animation: hln-git-branch-merge-p1 6s infinite;
}
.hln-git-branch-merge .hln-git-branch-merge-p2 {
  animation: hln-git-branch-merge-p2 6s infinite;
}
.hln-git-branch-merge .hln-git-branch-merge-ring {
  fill: none;
  stroke: var(--c);
  opacity: 0;
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: hln-git-branch-merge-ring 6s infinite;
}
@keyframes hln-git-branch-merge-g0 {
  0%, 6% { scale: 0; opacity: 1; animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1); }
  13%, 84% { scale: 1; opacity: 1; }
  92%, 100% { scale: 1; opacity: 0; }
}
@keyframes hln-git-branch-merge-g1 {
  0%, 14% { scale: 0; opacity: 1; animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1); }
  21%, 84% { scale: 1; opacity: 1; }
  92%, 100% { scale: 1; opacity: 0; }
}
@keyframes hln-git-branch-merge-g2 {
  0%, 25% { scale: 0; opacity: 1; animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1); }
  32%, 84% { scale: 1; opacity: 1; }
  92%, 100% { scale: 1; opacity: 0; }
}
@keyframes hln-git-branch-merge-g3 {
  0%, 36% { scale: 0; opacity: 1; animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1); }
  43%, 84% { scale: 1; opacity: 1; }
  92%, 100% { scale: 1; opacity: 0; }
}
@keyframes hln-git-branch-merge-g4 {
  0%, 44% { scale: 0; opacity: 1; animation-timing-function: cubic-bezier(0.65, 0, 0.35, 1); }
  50%, 84% { scale: 1; opacity: 1; }
  92%, 100% { scale: 1; opacity: 0; }
}
@keyframes hln-git-branch-merge-p0 {
  0%, 20% { scale: 0; animation-timing-function: cubic-bezier(0.3, 1.45, 0.45, 1); }
  26%, 84% { scale: 1; animation-timing-function: ease-in; }
  90%, 100% { scale: 0; }
}
@keyframes hln-git-branch-merge-p1 {
  0%, 31% { scale: 0; animation-timing-function: cubic-bezier(0.3, 1.45, 0.45, 1); }
  37%, 84% { scale: 1; animation-timing-function: ease-in; }
  90%, 100% { scale: 0; }
}
@keyframes hln-git-branch-merge-p2 {
  0%, 49% { scale: 0; animation-timing-function: cubic-bezier(0.3, 1.45, 0.45, 1); }
  55%, 84% { scale: 1; animation-timing-function: ease-in; }
  90%, 100% { scale: 0; }
}
@keyframes hln-git-branch-merge-ring {
  0%, 51% { opacity: 0; scale: 1; }
  52% { opacity: 1; scale: 1; animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1); }
  70%, 100% { opacity: 0; scale: 2.4; }
}
@media (prefers-reduced-motion: reduce) {
  .hln-git-branch-merge * {
    animation: none !important;
  }
  .hln-git-branch-merge .hln-git-branch-merge-leg, .hln-git-branch-merge .hln-git-branch-merge-pop { scale: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .hln-git-branch-merge, .hln-git-branch-merge *, .hln-git-branch-merge::before, .hln-git-branch-merge::after, .hln-git-branch-merge *::before, .hln-git-branch-merge *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

Customizing this snippet

  • Color: set --hln-color on the element or a parent. Currently previewing #3b82f6.
  • Base size: set --hln-size. Every other dimension is in em, so the whole thing scales together. Currently previewing 16px.
  • Speed: change the duration on the transition or animation shorthand. Staggered delays are relative, so they keep their rhythm.
  • Reduced motion: the rule at the end of the CSS turns the motion off for people who asked their OS for less of it. Keep it.

Similar hairline animations