Loaders

Car Loader

A sharp, flat 2D RC car loading spinner. The car drives back and forth across a dashed road with spinning wheels, fading out smoothly at the edges instead of a hard clip.

Loading preview
Advertisement
Ad space reserved for a future placement

Source code

"use client";

type CarLoaderSize = "sm" | "md" | "lg";

interface CarLoaderProps {
  accentColor?: string;
  size?: CarLoaderSize;
  label?: string;
  className?: string;
}

const SIZE_DIMENSIONS: Record<CarLoaderSize, { width: number; height: number }> = {
  sm: { width: 100, height: 50 },
  md: { width: 140, height: 70 },
  lg: { width: 180, height: 90 },
};

export function CarLoader({
  accentColor = "#3f3cf2",
  size = "md",
  label = "Loading",
  className,
}: CarLoaderProps) {
  const { width, height } = SIZE_DIMENSIONS[size];

  return (
    <div
      role="status"
      aria-label={label}
      className={["car-loader", className].filter(Boolean).join(" ")}
      style={{ width, height, ["--car-accent" as string]: accentColor }}
    >
      <svg viewBox="0 0 200 100" fill="none">
        <line
          x1="0"
          y1="92"
          x2="200"
          y2="92"
          stroke="currentColor"
          strokeOpacity="0.25"
          strokeWidth="3"
          strokeDasharray="10 8"
          className="car-loader-road"
        />
        <g className="car-loader-drive">
          <path d="M15 78 L15 60 L38 60 L52 40 L128 40 L145 60 L170 60 L170 78 Z" fill="var(--car-accent)" />
          <path d="M60 56 L70 44 L120 44 L134 56 Z" fill="white" fillOpacity="0.85" />
          <rect x="14" y="70" width="10" height="6" rx="1" fill="var(--car-accent)" />
          <g className="car-loader-wheel" style={{ transformOrigin: "48px 80px" }}>
            <circle cx="48" cy="80" r="13" fill="#1f2937" />
            <circle cx="48" cy="80" r="5" fill="var(--car-accent)" />
          </g>
          <g className="car-loader-wheel" style={{ transformOrigin: "150px 80px" }}>
            <circle cx="150" cy="80" r="13" fill="#1f2937" />
            <circle cx="150" cy="80" r="5" fill="var(--car-accent)" />
          </g>
        </g>
      </svg>
      <style jsx>{`
        .car-loader {
          display: inline-block;
          color: var(--car-accent);
        }
        .car-loader svg {
          width: 100%;
          height: 100%;
          overflow: hidden;
          -webkit-mask-image: linear-gradient(to right, transparent 0%, black 14%, black 86%, transparent 100%);
          mask-image: linear-gradient(to right, transparent 0%, black 14%, black 86%, transparent 100%);
        }
        .car-loader-drive {
          animation: car-loader-drive 2.4s linear infinite;
        }
        .car-loader-wheel {
          animation: car-loader-spin 0.8s linear infinite;
        }
        .car-loader-road {
          animation: car-loader-road 0.9s linear infinite;
        }
        @keyframes car-loader-drive {
          0% {
            transform: translateX(-45px);
          }
          100% {
            transform: translateX(220px);
          }
        }
        @keyframes car-loader-spin {
          to {
            transform: rotate(360deg);
          }
        }
        @keyframes car-loader-road {
          to {
            stroke-dashoffset: -18px;
          }
        }
        @media (prefers-reduced-motion: reduce) {
          .car-loader-drive,
          .car-loader-wheel,
          .car-loader-road {
            animation-duration: 3s;
          }
        }
      `}</style>
    </div>
  );
}

Usage

Import the component and use it like this.

import { CarLoader } from "./CarLoader";

export default function Example() {
  return <CarLoader accentColor="#3f3cf2" size="md" label="Loading" />;
}

Dependencies

No external dependencies.

Usage notes

Pass an accentColor hex, size (sm/md/lg), and an optional label for the accessible status announcement.