Ir al contenido
Evaluar mi proyecto
Componentes · Texto y animacionesDisponible

Cargadores

Seis indicadores de carga solo con CSS, en el color del texto.

Cargando (Anillo)Anillo
Cargando (Puntos)Puntos
Cargando (Barras)Barras
Cargando (Órbita)Órbita
Cargando (Pulso)Pulso
Cargando (Cuadros)Cuadros

Código completo

Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.

loaders.tsx71 líneas
Descargar
/**
 * Seis cargadores solo con CSS, en el color del texto (currentColor): anillo, puntos, barras, órbita, pulso y cuadros.
 * Anuncian "Cargando" a lectores de pantalla y se detienen con movimiento reducido.
 *
 *   <Loader variant="orbit" size={48} />
 *   <Loader variant="dots" label="Guardando…" />
 */

import type { CSSProperties } from "react";

export type LoaderVariant = "ring" | "dots" | "bars" | "orbit" | "pulse" | "squares";

export type LoaderProps = {
  variant?: LoaderVariant;
  /** Tamaño en píxeles. */
  size?: number;
  /** Texto para lectores de pantalla. */
  label?: string;
  className?: string;
  style?: CSSProperties;
};

const CSS = `
.gcld-root { position: relative; display: inline-block; width: var(--gcld-size, 40px); height: var(--gcld-size, 40px); color: inherit; }
.gcld-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.gcld-ring { position: absolute; inset: 0; box-sizing: border-box; border: calc(var(--gcld-size) * .1) solid currentColor; border-color: currentColor transparent currentColor transparent; border-radius: 50%; animation: gcld-spin 1s cubic-bezier(.5, .1, .5, .9) infinite; }
.gcld-dots { display: flex; align-items: center; justify-content: space-between; height: 100%; }
.gcld-dots i { width: 22%; height: 22%; border-radius: 50%; background: currentColor; animation: gcld-bounce 1.1s ease-in-out infinite; }
.gcld-dots i:nth-child(2) { animation-delay: .15s; } .gcld-dots i:nth-child(3) { animation-delay: .3s; }
.gcld-bars { display: flex; align-items: center; justify-content: space-between; height: 100%; }
.gcld-bars i { width: 14%; height: 100%; border-radius: 99px; background: currentColor; transform-origin: center; animation: gcld-stretch 1s ease-in-out infinite; }
.gcld-bars i:nth-child(2) { animation-delay: .1s; } .gcld-bars i:nth-child(3) { animation-delay: .2s; } .gcld-bars i:nth-child(4) { animation-delay: .3s; } .gcld-bars i:nth-child(5) { animation-delay: .4s; }
.gcld-orbit { position: absolute; inset: 0; animation: gcld-spin 1.6s linear infinite; }
.gcld-orbit::before, .gcld-orbit::after { content: ""; position: absolute; width: 26%; height: 26%; border-radius: 50%; background: currentColor; }
.gcld-orbit::before { top: 0; left: 37%; } .gcld-orbit::after { bottom: 0; left: 37%; opacity: .4; }
.gcld-orbit-core { position: absolute; inset: 36%; border-radius: 50%; background: currentColor; opacity: .25; }
.gcld-pulse { position: absolute; inset: 0; }
.gcld-pulse i { position: absolute; inset: 0; border: calc(var(--gcld-size) * .06) solid currentColor; border-radius: 50%; opacity: 0; animation: gcld-ping 1.8s cubic-bezier(.2, .6, .3, 1) infinite; }
.gcld-pulse i:nth-child(2) { animation-delay: .6s; } .gcld-pulse i:nth-child(3) { animation-delay: 1.2s; }
.gcld-squares { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8%; height: 100%; }
.gcld-squares i { border-radius: 18%; background: currentColor; animation: gcld-fade 1.2s ease-in-out infinite; }
.gcld-squares i:nth-child(2), .gcld-squares i:nth-child(4) { animation-delay: .15s; } .gcld-squares i:nth-child(3), .gcld-squares i:nth-child(5), .gcld-squares i:nth-child(7) { animation-delay: .3s; } .gcld-squares i:nth-child(6), .gcld-squares i:nth-child(8) { animation-delay: .45s; } .gcld-squares i:nth-child(9) { animation-delay: .6s; }
@keyframes gcld-spin { to { transform: rotate(360deg); } }
@keyframes gcld-bounce { 0%, 80%, 100% { transform: translateY(0) scale(.7); opacity: .5; } 40% { transform: translateY(-70%) scale(1); opacity: 1; } }
@keyframes gcld-stretch { 0%, 100% { transform: scaleY(.25); opacity: .55; } 50% { transform: scaleY(1); opacity: 1; } }
@keyframes gcld-ping { 0% { transform: scale(.2); opacity: .9; } 100% { transform: scale(1); opacity: 0; } }
@keyframes gcld-fade { 0%, 100% { opacity: .15; transform: scale(.85); } 50% { opacity: 1; transform: scale(1); } }
@media (prefers-reduced-motion: reduce) { .gcld-root *, .gcld-root *::before, .gcld-root *::after { animation-duration: 4s !important; } }
`;

const inner: Record<LoaderVariant, React.ReactNode> = {
  ring: <span className="gcld-ring" />,
  dots: <span className="gcld-dots"><i /><i /><i /></span>,
  bars: <span className="gcld-bars"><i /><i /><i /><i /><i /></span>,
  orbit: <><span className="gcld-orbit-core" /><span className="gcld-orbit" /></>,
  pulse: <span className="gcld-pulse"><i /><i /><i /></span>,
  squares: <span className="gcld-squares">{Array.from({ length: 9 }, (_, index) => <i key={index} />)}</span>,
};

export function Loader({ variant = "ring", size = 40, label = "Cargando", className, style }: LoaderProps) {
  return (
    <>
      <style>{CSS}</style>
      <span role="status" className={["gcld-root", className].filter(Boolean).join(" ")} style={{ ["--gcld-size" as string]: `${size}px`, ...style } as CSSProperties}>
        <span className="gcld-sr">{label}</span>
        <span aria-hidden="true" style={{ display: "contents" }}>{inner[variant]}</span>
      </span>
    </>
  );
}

Úsalo en tu proyecto

Instálalo con la CLI de shadcn.

terminal.sh1 líneas
npx shadcn@latest add https://www.gassierconsulting.com/r/cargadores

Ejemplo de uso de Cargadores

Después de copiar loaders.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.

ejemplo.tsx5 líneas
import { Loader } from "./loaders";

export default function Ejemplo() {
  return <Loader variant="ring" label="Ejemplo" />;
}

Propiedades de Loader

Loader acepta 5 propiedades: todas son opcionales y tienen un valor por defecto, así que funciona sin configurar nada.

PropiedadTipoDescripción
variant?LoaderVariantOpcional.
size?numberTamaño en píxeles.
label?stringTexto para lectores de pantalla.
className?stringOpcional.
style?CSSPropertiesOpcional.

Variantes disponibles

  • variant: ring, dots, bars, orbit, pulse, squares

Personaliza Cargadores con variables CSS

Define estas variables en el contenedor o en tu hoja de estilos para cambiar los colores sin tocar el componente. Si no las defines, se usa el valor por defecto.

VariableValor por defecto
--gcld-size40px

Accesibilidad y rendimiento

El archivo tiene 71 líneas (5 KB) y define 5 animaciones con @keyframes. Esto es lo que hace Cargadores por quien lo usa:

  • Detecta prefers-reduced-motion y baja o quita el movimiento cuando la persona lo pide.
  • Declara roles ARIA para lectores de pantalla: status.
Licencia
MIT
Dependencias
React >=18
Compatibilidad
React · Next.js · Sin Tailwind

Acerca del producto

Seis cargadores en un solo componente: anillo, puntos, barras, órbita, pulso y cuadros. Usan currentColor, así que toman el color de su contenedor, y anuncian "Cargando" a lectores de pantalla.

Incluye

Un solo archivo React + TypeScript, con los estilos dentro. No usa Tailwind ni bibliotecas externas.

  • Seis variantes con la propiedad variant
  • Tamaño en píxeles
  • Texto accesible configurable (label)
  • Sin JavaScript

Cómo usarlo

<Loader variant="orbit" size={48} />. Cambia el color con color en el contenedor.