Kaleidoscope
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>
</>
);
}Markup where the component goes, CSS in your stylesheet. No JavaScript.
HTML
<div class="loader-kaleidoscope" role="status" aria-label="Loading">
<span class="loader-kaleidoscope__wedge" aria-hidden="true" style="transform:rotate(0deg)">
<span class="loader-kaleidoscope__shard loader-kaleidoscope__shard--a"></span>
<span class="loader-kaleidoscope__shard loader-kaleidoscope__shard--b"></span>
<span class="loader-kaleidoscope__shard loader-kaleidoscope__shard--c"></span>
</span>
<span class="loader-kaleidoscope__wedge" aria-hidden="true" style="transform:rotate(60deg) scaleY(-1)">
<span class="loader-kaleidoscope__shard loader-kaleidoscope__shard--a"></span>
<span class="loader-kaleidoscope__shard loader-kaleidoscope__shard--b"></span>
<span class="loader-kaleidoscope__shard loader-kaleidoscope__shard--c"></span>
</span>
<span class="loader-kaleidoscope__wedge" aria-hidden="true" style="transform:rotate(120deg)">
<span class="loader-kaleidoscope__shard loader-kaleidoscope__shard--a"></span>
<span class="loader-kaleidoscope__shard loader-kaleidoscope__shard--b"></span>
<span class="loader-kaleidoscope__shard loader-kaleidoscope__shard--c"></span>
</span>
<span class="loader-kaleidoscope__wedge" aria-hidden="true" style="transform:rotate(180deg) scaleY(-1)">
<span class="loader-kaleidoscope__shard loader-kaleidoscope__shard--a"></span>
<span class="loader-kaleidoscope__shard loader-kaleidoscope__shard--b"></span>
<span class="loader-kaleidoscope__shard loader-kaleidoscope__shard--c"></span>
</span>
<span class="loader-kaleidoscope__wedge" aria-hidden="true" style="transform:rotate(240deg)">
<span class="loader-kaleidoscope__shard loader-kaleidoscope__shard--a"></span>
<span class="loader-kaleidoscope__shard loader-kaleidoscope__shard--b"></span>
<span class="loader-kaleidoscope__shard loader-kaleidoscope__shard--c"></span>
</span>
<span class="loader-kaleidoscope__wedge" aria-hidden="true" style="transform:rotate(300deg) scaleY(-1)">
<span class="loader-kaleidoscope__shard loader-kaleidoscope__shard--a"></span>
<span class="loader-kaleidoscope__shard loader-kaleidoscope__shard--b"></span>
<span class="loader-kaleidoscope__shard loader-kaleidoscope__shard--c"></span>
</span>
</div>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; }
}Goal: Create a "Kaleidoscope" loader.
Visual behavior: 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.
Technique:
- Six 60deg wedges (clip-path triangles from the centre to the rim) are rotated round the centre; odd wedges add scaleY(-1), mirroring each against its neighbours.
- Each wedge holds the same three shards (a triangle, a disc, an outlined square), each drifting, turning or scaling on its own keyframe and period.
- Because every wedge runs identical animations, the six copies stay perfectly symmetric; the circular container turns once every 14s.
Accessibility: The root has role="status" and aria-label="Loading"; everything inside is decorative. Under prefers-reduced-motion everything slows to a 40s period.
My stack: {{USER_STACK}}
My styling: {{USER_STYLING}}
My constraints: {{USER_CONSTRAINTS}}
My colors: neutral only. Black, white and grays, no hues. Use the color values above as written, or my project's closest neutral tokens.
Return a single self-contained component. No dependencies.Goal: Create a "Kaleidoscope" loader.
Visual behavior: 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.
Technique:
- Six 60deg wedges (clip-path triangles from the centre to the rim) are rotated round the centre; odd wedges add scaleY(-1), mirroring each against its neighbours.
- Each wedge holds the same three shards (a triangle, a disc, an outlined square), each drifting, turning or scaling on its own keyframe and period.
- Because every wedge runs identical animations, the six copies stay perfectly symmetric; the circular container turns once every 14s.
Accessibility: The root has role="status" and aria-label="Loading"; everything inside is decorative. Under prefers-reduced-motion everything slows to a 40s period.
My stack: {{USER_STACK}}
My styling: {{USER_STYLING}}
My constraints: {{USER_CONSTRAINTS}}
My colors: use my project's existing color tokens. The color values above are grayscale placeholders. Map each one by its role (surface, text, border, accent, glow) to my theme, and keep their relative lightness so contrast and the effect still read.
Return a single self-contained component. No dependencies.Paste into Claude, ChatGPT or Cursor after filling in {{USER_STACK}}, {{USER_STYLING}} and {{USER_CONSTRAINTS}}. Neutral keeps the greys you see here; Match my project asks the model to map them onto your own colour tokens.
Free to use in any project, commercial included. MIT-0, no attribution required. Licence
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.