Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions components/motion/skeleton-loader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { cn } from "@/lib/utils";

export interface SkeletonLoaderProps {
className?: string;
width?: number | string;
height?: number | string;
circle?: boolean;
shimmer?: boolean;
}

export function SkeletonLoader({
className,
width,
height = 16,
circle = false,
shimmer = true,
}: SkeletonLoaderProps) {
return (
<>
<style>
{`@keyframes beui-skeleton-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}`}
</style>
<span
aria-hidden
className={cn(
"block shrink-0 bg-muted",
circle ? "rounded-full" : "rounded-xl",
shimmer &&
"bg-[length:200%_100%] bg-[linear-gradient(110deg,var(--muted)_30%,var(--card)_50%,var(--muted)_70%)]",
className,
)}
style={{
width,
height,
animation: shimmer ? "beui-skeleton-shimmer 1.5s linear infinite" : undefined,
}}
/>
</>
);
}
3 changes: 3 additions & 0 deletions components/previews/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ export const previews: Record<string, ComponentType> = {
"motion/theme-toggle": dynamic(() =>
import("./motion/theme-toggle.preview").then((m) => m.ThemeTogglePreview),
),
"motion/skeleton-loader": dynamic(() =>
import("./motion/skeleton-loader.preview").then((m) => m.SkeletonLoaderPreview),
),
};

export function getPreview(category: string, slug: string) {
Expand Down
28 changes: 28 additions & 0 deletions components/previews/motion/skeleton-loader.preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"use client";

import { SkeletonLoader } from "@/components/motion/skeleton-loader";

export function SkeletonLoaderPreview() {
return (
<div className="w-full max-w-sm rounded-3xl border border-border bg-card p-4">
<div className="flex items-center gap-3">
<SkeletonLoader circle width={44} height={44} />
<div className="flex flex-1 flex-col gap-2">
<SkeletonLoader width="48%" height={14} />
<SkeletonLoader width="32%" height={12} className="rounded-full" />
</div>
</div>

<div className="mt-4 space-y-2">
<SkeletonLoader width="100%" height={12} />
<SkeletonLoader width="92%" height={12} />
<SkeletonLoader width="74%" height={12} />
</div>

<div className="mt-5 flex gap-2">
<SkeletonLoader width={88} height={32} className="rounded-full" />
<SkeletonLoader width={72} height={32} className="rounded-full" />
</div>
</div>
);
}
7 changes: 7 additions & 0 deletions lib/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,13 @@ export const registry: CategoryEntry[] = [
description: "Theme toggle button with a full-page rectangle clip-path reveal via the View Transition API.",
file: "components/motion/theme-toggle.tsx",
},
{
slug: "skeleton-loader",
name: "Skeleton Loader",
description: "Shimmering placeholder block for loading states, with line, pill and circular avatar shapes.",
file: "components/motion/skeleton-loader.tsx",
badge: "new",
},
{
slug: "bouncy-accordion",
name: "Bouncy Accordion",
Expand Down