Kaleidoscope

No dependencies
Code format
React + Tailwind
const css = `
  @keyframes loader-kaleidoscope-turn { to { transform: rotate(360deg); } }
  @keyframes loader-kaleidoscope-a {
    0%, 100% { transform: translate(10px, -6px) rotate(0deg); }
    50% { transform: translate(30px, 8px) rotate(120deg); }
  }
  @keyframes loader-kaleidoscope-b {
    0%, 100% { transform: translate(34px, -2px) scale(0.6); }
    50% { transform: translate(16px, 10px) scale(1.2); }
  }
  @keyframes loader-kaleidoscope-c {
    0%, 100% { transform: translate(22px, 12px) rotate(45deg); }
    50% { transform: translate(40px, -8px) rotate(225deg); }
  }
  .loader-kaleidoscope {
    position: relative;
    width: 104px;
    height: 104px;
    border-radius: 50%;
    overflow: hidden;
    box-shadow: inset 0 0 0 1px light-dark(#dbdde1, #2e3035);
    animation: loader-kaleidoscope-turn 14s linear infinite;
  }
  /* A 60deg wedge from the centre to the right edge; six of them, alternate ones mirrored. */
  .loader-kaleidoscope__wedge {
    position: absolute;
    left: 50%;
    top: 0;
    width: 52px;
    height: 104px;
    transform-origin: 0 50%;
    clip-path: polygon(0 50%, 100% 21%, 100% 79%);
  }
  .loader-kaleidoscope__shard {
    position: absolute;
    left: 0;
    top: 50%;
    margin-top: -6px;
  }
  .loader-kaleidoscope__shard--a {
    width: 14px;
    height: 12px;
    background: light-dark(#42454c, #e5e6e9);
    clip-path: polygon(50% 0, 100% 100%, 0 100%);
    animation: loader-kaleidoscope-a 3.4s ease-in-out infinite;
  }
  .loader-kaleidoscope__shard--b {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: light-dark(#5f626a, #a7abb2);
    animation: loader-kaleidoscope-b 2.6s ease-in-out infinite;
  }
  .loader-kaleidoscope__shard--c {
    width: 9px;
    height: 9px;
    border: 1.5px solid light-dark(#373a40, #f1f2f3);
    animation: loader-kaleidoscope-c 4.2s ease-in-out infinite;
  }
  @media (prefers-reduced-motion: reduce) {
    .loader-kaleidoscope, .loader-kaleidoscope__shard { animation-duration: 40s; }
  }
`;

export function KaleidoscopeLoader() {
  return (
    <>
      <style>
        {css}
      </style>
      <div className="loader-kaleidoscope" role="status" aria-label="Loading">
        {Array.from({ length: 6 }, (_, i) => (
          <span
            key={i}
            className="loader-kaleidoscope__wedge"
            aria-hidden="true"
            // A mirror between neighbours: odd wedges are flipped across their own axis.
            style={{ transform: `rotate(${i * 60}deg)${i % 2 ? ' scaleY(-1)' : ''}` }}
          >
            <span className="loader-kaleidoscope__shard loader-kaleidoscope__shard--a" />
            <span className="loader-kaleidoscope__shard loader-kaleidoscope__shard--b" />
            <span className="loader-kaleidoscope__shard loader-kaleidoscope__shard--c" />
          </span>
        ))}
      </div>
    </>
  );
}

Free to use in any project, commercial included. MIT-0, no attribution required. Licence

Install with shadcn

npx shadcn@latest add @movin/loader-kaleidoscope
Source on GitHub

About this component

A turning kaleidoscope: three drifting shapes in one wedge are mirrored into six, so they meet and part in symmetric patterns while the whole disc slowly rotates.

Dependencies
None
Difficulty
Intermediate
Triggers
continuous, loop, morph
Techniques
clip path, keyframes, transform only
Reduced motion
Respected
Theme
Light and dark (follows your page's color-scheme)

The root has role="status" and aria-label="Loading"; everything inside is decorative. Under prefers-reduced-motion everything slows to a 40s period.

More loaders

See all
Line Clear
Maze
Metronome
Mobius