Loaders
SolarLoader
A small sun with turning rays and a beating core, with one planet circling it.
Download
- React
- Tailwind CSS v4
- motion
components/ui/solar-loader.tsx
"use client";
import { motion, useReducedMotion } from "motion/react";
type SolarLoaderProps = {
className?: string;
color?: string;
/** Colour of the orbiting planet. */
accent?: string;
/** Loader width in pixels. */
size?: number;
/** 1 is the default pace; 2 is twice as fast. */
speed?: number;
/** Read out by screen readers. */
label?: string;
};
/* SolarLoader: a small sun with turning rays, and one planet circling it. */
export function SolarLoader({
className = "",
color = "#fbbf24",
accent = "#a78bfa",
size = 72,
speed = 1,
label = "Loading",
}: SolarLoaderProps) {
const reduce = useReducedMotion();
const orbit = 2.4 / speed;
const planet = size * 0.12;
return (
<div role="status" className={`relative inline-block ${className}`} style={{ width: size, height: size }}>
<span className="sr-only">{label}</span>
{/* Orbit track */}
<span
aria-hidden
className="absolute inset-[6%] rounded-full border"
style={{ borderColor: `color-mix(in srgb, ${accent} 30%, transparent)` }}
/>
{/* Rays: a striped conic ring, masked to a band round the sun */}
<motion.span
aria-hidden
className="absolute inset-0 m-auto rounded-full"
style={{
width: size * 0.66,
height: size * 0.66,
background: `repeating-conic-gradient(${color} 0deg 7deg, transparent 7deg 30deg)`,
WebkitMask: "radial-gradient(circle, transparent 52%, #000 54%, transparent 72%)",
mask: "radial-gradient(circle, transparent 52%, #000 54%, transparent 72%)",
}}
animate={reduce ? { opacity: [0.5, 0.9, 0.5] } : { rotate: 360 }}
transition={reduce ? { duration: 3, repeat: Infinity } : { duration: orbit * 3, repeat: Infinity, ease: "linear" }}
/>
{/* Sun core */}
<motion.span
aria-hidden
className="absolute inset-0 m-auto rounded-full"
style={{
width: size * 0.34,
height: size * 0.34,
background: `radial-gradient(circle at 35% 35%, #fff, ${color} 55%)`,
boxShadow: `0 0 ${size / 4}px ${color}`,
}}
animate={reduce ? undefined : { scale: [1, 1.08, 1] }}
transition={{ duration: orbit / 2, repeat: Infinity, ease: "easeInOut" }}
/>
{/* Planet: sits at the top of a turning wrapper */}
{!reduce && (
<motion.span
aria-hidden
className="absolute inset-[6%]"
animate={{ rotate: 360 }}
transition={{ duration: orbit, repeat: Infinity, ease: "linear" }}
>
<span
className="absolute left-1/2 rounded-full"
style={{
width: planet,
height: planet,
top: -planet / 2,
marginLeft: -planet / 2,
background: accent,
boxShadow: `0 0 ${planet}px ${accent}`,
}}
/>
</motion.span>
)}
</div>
);
}
1. Install the dependency.
npm i motion2. Paste the Code tab into
components/ui/solar-loader.tsx. The styles are Tailwind CSS v4 classes.3. Use it. This is the preview above:
app/solar-loader-demo.tsx
import { SolarLoader } from "@/components/ui/solar-loader"; export default function SolarLoaderExample() { return ( <div className="grid h-full place-items-center bg-[radial-gradient(circle_at_50%_50%,#1d1a33,#0d0c14_70%)]"> <SolarLoader size={96} /> </div> ); }
Props
| Name | Type | Default | What it does |
|---|---|---|---|
| color | string | "#fbbf24" | Sun colour. |
| accent | string | "#a78bfa" | Planet and orbit colour. |
| size | number | 72 | Width in pixels. |
| speed | number | 1 | 1 is the default pace; 2 is twice as fast. |
| label | string | "Loading" | Read out by screen readers (role=status). |
| className | string | "" | Extra classes on the wrapper. |