Tunnel

No dependencies
Code format
React + Tailwind
const css = `
  @keyframes loader-tunnel-rush {
    0% { transform: scale(0.04) rotate(0deg); opacity: 0; }
    20% { opacity: 1; }
    100% { transform: scale(1.5) rotate(90deg); opacity: 0; }
  }
  .loader-tunnel {
    position: relative;
    width: 104px;
    height: 104px;
    border-radius: 18px;
    overflow: hidden;
    background: radial-gradient(circle, light-dark(#fbfcfd, #121417) 0 20%, light-dark(#f3f4f7, #1a1c1f));
    box-shadow: inset 0 0 0 1px light-dark(#dbdde1, #2e3035);
  }
  .loader-tunnel__frame {
    position: absolute;
    inset: 10px;
    border: 1.5px solid light-dark(#42454c, #e5e6e9);
    border-radius: 14px;
    opacity: 0;
    /* ease-in: frames creep out of the distance and rush past at the end, as perspective does. */
    animation: loader-tunnel-rush 2.4s cubic-bezier(0.7, 0, 0.9, 0.6) var(--d) infinite;
  }
  .loader-tunnel__light {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 6px;
    height: 6px;
    margin: -3px;
    border-radius: 50%;
    background: light-dark(#373a40, #f1f2f3);
    box-shadow: 0 0 12px 4px color-mix(in oklab, light-dark(#373a40, #f1f2f3) 45%, transparent);
  }
  @media (prefers-reduced-motion: reduce) { .loader-tunnel__frame { animation-duration: 9s; } }
`;

const FRAMES = 8;

export function TunnelLoader() {
  return (
    <>
      <style>
        {css}
      </style>
      <div className="loader-tunnel" role="status" aria-label="Loading">
        <span className="loader-tunnel__light" aria-hidden="true" />
        {Array.from({ length: FRAMES }, (_, i) => (
          <span
            key={i}
            className="loader-tunnel__frame"
            aria-hidden="true"
            style={{ '--d': `${(-(i / FRAMES) * 2.4).toFixed(2)}s` } as React.CSSProperties}
          />
        ))}
      </div>
    </>
  );
}

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

Install with shadcn

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

About this component

Rounded frames rush out of a point of light at the centre, twisting as they come, like flying down a wormhole.

Dependencies
None
Difficulty
Beginner
Triggers
continuous, loop, 3d
Techniques
keyframes, css variables, radial gradient
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 the frames travel much more slowly.

More loaders

See all
Vortex
Atom
Battery
Clock