Skip to content
All Taste components

Loaders

Skeleton to content

A card that shows a shimmering placeholder and crossfades to the real content when it arrives.

loading · loaded · crossfade

AR
Asha Rao
Design lead · 2 min ago

The new checkout is live. Drop-off on the payment step is down by a third this week.

CheckoutShipped
import { cn } from "@/lib/utils";

const shimmer = "rounded-sm bg-gradient-to-r from-surface-2 via-surface to-surface-2 bg-[length:200%_100%] animate-shimmer motion-reduce:animate-none";

interface Post { author: string; initials: string; meta: string; body: string; tags: string[] }

export function PostCard({ post }: { post?: Post }) {
  const loading = !post;
  const layer = "col-start-1 row-start-1 grid gap-3 transition duration-500 ease-taste motion-reduce:transition-none";
  return (
    <div aria-busy={loading} className="grid w-full max-w-xs rounded-lg border border-border bg-bg p-4">
      <div aria-hidden className={cn(layer, "content-start", loading ? "opacity-100" : "opacity-0")}>
        <div className="flex items-center gap-2.5">
          <span className={cn(shimmer, "size-9 rounded-full")} />
          <div className="grid flex-1 gap-1.5">
            <span className={cn(shimmer, "h-[11px] w-1/2")} />
            <span className={cn(shimmer, "h-[9px] w-1/3")} />
          </div>
        </div>
        <span className={cn(shimmer, "h-2.5")} />
        <span className={cn(shimmer, "h-2.5 w-4/5")} />
      </div>
      {post && (
        <div className={cn(layer, "animate-rise motion-reduce:animate-none")}>
          <div className="flex items-center gap-2.5">
            <span className="grid size-9 place-items-center rounded-full bg-primary-soft text-[13px] font-medium text-primary-soft-fg">{post.initials}</span>
            <div>
              <p className="font-medium text-fg">{post.author}</p>
              <p className="text-xs text-fg-subtle">{post.meta}</p>
            </div>
          </div>
          <p className="text-[13.5px] text-fg">{post.body}</p>
          <div className="flex gap-2">
            {post.tags.map((t) => (
              <span key={t} className="inline-flex h-[22px] items-center rounded-full bg-primary-soft px-2 text-xs font-medium text-primary-soft-fg">{t}</span>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.