For the complete documentation index, see llms.txt. Markdown variants are available by appending .md to documentation URLs. Homepage markdown is available at /llms.md/content.md. An agent skill is available at /.well-known/agent-skills/site-skill.md.

Skeleton

The Skeleton component from this design system, built on Base UI.

Installation

$ pnpm dlx shadcn@latest add @cobalt/skeleton

Source

import { cn } from "cn";

type SkeletonShape = "rect" | "text" | "circle";

type SkeletonProps = React.ComponentProps<"div"> & {
  shape?: SkeletonShape;
  isLoaded?: boolean;
};

function Skeleton({
  className,
  shape = "rect",
  isLoaded = false,
  children,
  ...props
}: SkeletonProps) {
  const block = cn(
    "block overflow-hidden isolate bg-[color:color-mix(in_srgb,var(--foreground)_8%,transparent)] after:pointer-events-none after:absolute after:inset-0 after:translate-x-[-100%] after:content-[''] after:[background:linear-gradient(90deg,transparent,var(--skeleton-line,var(--background)))_right/2px_100%_no-repeat,linear-gradient(90deg,transparent,var(--skeleton-glow,var(--background)))_right/72px_100%_no-repeat,linear-gradient(90deg,transparent,var(--skeleton-bloom,var(--background)))_right/240px_100%_no-repeat] after:[animation:skeleton-sheen_2.2s_cubic-bezier(0.4,0,0.2,1)_infinite] rtl:after:scale-x-[-1] rtl:after:[animation-direction:reverse] motion-reduce:after:hidden motion-reduce:[animation:skeleton-pulse_2s_ease-in-out_infinite]",
    shape === "rect" && "rounded-[12px]",
    shape === "text" && "rounded-[6px]",
    shape === "circle" && "rounded-full"
  );
  if (children === undefined) {
    return (
      <div
        data-slot="skeleton"
        aria-hidden
        className={cn(block, "relative", className)}
        {...props}
      />
    );
  }
  return (
    <div
      data-slot="skeleton"
      className={cn("relative block", className)}
      {...props}
    >
      <div
        aria-hidden={!isLoaded || undefined}
        inert={!isLoaded}
        className={cn(
          "transition-[opacity,filter] duration-300 ease-out motion-reduce:transition-none",
          isLoaded
            ? "opacity-100 blur-[0px]"
            : "opacity-0 motion-safe:blur-[4px]"
        )}
      >
        {children}
      </div>
      <div
        aria-hidden
        className={cn(
          block,
          "pointer-events-none absolute inset-0 transition-[opacity,filter] duration-300 ease-out motion-reduce:transition-none",
          isLoaded && "opacity-0"
        )}
      />
    </div>
  );
}

function SkeletonText({
  lines = 3,
  lineClassName = "h-3.5",
  className,
}: {
  lines?: number;
  lineClassName?: string;
  className?: string;
}) {
  return (
    <div aria-hidden className={cn("flex w-full flex-col gap-2.5", className)}>
      {Array.from({ length: lines }, (_, index) => (
        <Skeleton
          key={index}
          shape="text"
          className={cn(
            lineClassName,
            index === lines - 1 && lines > 1 ? "w-3/5" : "w-full"
          )}
        />
      ))}
    </div>
  );
}

export { Skeleton, SkeletonText };
export type { SkeletonProps, SkeletonShape };