All Taste components
Loaders
Spinners
Four small loading indicators — ring, dots, bars and pulse — in your brand colour.
ring · dots · bars · pulse
Ring
Dots
Bars
Pulse
/* globals.css
@keyframes dot { 0%, 80%, 100% { transform: scale(.6); opacity: .4; } 40% { transform: scale(1); opacity: 1; } }
@keyframes bar { 0%, 100% { transform: scaleY(.4); } 50% { transform: scaleY(1); } }
*/
import { cn } from "@/lib/utils";
export function RingSpinner({ className }: { className?: string }) {
return (
<span role="status" aria-label="Loading" className={cn("inline-block size-6 animate-spin rounded-full border-[2.5px] border-primary-soft border-t-primary motion-reduce:animate-none", className)} />
);
}
export function DotsSpinner() {
return (
<span role="status" aria-label="Loading" className="inline-flex gap-1.5">
{[0, 1, 2].map((i) => (
<i key={i} className="size-[7px] rounded-full bg-primary animate-[dot_1.2s_cubic-bezier(.2,.7,.1,1)_infinite] motion-reduce:animate-none" style={{ animationDelay: i * 160 + "ms" }} />
))}
</span>
);
}
export function BarsSpinner() {
return (
<span role="status" aria-label="Loading" className="inline-flex h-[22px] items-center gap-[3px]">
{[0, 1, 2, 3, 4].map((i) => (
<i key={i} className="h-full w-1 rounded-sm bg-primary animate-[bar_1s_ease-in-out_infinite] motion-reduce:animate-none" style={{ animationDelay: i * 100 + "ms" }} />
))}
</span>
);
}
export function PulseDot({ label = "Live" }: { label?: string }) {
return (
<span role="status" aria-label={label} className="relative inline-flex size-3">
<span aria-hidden className="absolute inset-0 animate-ping rounded-full bg-primary opacity-50 motion-reduce:animate-none" />
<span className="relative size-3 rounded-full bg-primary" />
</span>
);
}
The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.